summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform')
-rw-r--r--chromium/third_party/blink/renderer/platform/BUILD.gn40
-rw-r--r--chromium/third_party/blink/renderer/platform/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc10
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/timing_function.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/animation/timing_function.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc466
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/distance_effect.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/vector_math.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/custom_wrappable.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_data_store.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/exception_state.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/exception_state.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/parkable_string.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc34
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/parkable_string_test.cc81
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_state.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/string_resource.cc32
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/string_resource.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/union_base.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h17
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/content_decryption_module_result.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/crypto_result.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/disk_data_allocator.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/disk_data_allocator.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc13
-rw-r--r--chromium/third_party/blink/renderer/platform/encrypted_media_request.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/mediastream/DEPS9
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/platform.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc44
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_http_body.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_string.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/exported/web_url_response.cc21
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md33
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/README.md9
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache.cc5
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc55
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_description.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc5
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc5
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/length.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/geometry/length_functions.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/DEPS2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc15
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc127
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h26
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc271
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h9
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc504
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc263
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h8
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc113
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc49
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc156
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h30
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc15
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier_test.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc148
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc74
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.cc73
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h31
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.cc16
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc332
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h112
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc208
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/dark_mode_settings.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/generated_image.cc5
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc104
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h11
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc954
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h12
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc82
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h13
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc273
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h8
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc93
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_context.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h16
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/graphics_types.h16
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc112
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h90
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image.h32
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/image_observer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h13
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc13
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc35
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc10
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc197
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h12
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h9
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/BUILD.gn49
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md26
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc131
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/card_table_test.cc181
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/collection_support/heap_hash_table_backing.h80
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack_test.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc487
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/garbage_collected.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc45
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap.cc158
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc502
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_page.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_page.h165
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.h11
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc662
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test.cc5417
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc265
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc1889
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h64
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_verifier_test.cc98
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/marking_visitor.h126
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/minor_gc_test.cc295
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/name_trait_test.cc68
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc172
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent_node.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/persistent_test.cc57
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/thread_state.cc85
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/thread_state.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc75
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/trace_traits.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/unified_heap_controller.cc14
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/visitor.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc247
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/worklist.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/worklist_test.cc352
-rw-r--r--chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc91
-rw-r--r--chromium/third_party/blink/renderer/platform/heap_observer_list.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc558
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h33
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc152
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc222
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h29
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc100
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn2
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_parser.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/json/json_values.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/BUILD.gn3
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/OWNERS5
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.cc28
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/console_logger.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.cc37
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc134
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body.typemap10
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc179
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc149
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h9
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc109
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc12
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm1
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/DEPS6
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h16
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_test.cc (renamed from chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc)78
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc10
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/DEPS1
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni3
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/features.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/features.h15
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote_test.cc71
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h9
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h35
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set_test.cc109
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote_test.cc96
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set_test.cc50
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/network/BUILD.gn4
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data.typemap10
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap11
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc167
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc29
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h40
-rw-r--r--chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc79
-rw-r--r--chromium/third_party/blink/renderer/platform/network/http_parsers.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h4
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc80
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h10
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_test.cc38
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc70
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h22
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc21
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc19
-rw-r--r--chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5247
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn4
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/DEPS1
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/features.cc125
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/features.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h11
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc42
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h24
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc564
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h28
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc331
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h97
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc527
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc214
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h45
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc1110
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc25
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc169
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h43
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc357
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager_unittest.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc308
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc110
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_priority.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h5
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc11
-rw-r--r--chromium/third_party/blink/renderer/platform/supplementable.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/data/red-full-ranged-8bpc.avifbin0 -> 355 bytes
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/image_decode_to_nia.cc217
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h14
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc3
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc4
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc22
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h9
-rw-r--r--chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/text/date_components.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/text/layout_locale.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/text/text_direction.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_direction_mode.cc15
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_direction_mode.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_mode.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/text/writing_mode.h8
-rw-r--r--chromium/third_party/blink/renderer/platform/timer_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc30
-rw-r--r--chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc7
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl.cc27
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc53
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc59
-rw-r--r--chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc46
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/DEPS5
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc20
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h7
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h12
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/frame_widget.h61
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/DEPS3
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc6
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.cc60
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.h31
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc227
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc905
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/input_scroll_elasticity_controller_unittest.cc2
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.cc151
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller_unittest.cc158
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/prediction/input_filter_unittest_helpers.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc13
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc10
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc702
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h144
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/widget_base.cc375
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/widget_base.h92
-rw-r--r--chromium/third_party/blink/renderer/platform/widget/widget_base_client.h59
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/BUILD.gn1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/DEPS2
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md6
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc18
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h25
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/allocator/partitions_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/decimal.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/deque.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_functions.h54
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/hash_traits.h6
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/stack_util.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/ascii_ctype.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc171
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/case_map.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/character_visitor.h42
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/math_transform.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_hasher.h3
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc77
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc26
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/string_view.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/threading.h2
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/tree_node.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/type_traits.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/vector.h76
-rw-r--r--chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h6
560 files changed, 15993 insertions, 17131 deletions
diff --git a/chromium/third_party/blink/renderer/platform/BUILD.gn b/chromium/third_party/blink/renderer/platform/BUILD.gn
index b34305f0ee0..04e12c3d614 100644
--- a/chromium/third_party/blink/renderer/platform/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/BUILD.gn
@@ -865,10 +865,6 @@ jumbo_component("platform") {
"graphics/dark_mode_color_filter.h",
"graphics/dark_mode_filter.cc",
"graphics/dark_mode_filter.h",
- "graphics/dark_mode_generic_classifier.cc",
- "graphics/dark_mode_generic_classifier.h",
- "graphics/dark_mode_icon_classifier.cc",
- "graphics/dark_mode_icon_classifier.h",
"graphics/dark_mode_image_classifier.cc",
"graphics/dark_mode_image_classifier.h",
"graphics/dark_mode_settings.h",
@@ -981,6 +977,8 @@ jumbo_component("platform") {
"graphics/graphics_types.cc",
"graphics/graphics_types.h",
"graphics/graphics_types_3d.h",
+ "graphics/identifiability_paint_op_digest.cc",
+ "graphics/identifiability_paint_op_digest.h",
"graphics/image.cc",
"graphics/image.h",
"graphics/image_animation_policy.h",
@@ -1237,6 +1235,8 @@ jumbo_component("platform") {
"mojo/big_string_mojom_traits.h",
"mojo/bluetooth_mojom_traits.cc",
"mojo/bluetooth_mojom_traits.h",
+ "mojo/features.cc",
+ "mojo/features.h",
"mojo/fetch_api_request_headers_mojom_traits.h",
"mojo/heap_mojo_associated_receiver.h",
"mojo/heap_mojo_associated_receiver_set.h",
@@ -1398,6 +1398,9 @@ jumbo_component("platform") {
"text/web_entities.cc",
"text/web_entities.h",
"text/win/hyphenation_win.cc",
+ "text/writing_direction_mode.cc",
+ "text/writing_direction_mode.h",
+ "text/writing_mode.cc",
"text/writing_mode.h",
"text/writing_mode_utils.h",
"timer.cc",
@@ -1500,6 +1503,8 @@ jumbo_component("platform") {
"widget/input/prediction/predictor_factory.cc",
"widget/input/scroll_predictor.cc",
"widget/input/scroll_predictor.h",
+ "widget/input/widget_base_input_handler.cc",
+ "widget/input/widget_base_input_handler.h",
"widget/widget_base.cc",
"widget/widget_base.h",
"widget/widget_base_client.h",
@@ -1581,7 +1586,7 @@ jumbo_component("platform") {
"//skia",
"//skia:skcms",
"//third_party:freetype_harfbuzz",
- "//third_party/abseil-cpp/absl/types:optional",
+ "//third_party/abseil-cpp:absl",
"//third_party/blink/public:image_resources",
"//third_party/blink/public/common",
"//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings_blink",
@@ -1595,7 +1600,7 @@ jumbo_component("platform") {
"//third_party/one_euro_filter",
"//third_party/webrtc_overrides:webrtc_component",
"//third_party/zlib/google:compression_utils",
- "//ui/base/cursor",
+ "//ui/base/cursor:cursor_base",
"//ui/base/cursor/mojom:cursor_type_blink",
"//ui/events/ipc",
"//ui/gfx/geometry",
@@ -1826,7 +1831,6 @@ jumbo_source_set("blink_platform_unittests_sources") {
"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",
"exported/video_capture/web_video_capture_impl_manager_test.cc",
"exported/web_icon_sizes_parser_test.cc",
@@ -1949,6 +1953,7 @@ jumbo_source_set("blink_platform_unittests_sources") {
"mac/graphics_context_canvas_test.mm",
"media/webaudiosourceprovider_impl_test.cc",
"mediastream/media_stream_audio_processor_options_test.cc",
+ "mediastream/media_stream_audio_test.cc",
"mediastream/webrtc_uma_histograms_test.cc",
"mhtml/mhtml_parser_test.cc",
"mojo/big_string_mojom_traits_test.cc",
@@ -2010,6 +2015,7 @@ jumbo_source_set("blink_platform_unittests_sources") {
"widget/compositing/test/stub_layer_tree_view_delegate.h",
"widget/input/input_handler_proxy_unittest.cc",
"widget/input/input_scroll_elasticity_controller_unittest.cc",
+ "widget/input/overscroll_bounce_controller_unittest.cc",
"widget/input/prediction/empty_filter_unittests.cc",
"widget/input/prediction/filter_factory_unittests.cc",
"widget/input/prediction/input_filter_unittest_helpers.cc",
@@ -2114,6 +2120,26 @@ executable("image_decode_bench") {
defines = [ "INSIDE_BLINK" ]
}
+executable("image_decode_to_nia") {
+ visibility = [] # Allow re-assignment of list.
+ visibility = [ "*" ]
+
+ sources = [ "testing/image_decode_to_nia.cc" ]
+
+ deps = [
+ ":platform",
+ "//base",
+ "//third_party/blink/renderer/platform/wtf",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer/platform/wtf:wtf_config",
+ "//third_party/blink/renderer:config",
+ ]
+
+ defines = [ "INSIDE_BLINK" ]
+}
+
test("blink_platform_perftests") {
sources = [
"testing/blink_perf_test_suite.cc",
diff --git a/chromium/third_party/blink/renderer/platform/DEPS b/chromium/third_party/blink/renderer/platform/DEPS
index 99201dec859..ad05409fe08 100644
--- a/chromium/third_party/blink/renderer/platform/DEPS
+++ b/chromium/third_party/blink/renderer/platform/DEPS
@@ -1,5 +1,5 @@
include_rules = [
- # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # To only allow a subset of base/ in Blink, we explicitly list all
# directories and files instead of writing 'base/'.
"+base/allocator/partition_allocator",
"+base/atomic_ref_count.h",
@@ -42,6 +42,7 @@ include_rules = [
"+base/test",
"+base/test/fuzzed_data_provider.h",
"+base/threading/thread_task_runner_handle.h",
+ "+base/threading/thread_restrictions.h",
"+base/time",
"+base/timer",
"+base/trace_event",
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 f02f038486a..206eb4f5b8c 100644
--- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -80,14 +80,6 @@ void CompositorAnimation::AbortKeyframeModel(int keyframe_model_id) {
animation_->AbortKeyframeModel(keyframe_model_id);
}
-void CompositorAnimation::UpdateScrollTimeline(
- base::Optional<cc::ElementId> element_id,
- base::Optional<double> start_scroll_offset,
- base::Optional<double> end_scroll_offset) {
- animation_->UpdateScrollTimeline(element_id, start_scroll_offset,
- end_scroll_offset);
-}
-
void CompositorAnimation::UpdatePlaybackRate(double playback_rate) {
cc::ToWorkletAnimation(animation_.get())->UpdatePlaybackRate(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 348a4480437..527e422145b 100644
--- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -57,9 +57,6 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate {
void PauseKeyframeModel(int keyframe_model_id, base::TimeDelta time_offset);
void AbortKeyframeModel(int keyframe_model_id);
- void UpdateScrollTimeline(base::Optional<cc::ElementId>,
- base::Optional<double> start_scroll_offset,
- base::Optional<double> end_scroll_offset);
void UpdatePlaybackRate(double playback_rate);
private:
diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
index 06509423ca7..5a577c0b285 100644
--- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
+++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_delegate.h
@@ -16,8 +16,6 @@ class PLATFORM_EXPORT CompositorAnimationDelegate {
public:
virtual ~CompositorAnimationDelegate() = default;
- // TODO(yigu): The Notify* methods should be called from cc once per
- // animation.
virtual void NotifyAnimationStarted(double monotonic_time, int group) = 0;
virtual void NotifyAnimationFinished(double monotonic_time, int group) = 0;
virtual void NotifyAnimationAborted(double monotonic_time, int group) = 0;
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 3da0644ab74..0edd2033de0 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
@@ -6,6 +6,7 @@
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
+#include "cc/animation/scroll_timeline.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
@@ -32,6 +33,15 @@ cc::AnimationTimeline* CompositorAnimationTimeline::GetAnimationTimeline()
return animation_timeline_.get();
}
+void CompositorAnimationTimeline::UpdateCompositorTimeline(
+ base::Optional<CompositorElementId> pending_id,
+ base::Optional<double> start_scroll_offset,
+ base::Optional<double> end_scroll_offset) {
+ ToScrollTimeline(animation_timeline_.get())
+ ->UpdateScrollerIdAndScrollOffsets(pending_id, start_scroll_offset,
+ end_scroll_offset);
+}
+
void CompositorAnimationTimeline::AnimationAttached(
const blink::CompositorAnimationClient& client) {
if (client.GetCompositorAnimation()) {
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 8307cd8f257..f2181c616bb 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
@@ -10,7 +10,9 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
#include "cc/animation/animation_timeline.h"
+#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -28,6 +30,9 @@ class PLATFORM_EXPORT CompositorAnimationTimeline {
~CompositorAnimationTimeline();
cc::AnimationTimeline* GetAnimationTimeline() const;
+ void UpdateCompositorTimeline(base::Optional<CompositorElementId> pending_id,
+ base::Optional<double> start_scroll_offset,
+ base::Optional<double> end_scroll_offset);
void AnimationAttached(const CompositorAnimationClient&);
void AnimationDestroyed(const CompositorAnimationClient&);
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 5d8e9c8806a..9922baf0d94 100644
--- a/chromium/third_party/blink/renderer/platform/animation/timing_function.cc
+++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
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 8cf6ef43f37..bbefc14c106 100644
--- a/chromium/third_party/blink/renderer/platform/animation/timing_function.h
+++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.h
@@ -26,6 +26,7 @@
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_ANIMATION_TIMING_FUNCTION_H_
#include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
#include "cc/animation/timing_function.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
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 9e44ea873ab..f761468e0ea 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
@@ -26,9 +26,17 @@
#include "third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h"
#include <cmath>
+
+#include "base/notreached.h"
+#include "build/build_config.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "third_party/blink/renderer/platform/audio/vector_math.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#if defined(ARCH_CPU_X86_FAMILY)
+#include <xmmintrin.h>
+#endif
+
namespace blink {
// Delay nodes have a max allowed delay time of this many seconds.
@@ -38,13 +46,15 @@ AudioDelayDSPKernel::AudioDelayDSPKernel(AudioDSPKernelProcessor* processor,
size_t processing_size_in_frames)
: AudioDSPKernel(processor),
write_index_(0),
- delay_times_(processing_size_in_frames) {}
+ delay_times_(processing_size_in_frames),
+ temp_buffer_(processing_size_in_frames) {}
AudioDelayDSPKernel::AudioDelayDSPKernel(double max_delay_time,
float sample_rate)
: AudioDSPKernel(sample_rate),
max_delay_time_(max_delay_time),
- write_index_(0) {
+ write_index_(0),
+ temp_buffer_(audio_utilities::kRenderQuantumFrames) {
DCHECK_GT(max_delay_time_, 0.0);
DCHECK_LE(max_delay_time_, kMaxDelayTimeSeconds);
DCHECK(std::isfinite(max_delay_time_));
@@ -59,10 +69,12 @@ AudioDelayDSPKernel::AudioDelayDSPKernel(double max_delay_time,
size_t AudioDelayDSPKernel::BufferLengthForDelay(double max_delay_time,
double sample_rate) const {
// 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,
- audio_utilities::kRoundUp);
+ // |maxDelayTime|. Add an additional render quantum frame size so we can
+ // vectorize the delay processing. The extra space is needed so that writes
+ // to the buffer won't overlap reads from the buffer.
+ return audio_utilities::kRenderQuantumFrames +
+ audio_utilities::TimeToSampleFrame(max_delay_time, sample_rate,
+ audio_utilities::kRoundUp);
}
bool AudioDelayDSPKernel::HasSampleAccurateValues() {
@@ -81,9 +93,247 @@ double AudioDelayDSPKernel::DelayTime(float sample_rate) {
return desired_delay_frames_ / sample_rate;
}
-void AudioDelayDSPKernel::Process(const float* source,
- float* destination,
- uint32_t frames_to_process) {
+static void CopyToCircularBuffer(float* buffer,
+ int write_index,
+ int buffer_length,
+ const float* source,
+ uint32_t frames_to_process) {
+ // The algorithm below depends on this being true because we don't expect to
+ // have to fill the entire buffer more than once.
+ DCHECK_GE(static_cast<uint32_t>(buffer_length), frames_to_process);
+
+ // Copy |frames_to_process| values from |source| to the circular buffer that
+ // starts at |buffer| of length |buffer_length|. The copy starts at index
+ // |write_index| into the buffer.
+ float* write_pointer = &buffer[write_index];
+ int remainder = buffer_length - write_index;
+
+ // Copy the sames over, carefully handling the case where we need to wrap
+ // around to the beginning of the buffer.
+ memcpy(write_pointer, source,
+ sizeof(*write_pointer) *
+ std::min(static_cast<int>(frames_to_process), remainder));
+ memcpy(buffer, source + remainder,
+ sizeof(*write_pointer) *
+ std::max(0, static_cast<int>(frames_to_process) - remainder));
+}
+
+#if defined(ARCH_CPU_X86_FAMILY)
+static ALWAYS_INLINE __m128i WrapIndexVector(__m128i v_write_index,
+ __m128i v_buffer_length) {
+ // Wrap the write_index if any index is past the end of the buffer.
+
+ // cmp = 0xffffffff if buffer length < write index and 0 otherwise. (That is,
+ // 0xffffffff if index >= buffer length.)
+ __m128i cmp = _mm_cmplt_epi32(v_buffer_length, v_write_index);
+
+ // Bitwise and cmp with buffer length to get buffer length or 0 depending on
+ // whether buffer length < index or not. Subtract this from the index to wrap
+ // the index appropriately.
+ return _mm_sub_epi32(v_write_index, _mm_and_si128(cmp, v_buffer_length));
+}
+
+static ALWAYS_INLINE __m128 WrapPositionVector(__m128 v_position,
+ __m128 v_buffer_length) {
+ // Wrap the read position if it exceed the buffer length.
+
+ // If buffer length < read_position, set cmp to 0xffffffff. Otherwise zero.
+ __m128i cmp = _mm_cmplt_ps(v_buffer_length, v_position);
+
+ // Bitwise and buffer_length with cmp to get buffer_length or 0 depending on
+ // whether read_position >= buffer length or not. Then subtract from the
+ // psoition to wrap it around if needed.
+ return _mm_sub_ps(v_position, _mm_and_ps(v_buffer_length, cmp));
+}
+
+std::tuple<unsigned, int> AudioDelayDSPKernel::ProcessARateVector(
+ float* destination,
+ uint32_t frames_to_process) const {
+ const int buffer_length = buffer_.size();
+ const float* buffer = buffer_.Data();
+
+ const float sample_rate = this->SampleRate();
+ const float* delay_times = delay_times_.Data();
+
+ int w_index = write_index_;
+
+ const __m128 v_sample_rate = _mm_set1_ps(sample_rate);
+
+ // The buffer length as a float and as an int so we don't need to constant
+ // convert from one to the other.
+ const __m128 v_buffer_length_float = _mm_set1_ps(buffer_length);
+ const __m128i v_buffer_length_int = _mm_set1_epi32(buffer_length);
+
+ // How much to increment the write index each time through the loop.
+ const __m128i v_incr = _mm_set1_epi32(4);
+
+ // Temp arrays for storing the samples needed for interpolation
+ float sample1[4] __attribute((aligned(16)));
+ float sample2[4] __attribute((aligned(16)));
+
+ // Initialize the write index vector, and wrap the values if needed.
+ __m128i v_write_index =
+ _mm_set_epi32(w_index + 3, w_index + 2, w_index + 1, w_index + 0);
+ v_write_index = WrapIndexVector(v_write_index, v_buffer_length_int);
+
+ const int number_of_loops = frames_to_process / 4;
+ int k = 0;
+
+ for (int n = 0; n < number_of_loops; ++n, k += 4) {
+ const __m128 v_delay_time = _mm_loadu_ps(delay_times + k);
+ const __m128 v_desired_delay_frames =
+ _mm_mul_ps(v_delay_time, v_sample_rate);
+
+ // read_position = write_index + buffer_length - desired_delay_frames. Wrap
+ // the position if needed.
+ __m128 v_read_position =
+ _mm_add_ps(_mm_cvtepi32_ps(v_write_index),
+ _mm_sub_ps(v_buffer_length_float, v_desired_delay_frames));
+ v_read_position =
+ WrapPositionVector(v_read_position, v_buffer_length_float);
+
+ // Get indices into the buffer for the samples we need for interpolation.
+ const __m128i v_read_index1 = _mm_cvttps_epi32(v_read_position);
+ const __m128i v_read_index2 = WrapIndexVector(
+ _mm_add_epi32(v_read_index1, _mm_set1_epi32(1)), v_buffer_length_int);
+
+ const __m128 interpolation_factor =
+ _mm_sub_ps(v_read_position, _mm_cvtepi32_ps(v_read_index1));
+
+ const uint32_t* read_index1 =
+ reinterpret_cast<const uint32_t*>(&v_read_index1);
+ const uint32_t* read_index2 =
+ reinterpret_cast<const uint32_t*>(&v_read_index2);
+
+ for (int m = 0; m < 4; ++m) {
+ sample1[m] = buffer[read_index1[m]];
+ sample2[m] = buffer[read_index2[m]];
+ }
+
+ const __m128 v_sample1 = _mm_load_ps(sample1);
+ const __m128 v_sample2 = _mm_load_ps(sample2);
+
+ v_write_index = _mm_add_epi32(v_write_index, v_incr);
+ v_write_index = WrapIndexVector(v_write_index, v_buffer_length_int);
+
+ const __m128 sample = _mm_add_ps(
+ v_sample1,
+ _mm_mul_ps(interpolation_factor, _mm_sub_ps(v_sample2, v_sample1)));
+ _mm_store_ps(destination + k, sample);
+ }
+
+ // Update |w_index|_ based on how many frames we processed here, wrapping
+ // around if needed.
+ w_index = write_index_ + k;
+ if (w_index >= buffer_length)
+ w_index -= buffer_length;
+
+ return std::make_tuple(k, w_index);
+}
+
+static ALWAYS_INLINE void HandleNaN(float* delay_times,
+ uint32_t frames_to_process,
+ float max_time) {
+ unsigned k = 0;
+ const unsigned number_of_loops = frames_to_process / 4;
+
+ __m128 v_max_time = _mm_set1_ps(max_time);
+
+ // This is approximately 4 times faster than the scalar version.
+ for (unsigned loop = 0; loop < number_of_loops; ++loop, k += 4) {
+ __m128 x = _mm_loadu_ps(delay_times + k);
+ // 0xffffffff if x is NaN. Otherwise 0
+ __m128 cmp = _mm_cmpunord_ps(x, x);
+
+ // Use cmp as a mask to set a component of x to 0 if is NaN. Otherwise,
+ // preserve x.
+ x = _mm_andnot_ps(cmp, x);
+
+ // Now set cmp to be max_time if the value is 0xffffffff or 0.
+ cmp = _mm_and_ps(cmp, v_max_time);
+
+ // Merge i (bitwise or) x and cmp. This makes x = max_time if x was NaN and
+ // preserves x if not.
+ x = _mm_or_ps(x, cmp);
+ _mm_storeu_ps(delay_times + k, x);
+ }
+
+ // Handle any frames not done in the loop above.
+ for (; k < frames_to_process; ++k) {
+ if (std::isnan(delay_times[k]))
+ delay_times[k] = max_time;
+ }
+}
+#else
+std::tuple<unsigned, int> AudioDelayDSPKernel::ProcessARateVector(
+ float* destination,
+ uint32_t frames_to_process) const {
+ // We don't have a vectorized version, so just do nothing and return the 0 to
+ // indicate no frames processed and return the current write_index_.
+ return std::make_tuple(0, write_index_);
+}
+
+static ALWAYS_INLINE void HandleNaN(float* delay_times,
+ uint32_t frames_to_process,
+ float max_time) {
+ for (unsigned k = 0; k < frames_to_process; ++k) {
+ if (std::isnan(delay_times[k]))
+ delay_times[k] = max_time;
+ }
+}
+#endif
+
+int AudioDelayDSPKernel::ProcessARateScalar(unsigned start,
+ int w_index,
+ float* destination,
+ uint32_t frames_to_process) const {
+ const int buffer_length = buffer_.size();
+ const float* buffer = buffer_.Data();
+
+ DCHECK(buffer_length);
+ DCHECK(destination);
+ DCHECK_GE(write_index_, 0);
+ DCHECK_LT(write_index_, buffer_length);
+
+ float sample_rate = this->SampleRate();
+ const float* delay_times = delay_times_.Data();
+
+ for (unsigned i = start; i < frames_to_process; ++i) {
+ double delay_time = delay_times[i];
+ double desired_delay_frames = delay_time * sample_rate;
+
+ double read_position = w_index + buffer_length - desired_delay_frames;
+ if (read_position >= buffer_length)
+ read_position -= buffer_length;
+
+ // Linearly interpolate in-between delay times.
+ int read_index1 = static_cast<int>(read_position);
+ DCHECK_GE(read_index1, 0);
+ DCHECK_LT(read_index1, buffer_length);
+ int read_index2 = read_index1 + 1;
+ if (read_index2 >= buffer_length)
+ read_index2 -= buffer_length;
+ DCHECK_GE(read_index2, 0);
+ DCHECK_LT(read_index2, buffer_length);
+
+ float interpolation_factor = read_position - read_index1;
+
+ float sample1 = buffer[read_index1];
+ float sample2 = buffer[read_index2];
+
+ ++w_index;
+ if (w_index >= buffer_length)
+ w_index -= buffer_length;
+
+ destination[i] = sample1 + interpolation_factor * (sample2 - sample1);
+ }
+
+ return w_index;
+}
+
+void AudioDelayDSPKernel::ProcessARate(const float* source,
+ float* destination,
+ uint32_t frames_to_process) {
int buffer_length = buffer_.size();
float* buffer = buffer_.Data();
@@ -93,105 +343,135 @@ void AudioDelayDSPKernel::Process(const float* source,
DCHECK_GE(write_index_, 0);
DCHECK_LT(write_index_, buffer_length);
- float sample_rate = this->SampleRate();
+ float* delay_times = delay_times_.Data();
+ CalculateSampleAccurateValues(delay_times, frames_to_process);
+
+ // Any NaN's get converted to max time
+ // TODO(crbug.com/1013345): Don't need this if that bug is fixed
double max_time = MaxDelayTime();
+ HandleNaN(delay_times, frames_to_process, max_time);
- if (HasSampleAccurateValues() && IsAudioRate()) {
- float* delay_times = delay_times_.Data();
- CalculateSampleAccurateValues(delay_times, frames_to_process);
+ CopyToCircularBuffer(buffer, write_index_, buffer_length, source,
+ frames_to_process);
- int w_index = write_index_;
+ unsigned frames_processed;
+ std::tie(frames_processed, write_index_) =
+ ProcessARateVector(destination, frames_to_process);
- for (unsigned i = 0; i < frames_to_process; ++i) {
- double delay_time = delay_times[i];
- // TODO(crbug.com/1013345): Don't need this if that bug is fixed
- if (std::isnan(delay_time))
- delay_time = max_time;
+ if (frames_processed < frames_to_process) {
+ write_index_ = ProcessARateScalar(frames_processed, write_index_,
+ destination, frames_to_process);
+ }
+}
- double desired_delay_frames = delay_time * sample_rate;
+void AudioDelayDSPKernel::ProcessKRate(const float* source,
+ float* destination,
+ uint32_t frames_to_process) {
+ int buffer_length = buffer_.size();
+ float* buffer = buffer_.Data();
- double read_position = w_index + buffer_length - desired_delay_frames;
- if (read_position >= buffer_length)
- read_position -= buffer_length;
+ DCHECK(buffer_length);
+ DCHECK(source);
+ DCHECK(destination);
+ DCHECK_GE(write_index_, 0);
+ DCHECK_LT(write_index_, buffer_length);
- // Linearly interpolate in-between delay times.
- int read_index1 = static_cast<int>(read_position);
- DCHECK_GE(read_index1, 0);
- DCHECK_LT(read_index1, buffer_length);
- int read_index2 = read_index1 + 1;
- if (read_index2 >= buffer_length)
- read_index2 -= buffer_length;
- DCHECK_GE(read_index2, 0);
- DCHECK_LT(read_index2, buffer_length);
+ float sample_rate = this->SampleRate();
+ double max_time = MaxDelayTime();
- buffer[w_index] = *source++;
+ // This is basically the same as above, but optimized for the case where the
+ // delay time is constant for the current render.
+ //
+ // TODO(crbug.com/1012198): There are still some further optimizations that
+ // could be done. |interpolation_factor| could be a float to eliminate
+ // several conversions between floats and doubles. It might be possible to
+ // get rid of the wrapping if the buffer were longer. This may also allow
+ // |write_index_| to be different from |read_index1| or |read_index2| which
+ // simplifies the loop a bit.
+
+ double delay_time = this->DelayTime(sample_rate);
+ // 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;
+ int w_index = write_index_;
+ double read_position = w_index + buffer_length - desired_delay_frames;
+
+ if (read_position >= buffer_length)
+ read_position -= buffer_length;
+
+ // Linearly interpolate in-between delay times. |read_index1| and
+ // |read_index2| are the indices of the frames to be used for
+ // interpolation.
+ int read_index1 = static_cast<int>(read_position);
+ float interpolation_factor = read_position - read_index1;
+ float* buffer_end = &buffer[buffer_length];
+ DCHECK_GE(static_cast<unsigned>(buffer_length), frames_to_process);
+
+ // sample1 and sample2 hold the current and next samples in the buffer.
+ // These are used for interoplating the delay value. To reduce memory
+ // usage and an extra memcpy, sample1 can be the same as destination.
+ float* sample1 = destination;
+
+ // Copy data from the source into the buffer, starting at the write index.
+ // The buffer is circular, so carefully handle the wrapping of the write
+ // pointer.
+ CopyToCircularBuffer(buffer, write_index_, buffer_length, source,
+ frames_to_process);
+ w_index += frames_to_process;
+ if (w_index >= buffer_length)
+ w_index -= buffer_length;
+ write_index_ = w_index;
+
+ // Now copy out the samples from the buffer, starting at the read pointer,
+ // carefully handling wrapping of the read pointer.
+ float* read_pointer = &buffer[read_index1];
+
+ int remainder = buffer_end - read_pointer;
+ memcpy(sample1, read_pointer,
+ sizeof(*sample1) *
+ std::min(static_cast<int>(frames_to_process), remainder));
+ memcpy(sample1 + remainder, buffer,
+ sizeof(*sample1) *
+ std::max(0, static_cast<int>(frames_to_process) - remainder));
+
+ // If interpolation_factor = 0, we don't need to do any interpolation and
+ // sample1 contains the desried values. We can skip the following code.
+ if (interpolation_factor != 0) {
+ DCHECK_LE(frames_to_process, temp_buffer_.size());
- float interpolation_factor = read_position - read_index1;
+ int read_index2 = (read_index1 + 1) % buffer_length;
+ float* sample2 = temp_buffer_.Data();
- float sample1 = buffer[read_index1];
- float sample2 = buffer[read_index2];
+ read_pointer = &buffer[read_index2];
+ remainder = buffer_end - read_pointer;
+ memcpy(sample2, read_pointer,
+ sizeof(*sample1) *
+ std::min(static_cast<int>(frames_to_process), remainder));
+ memcpy(sample2 + remainder, buffer,
+ sizeof(*sample1) *
+ std::max(0, static_cast<int>(frames_to_process) - remainder));
- ++w_index;
- if (w_index >= buffer_length)
- w_index -= buffer_length;
+ // Interpolate samples, where f = interpolation_factor
+ // dest[k] = sample1[k] + f*(sample2[k] - sample1[k]);
- *destination++ = sample1 + interpolation_factor * (sample2 - sample1);
- }
+ // sample2[k] = sample2[k] - sample1[k]
+ vector_math::Vsub(sample2, 1, sample1, 1, sample2, 1, frames_to_process);
- write_index_ = w_index;
- } else {
- // This is basically the same as above, but optimized for the case where the
- // delay time is constant for the current render.
+ // dest[k] = dest[k] + f*sample2[k]
+ // = sample1[k] + f*(sample2[k] - sample1[k]);
//
- // TODO(crbug.com/1012198): There are still some further optimizations that
- // could be done. interp_factor could be a float to eliminate several
- // conversions between floats and doubles. It might be possible to get rid
- // of the wrapping if the buffer were longer. This may aslo allow
- // |write_index_| to be different from |read_index1| or |read_index2| which
- // simplifies the loop a bit.
-
- double delay_time = this->DelayTime(sample_rate);
- // 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;
- int w_index = write_index_;
- double read_position = w_index + buffer_length - desired_delay_frames;
- if (read_position >= buffer_length)
- read_position -= buffer_length;
-
- // Linearly interpolate in-between delay times. |read_index1| and
- // |read_index2| are the indices of the frames to be used for
- // interpolation.
- int read_index1 = static_cast<int>(read_position);
- int read_index2 = (read_index1 + 1) % buffer_length;
- float interp_factor = read_position - read_index1;
-
- 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.
- *w++ = *source++;
- float sample1 = *r1++;
- float sample2 = *r2++;
-
- // Update the indices and wrap them to the beginning of the buffer if
- // needed.
- if (w >= buffer_end)
- w = buffer;
- if (r1 >= buffer_end)
- r1 = buffer;
- if (r2 >= buffer_end)
- r2 = buffer;
-
- // Linearly interpolate between samples.
- *destination++ = sample1 + interp_factor * (sample2 - sample1);
- }
+ vector_math::Vsma(sample2, 1, interpolation_factor, destination, 1,
+ frames_to_process);
+ }
+}
- write_index_ = w - buffer;
+void AudioDelayDSPKernel::Process(const float* source,
+ float* destination,
+ uint32_t frames_to_process) {
+ if (HasSampleAccurateValues() && IsAudioRate()) {
+ ProcessARate(source, destination, frames_to_process);
+ } else {
+ ProcessKRate(source, destination, frames_to_process);
}
}
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
index 7936d90bdf2..ef72ff89619 100644
--- a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.h
@@ -35,9 +35,34 @@ class PLATFORM_EXPORT AudioDelayDSPKernel : public AudioDSPKernel {
public:
AudioDelayDSPKernel(double max_delay_time, float sample_rate);
+ // Process the delay. Basically dispatches to either ProcessKRate or
+ // ProcessARate.
void Process(const float* source,
float* destination,
uint32_t frames_to_process) override;
+
+ // Handles k-rate processing
+ void ProcessKRate(const float* source,
+ float* destination,
+ uint32_t frames_to_process);
+
+ // Handles a-rate processing
+ void ProcessARate(const float* source,
+ float* destination,
+ uint32_t frames_to_process);
+ // Main processing loop for ProcessARate using scalar operations. Returns the
+ // new write_index.
+ int ProcessARateScalar(unsigned start,
+ int w_index,
+ float* destination,
+ uint32_t frames_to_process) const;
+
+ // Vector version of ProcessARateScalar. Returns the number of samples
+ // process by this function and the updated wirte_index_.
+ std::tuple<unsigned, int> ProcessARateVector(
+ float* destination,
+ uint32_t frames_to_process) const;
+
void Reset() override;
float MaxDelayTime() const { return max_delay_time_; }
@@ -71,6 +96,10 @@ class PLATFORM_EXPORT AudioDelayDSPKernel : public AudioDSPKernel {
AudioFloatArray delay_times_;
+ // Temporary buffer used to hold the second sample for interpolation if
+ // needed.
+ AudioFloatArray temp_buffer_;
+
size_t BufferLengthForDelay(double delay_time, double sample_rate) const;
};
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 4372a94a853..b3605e6f7cf 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
protected:
virtual ~AudioSourceProviderClient() = default;
diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc
index cfe0b7dd719..7fc4153631d 100644
--- a/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc
+++ b/chromium/third_party/blink/renderer/platform/audio/audio_utilities.cc
@@ -24,6 +24,7 @@
*/
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
diff --git a/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc b/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc
index 7f10ec50c0d..83f3c130905 100644
--- a/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc
+++ b/chromium/third_party/blink/renderer/platform/audio/distance_effect.cc
@@ -30,6 +30,7 @@
#include <math.h>
#include <algorithm>
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc
index ac01b96c9bc..8df6c8230cf 100644
--- a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor.cc
@@ -26,9 +26,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "third_party/blink/renderer/platform/audio/dynamics_compressor.h"
+
+#include "base/logging.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/audio/audio_utilities.h"
-#include "third_party/blink/renderer/platform/audio/dynamics_compressor.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
namespace blink {
@@ -140,7 +143,7 @@ void DynamicsCompressor::Process(const AudioBus* source_bus,
float release_time = ParameterValue(kParamRelease);
float pre_delay_time = ParameterValue(kParamPreDelay);
- // This is effectively a master volume on the compressed signal
+ // This is effectively a make-up gain on the compressed signal
// (pre-blending).
float db_post_gain = ParameterValue(kParamPostGain);
diff --git a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc
index daec0eceb54..83b1e63a6c8 100644
--- a/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc
+++ b/chromium/third_party/blink/renderer/platform/audio/dynamics_compressor_kernel.cc
@@ -232,7 +232,7 @@ void DynamicsCompressorKernel::Process(
// Empirical/perceptual tuning.
full_range_makeup_gain = powf(full_range_makeup_gain, 0.6f);
- float master_linear_gain =
+ float linear_post_gain =
audio_utilities::DecibelsToLinear(db_post_gain) * full_range_makeup_gain;
// Attack parameters.
@@ -453,9 +453,9 @@ void DynamicsCompressorKernel::Process(
float post_warp_compressor_gain =
sinf(kPiOverTwoFloat * compressor_gain);
- // Calculate total gain using master gain and effect blend.
+ // Calculate total gain using the linear post-gain and effect blend.
float total_gain =
- dry_mix + wet_mix * master_linear_gain * post_warp_compressor_gain;
+ dry_mix + wet_mix * linear_post_gain * post_warp_compressor_gain;
// Calculate metering.
float db_real_gain = 20 * std::log10(post_warp_compressor_gain);
diff --git a/chromium/third_party/blink/renderer/platform/audio/vector_math.h b/chromium/third_party/blink/renderer/platform/audio/vector_math.h
index b6bfe17079f..0ded2755e0a 100644
--- a/chromium/third_party/blink/renderer/platform/audio/vector_math.h
+++ b/chromium/third_party/blink/renderer/platform/audio/vector_math.h
@@ -60,6 +60,9 @@ PLATFORM_EXPORT void PrepareFilterForConv(const float* filter_p,
// Vector scalar multiply and then add.
//
// dest[k*dest_stride] += scale * source[k*source_stride]
+//
+// Note: Mac has a different implementation, and it may produce slightly
+// different results from what linux and windows would do.
PLATFORM_EXPORT void Vsma(const float* source_p,
int source_stride,
const float* scale,
diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.cc b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.cc
index ca72c72dd62..ae00908b3f7 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.cc
@@ -71,7 +71,7 @@ void ActiveScriptWrappableManager::
recomputed_cnt_ = 0;
}
-void ActiveScriptWrappableManager::Trace(Visitor* visitor) {
+void ActiveScriptWrappableManager::Trace(Visitor* visitor) const {
visitor->Trace(active_script_wrappables_);
visitor->RegisterWeakCallbackMethod<
ActiveScriptWrappableManager,
diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.h b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.h
index 387f9194be5..d9ed60fce38 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_manager.h
@@ -51,7 +51,7 @@ class PLATFORM_EXPORT ActiveScriptWrappableManager final
// Does not allocate.
void IterateActiveScriptWrappables(Visitor*);
- void Trace(Visitor* visitor);
+ void Trace(Visitor* visitor) const;
private:
// Called during weakness processing. Not allowed to allocate. The next Add()
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc
index 8cce68e9a7e..413fdb58810 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -30,7 +30,7 @@ CallbackFunctionBase::CallbackFunctionBase(
}
}
-void CallbackFunctionBase::Trace(Visitor* visitor) {
+void CallbackFunctionBase::Trace(Visitor* visitor) const {
visitor->Trace(callback_function_);
visitor->Trace(callback_relevant_script_state_);
visitor->Trace(incumbent_script_state_);
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 b5a1521455f..d431f0ea99c 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(Visitor* visitor);
+ virtual void Trace(Visitor* visitor) const;
v8::Local<v8::Object> CallbackObject() {
return callback_function_.NewLocal(GetIsolate());
diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
index bf9564a3b77..45095c37353 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -32,7 +32,7 @@ CallbackInterfaceBase::CallbackInterfaceBase(
}
}
-void CallbackInterfaceBase::Trace(Visitor* visitor) {
+void CallbackInterfaceBase::Trace(Visitor* visitor) const {
visitor->Trace(callback_object_);
visitor->Trace(callback_relevant_script_state_);
visitor->Trace(incumbent_script_state_);
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 e32054f4a52..c5d98c88f2f 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(Visitor*);
+ virtual void Trace(Visitor*) const;
// 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/custom_wrappable.h b/chromium/third_party/blink/renderer/platform/bindings/custom_wrappable.h
index f7a543b205b..86af271f5b7 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/custom_wrappable.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/custom_wrappable.h
@@ -17,7 +17,7 @@ class PLATFORM_EXPORT CustomWrappable
public NameClient {
public:
virtual ~CustomWrappable() = default;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
const char* NameInHeapSnapshot() const override { return "CustomWrappable"; }
protected:
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 a5e93b4436d..90422c62d7d 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h
@@ -36,7 +36,7 @@ class PLATFORM_EXPORT DictionaryBase : public GarbageCollected<DictionaryBase> {
return v8_object;
}
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
protected:
DictionaryBase() = default;
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 e0b991138b5..396543369c6 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
@@ -20,7 +20,7 @@ void DOMDataStore::Dispose() {
}
}
-void DOMDataStore::Trace(Visitor* visitor) {
+void DOMDataStore::Trace(Visitor* visitor) const {
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 0d8cfbc5ce7..60bcc9d8f3c 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
@@ -176,7 +176,7 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> {
return wrapper_map_.find(object) != wrapper_map_.end();
}
- virtual void Trace(Visitor*);
+ virtual void Trace(Visitor*) const;
private:
// We can use a wrapper stored in a ScriptWrappable when we're in the main
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
index a48be9f22f4..bd637941ef5 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.cc
@@ -175,6 +175,26 @@ void DOMWrapperWorld::SetIsolatedWorldSecurityOrigin(
IsolatedWorldSecurityOrigins().erase(world_id);
}
+typedef HashMap<int, String> IsolatedWorldStableIdMap;
+static IsolatedWorldStableIdMap& IsolatedWorldStableIds() {
+ DCHECK(IsMainThread());
+ DEFINE_STATIC_LOCAL(IsolatedWorldStableIdMap, map, ());
+ return map;
+}
+
+String DOMWrapperWorld::NonMainWorldStableId() const {
+ DCHECK(!this->IsMainWorld());
+ return IsolatedWorldStableIds().at(GetWorldId());
+}
+
+void DOMWrapperWorld::SetNonMainWorldStableId(int32_t world_id,
+ const String& stable_id) {
+#if DCHECK_IS_ON()
+ DCHECK(!IsMainWorldId(world_id));
+#endif
+ IsolatedWorldStableIds().Set(world_id, stable_id);
+}
+
typedef HashMap<int, String> IsolatedWorldHumanReadableNameMap;
static IsolatedWorldHumanReadableNameMap& IsolatedWorldHumanReadableNames() {
DCHECK(IsMainThread());
@@ -182,7 +202,7 @@ static IsolatedWorldHumanReadableNameMap& IsolatedWorldHumanReadableNames() {
return map;
}
-String DOMWrapperWorld::NonMainWorldHumanReadableName() {
+String DOMWrapperWorld::NonMainWorldHumanReadableName() const {
DCHECK(!this->IsMainWorld());
return IsolatedWorldHumanReadableNames().at(GetWorldId());
}
diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
index 75dde53c356..571e3f6a195 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -115,8 +115,11 @@ class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted<DOMWrapperWorld> {
static DOMWrapperWorld& MainWorld();
+ static void SetNonMainWorldStableId(int32_t world_id, const String&);
+ String NonMainWorldStableId() const;
+
static void SetNonMainWorldHumanReadableName(int32_t world_id, const String&);
- String NonMainWorldHumanReadableName();
+ String NonMainWorldHumanReadableName() const;
// Associates an isolated world (see above for description) with a security
// origin. XMLHttpRequest instances used in that world will be considered
diff --git a/chromium/third_party/blink/renderer/platform/bindings/exception_state.cc b/chromium/third_party/blink/renderer/platform/bindings/exception_state.cc
index ae8625bcf19..bd58b9d7853 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/exception_state.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/exception_state.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
diff --git a/chromium/third_party/blink/renderer/platform/bindings/exception_state.h b/chromium/third_party/blink/renderer/platform/bindings/exception_state.h
index a3b5c17b9b3..7a9ea102429 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/exception_state.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/exception_state.h
@@ -32,6 +32,7 @@
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_EXCEPTION_STATE_H_
#include "base/macros.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h"
#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.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 61d9f2ca90a..8efee586e62 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc
@@ -531,18 +531,20 @@ String ParkableStringImpl::UnparkInternal() {
// variable protected by it.
base::ElapsedTimer timer;
+ auto& manager = ParkableStringManager::Instance();
if (is_on_disk()) {
base::ElapsedTimer disk_read_timer;
DCHECK(has_on_disk_data());
metadata_->compressed_ = std::make_unique<Vector<uint8_t>>();
metadata_->compressed_->Grow(metadata_->on_disk_metadata_->size());
- auto& manager = ParkableStringManager::Instance();
manager.data_allocator().Read(*metadata_->on_disk_metadata_,
metadata_->compressed_->data());
- RecordStatistics(metadata_->on_disk_metadata_->size(),
- disk_read_timer.Elapsed(), ParkingAction::kRead);
+ base::TimeDelta elapsed = disk_read_timer.Elapsed();
+ RecordStatistics(metadata_->on_disk_metadata_->size(), elapsed,
+ ParkingAction::kRead);
manager.OnReadFromDisk(this);
+ manager.RecordDiskReadTime(elapsed);
}
base::StringPiece compressed_string_piece(
@@ -579,7 +581,7 @@ String ParkableStringImpl::UnparkInternal() {
uncompressed_string_piece));
base::TimeDelta elapsed = timer.Elapsed();
- ParkableStringManager::Instance().RecordUnparkingTime(elapsed);
+ manager.RecordUnparkingTime(elapsed);
RecordStatistics(CharactersSizeInBytes(), elapsed, ParkingAction::kUnparked);
return uncompressed;
@@ -725,7 +727,7 @@ void ParkableStringImpl::PostBackgroundWritingTask() {
this, metadata_->compressed_->data(), metadata_->compressed_->size(),
Thread::Current()->GetTaskRunner());
worker_pool::PostTask(
- FROM_HERE,
+ FROM_HERE, {base::MayBlock(), base::ThreadPool()},
CrossThreadBindOnce(&ParkableStringImpl::WriteToDiskInBackground,
std::move(params)));
}
@@ -737,24 +739,27 @@ void ParkableStringImpl::WriteToDiskInBackground(
auto& allocator = ParkableStringManager::Instance().data_allocator();
base::ElapsedTimer timer;
auto metadata = allocator.Write(params->data, params->size);
- RecordStatistics(params->size, timer.Elapsed(), ParkingAction::kWritten);
+ base::TimeDelta elapsed = timer.Elapsed();
+ RecordStatistics(params->size, elapsed, ParkingAction::kWritten);
auto* task_runner = params->callback_task_runner.get();
PostCrossThreadTask(
*task_runner, FROM_HERE,
CrossThreadBindOnce(
[](std::unique_ptr<BackgroundTaskParams> params,
- std::unique_ptr<DiskDataAllocator::Metadata> metadata) {
+ std::unique_ptr<DiskDataAllocator::Metadata> metadata,
+ base::TimeDelta elapsed) {
auto* string = params->string.get();
string->OnWritingCompleteOnMainThread(std::move(params),
- std::move(metadata));
+ std::move(metadata), elapsed);
},
- std::move(params), std::move(metadata)));
+ std::move(params), std::move(metadata), elapsed));
}
void ParkableStringImpl::OnWritingCompleteOnMainThread(
std::unique_ptr<BackgroundTaskParams> params,
- std::unique_ptr<DiskDataAllocator::Metadata> on_disk_metadata) {
+ std::unique_ptr<DiskDataAllocator::Metadata> on_disk_metadata,
+ base::TimeDelta writing_time) {
DCHECK(metadata_->background_task_in_progress_);
DCHECK(!metadata_->on_disk_metadata_);
@@ -774,6 +779,10 @@ void ParkableStringImpl::OnWritingCompleteOnMainThread(
DiscardCompressedData();
metadata_->state_ = State::kOnDisk;
}
+
+ // Record the time no matter whether the string was discarded or not, as the
+ // writing cost was paid.
+ ParkableStringManager::Instance().RecordDiskWriteTime(writing_time);
}
ParkableString::ParkableString(scoped_refptr<StringImpl>&& impl) {
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 0dcfd51af92..600a74030a5 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h
@@ -207,9 +207,11 @@ class PLATFORM_EXPORT ParkableStringImpl final
// Called on the main thread after writing is done.
// |params| is the same as the one passed to PostBackgroundWritingTask()|,
// |metadata| is the on-disk metadata, nullptr if writing failed.
+ // |writing_time| is the elapsed background thread time used by disk writing.
void OnWritingCompleteOnMainThread(
std::unique_ptr<BackgroundTaskParams> params,
- std::unique_ptr<DiskDataAllocator::Metadata> metadata);
+ std::unique_ptr<DiskDataAllocator::Metadata> metadata,
+ base::TimeDelta writing_time);
void DiscardUncompressedData();
void DiscardCompressedData();
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 ccd58f82f9d..a2213dc9332 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
+#include <algorithm>
#include <utility>
#include "base/bind.h"
@@ -14,6 +15,7 @@
#include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event.h"
+#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/bindings/parkable_string.h"
#include "third_party/blink/renderer/platform/disk_data_allocator.h"
@@ -163,6 +165,8 @@ bool ParkableStringManager::OnMemoryDump(
dump->AddScalar("on_disk_size", "bytes", stats.on_disk_size);
dump->AddScalar("on_disk_footprint", "bytes",
data_allocator().disk_footprint());
+ dump->AddScalar("on_disk_free_chunks", "bytes",
+ data_allocator().free_chunks_size());
pmd->AddSuballocation(dump->guid(),
WTF::Partitions::kAllocatedObjectPoolName);
@@ -283,11 +287,6 @@ void ParkableStringManager::OnUnparked(ParkableStringImpl* was_parked_string) {
ScheduleAgingTaskIfNeeded();
}
-void ParkableStringManager::RecordUnparkingTime(
- base::TimeDelta unparking_time) {
- total_unparking_time_ += unparking_time;
-}
-
void ParkableStringManager::ParkAll(ParkableStringImpl::ParkingMode mode) {
DCHECK(IsMainThread());
DCHECK(CompressionEnabled());
@@ -334,13 +333,34 @@ void ParkableStringManager::RecordStatisticsAfter5Minutes() const {
size_t savings = stats.compressed_original_size - stats.compressed_size;
base::UmaHistogramCounts100000("Memory.ParkableString.SavingsKb.5min",
savings / 1000);
-
if (stats.compressed_original_size != 0) {
size_t ratio_percentage =
(100 * stats.compressed_size) / stats.compressed_original_size;
base::UmaHistogramPercentage("Memory.ParkableString.CompressionRatio.5min",
ratio_percentage);
}
+
+ // May not be usable, e.g. Incognito, permission or write failure.
+ if (base::FeatureList::IsEnabled(features::kParkableStringsToDisk)) {
+ base::UmaHistogramBoolean("Memory.ParkableString.DiskIsUsable.5min",
+ data_allocator().may_write());
+ }
+ // These metrics only make sense if the disk allocator is used.
+ if (data_allocator().may_write()) {
+ base::UmaHistogramTimes("Memory.ParkableString.DiskWriteTime.5min",
+ total_disk_write_time_);
+ base::UmaHistogramTimes("Memory.ParkableString.DiskReadTime.5min",
+ total_disk_read_time_);
+
+ base::UmaHistogramCounts100000(
+ "Memory.ParkableString.MemorySavingsKb.5min",
+ std::max(0, static_cast<int>(stats.savings_size)) / 1000);
+ base::UmaHistogramCounts100000("Memory.ParkableString.OnDiskSizeKb.5min",
+ stats.on_disk_size / 1000);
+ base::UmaHistogramCounts100000(
+ "Memory.ParkableString.OnDiskFootprintKb.5min",
+ static_cast<int>(data_allocator().disk_footprint()) / 1000);
+ }
}
void ParkableStringManager::AgeStringsAndPark() {
@@ -480,6 +500,8 @@ void ParkableStringManager::ResetForTesting() {
did_register_memory_pressure_listener_ = false;
total_unparking_time_ = base::TimeDelta();
total_parking_thread_time_ = base::TimeDelta();
+ total_disk_read_time_ = base::TimeDelta();
+ total_disk_write_time_ = base::TimeDelta();
unparked_strings_.clear();
parked_strings_.clear();
on_disk_strings_.clear();
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 3619af6f11f..f59aa5029d5 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
@@ -96,10 +96,20 @@ class PLATFORM_EXPORT ParkableStringManager {
void RecordStatisticsAfter5Minutes() const;
void AgeStringsAndPark();
void ScheduleAgingTaskIfNeeded();
- void RecordUnparkingTime(base::TimeDelta);
+
+ void RecordUnparkingTime(base::TimeDelta unparking_time) {
+ total_unparking_time_ += unparking_time;
+ }
void RecordParkingThreadTime(base::TimeDelta parking_thread_time) {
total_parking_thread_time_ += parking_thread_time;
}
+ void RecordDiskWriteTime(base::TimeDelta write_time) {
+ total_disk_write_time_ += write_time;
+ }
+ void RecordDiskReadTime(base::TimeDelta read_time) {
+ total_disk_read_time_ += read_time;
+ }
+
Statistics ComputeStatistics() const;
DiskDataAllocator& data_allocator() const {
@@ -123,6 +133,8 @@ class PLATFORM_EXPORT ParkableStringManager {
bool did_register_memory_pressure_listener_;
base::TimeDelta total_unparking_time_;
base::TimeDelta total_parking_thread_time_;
+ base::TimeDelta total_disk_read_time_;
+ base::TimeDelta total_disk_write_time_;
StringMap unparked_strings_;
StringMap 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 6e222ad940b..f46a56f2dc1 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
@@ -18,6 +18,7 @@
#include "base/trace_event/process_memory_dump.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/renderer/platform/bindings/parkable_string.h"
#include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
@@ -814,6 +815,10 @@ TEST_F(ParkableStringTest, ReportMemoryDump) {
MemoryAllocatorDump::Entry("on_disk_footprint", "bytes", kCompressedSize);
EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(on_disk_footprint))));
+ MemoryAllocatorDump::Entry on_disk_free_chunks =
+ MemoryAllocatorDump::Entry("on_disk_free_chunks", "bytes", 0);
+ EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(on_disk_free_chunks))));
+
// |parkable1| is compressed.
compressed =
MemoryAllocatorDump::Entry("compressed_size", "bytes", kCompressedSize);
@@ -1002,27 +1007,15 @@ TEST_F(ParkableStringTest, ReportTotalUnparkingTime) {
// compression metrics.
DisableOnDiskWriting();
- // On some platforms, initialization takes time, though it happens when
- // base::ThreadTicks is used. To prevent flakiness depending on test execution
- // ordering, force initialization.
- if (base::ThreadTicks::IsSupported())
- base::ThreadTicks::WaitUntilInitialized();
-
- // Need to make the string really large, otherwise unparking takes less than
- // 1ms, and the 0 bucket is populated.
- const size_t original_size = 5 * 1000 * 1000;
- Vector<char> data(original_size, 'a');
- ParkableString parkable(String(data.data(), data.size()).ReleaseImpl());
-
+ ParkableString parkable(MakeLargeString().ReleaseImpl());
ParkAndWait(parkable);
+
const int kNumIterations = 10;
- size_t compressed_size;
for (int i = 0; i < kNumIterations; ++i) {
parkable.ToString();
ASSERT_FALSE(parkable.Impl()->is_parked());
WaitForDelayedParking();
ASSERT_TRUE(parkable.Impl()->is_parked());
- compressed_size = parkable.Impl()->compressed_size();
WaitForDiskWriting();
WaitForAging();
CheckOnlyCpuCostTaskRemains();
@@ -1047,16 +1040,68 @@ TEST_F(ParkableStringTest, ReportTotalUnparkingTime) {
}
histogram_tester.ExpectUniqueSample("Memory.ParkableString.TotalSizeKb.5min",
- original_size / 1000, 1);
+ kSizeKb, 1);
histogram_tester.ExpectUniqueSample(
- "Memory.ParkableString.CompressedSizeKb.5min", compressed_size / 1000, 1);
+ "Memory.ParkableString.CompressedSizeKb.5min", kCompressedSize / 1000, 1);
- size_t expected_savings = original_size - compressed_size;
+ size_t expected_savings = kSizeKb * 1000 - kCompressedSize;
histogram_tester.ExpectUniqueSample("Memory.ParkableString.SavingsKb.5min",
expected_savings / 1000, 1);
histogram_tester.ExpectUniqueSample(
"Memory.ParkableString.CompressionRatio.5min",
- 100 * compressed_size / original_size, 1);
+ (100 * kCompressedSize) / (kSizeKb * 1000), 1);
+}
+
+TEST_F(ParkableStringTest, ReportTotalDiskTime) {
+ base::ScopedMockElapsedTimersForTest mock_elapsed_timers;
+ base::HistogramTester histogram_tester;
+ base::test::ScopedFeatureList features;
+ features.InitAndEnableFeature(features::kParkableStringsToDisk);
+
+ ParkableString parkable(MakeLargeString().ReleaseImpl());
+ ParkAndWait(parkable);
+
+ const int kNumIterations = 10;
+ for (int i = 0; i < kNumIterations; ++i) {
+ parkable.ToString();
+ ASSERT_FALSE(parkable.Impl()->is_parked());
+ WaitForDelayedParking();
+ ASSERT_TRUE(parkable.Impl()->is_parked());
+ WaitForDiskWriting();
+ WaitForAging();
+ CheckOnlyCpuCostTaskRemains();
+ }
+
+ task_environment_.FastForwardUntilNoTasksRemain();
+ int64_t mock_elapsed_time_ms =
+ base::ScopedMockElapsedTimersForTest::kMockElapsedTime.InMilliseconds();
+ // The string is read kNumIterations times.
+ histogram_tester.ExpectUniqueSample("Memory.ParkableString.DiskReadTime.5min",
+ mock_elapsed_time_ms * kNumIterations, 1);
+
+ // The string is only written once despite the multiple parking/unparking
+ // calls.
+ histogram_tester.ExpectUniqueSample("Memory.ParkableString.DiskIsUsable.5min",
+ true, 1);
+
+ // The string is only written once despite the multiple parking/unparking
+ // calls.
+ histogram_tester.ExpectUniqueSample(
+ "Memory.ParkableString.DiskWriteTime.5min", mock_elapsed_time_ms, 1);
+
+ histogram_tester.ExpectUniqueSample("Memory.ParkableString.TotalSizeKb.5min",
+ kSizeKb, 1);
+ histogram_tester.ExpectUniqueSample(
+ "Memory.ParkableString.CompressedSizeKb.5min", 0, 1);
+
+ size_t expected_savings = kSizeKb * 1000 - kCompressedSize;
+ histogram_tester.ExpectUniqueSample(
+ "Memory.ParkableString.MemorySavingsKb.5min", expected_savings / 1000, 1);
+ histogram_tester.ExpectUniqueSample("Memory.ParkableString.OnDiskSizeKb.5min",
+ kCompressedSize / 1000, 1);
+ histogram_tester.ExpectUniqueSample(
+ "Memory.ParkableString.OnDiskFootprintKb.5min", kCompressedSize / 1000,
+ 1);
}
class ParkableStringTestWithQueuedThreadPool : public ParkableStringTest {
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 fe8f5214309..6e15daf4633 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/script_state.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_state.h
@@ -53,7 +53,7 @@ class V8PerContextData;
// ToV8(...);
// }
//
-// virtual void Trace(Visitor* visitor) {
+// virtual void Trace(Visitor* visitor) const {
// visitor->Trace(script_state_); // ScriptState also needs to be traced.
// }
//
@@ -124,7 +124,7 @@ class PLATFORM_EXPORT ScriptState final : public GarbageCollected<ScriptState> {
ScriptState(v8::Local<v8::Context>, scoped_refptr<DOMWrapperWorld>);
~ScriptState();
- void Trace(Visitor*) {}
+ void Trace(Visitor*) const {}
static ScriptState* Current(v8::Isolate* isolate) { // DEPRECATED
return From(isolate->GetCurrentContext());
@@ -241,7 +241,7 @@ class ScriptStateProtectingContext final
}
}
- void Trace(Visitor* visitor) { visitor->Trace(script_state_); }
+ void Trace(Visitor* visitor) const { 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 aaea2cdebb7..14f03fa1254 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc
@@ -38,7 +38,7 @@ v8::Local<v8::Object> ScriptWrappable::AssociateWithWrapper(
wrapper_type_info, wrapper);
}
-void ScriptWrappable::Trace(Visitor* visitor) {
+void ScriptWrappable::Trace(Visitor* visitor) const {
visitor->Trace(main_world_wrapper_);
}
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 2a0f41e3f32..75dfd25a342 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h
@@ -68,7 +68,7 @@ class PLATFORM_EXPORT ScriptWrappable
const char* NameInHeapSnapshot() const override;
- virtual void Trace(Visitor*);
+ virtual void Trace(Visitor*) const;
template <typename T>
T* ToImpl() {
diff --git a/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc b/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc
index 6ff600c0959..3cc1561ca93 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/string_resource.cc
@@ -86,8 +86,7 @@ AtomicString StringTraits<AtomicString>::FromV8String(
}
template <typename StringType>
-StringType ToBlinkString(v8::Local<v8::String> v8_string,
- ExternalMode external) {
+StringType ToBlinkString(v8::Local<v8::String> v8_string, ExternalMode mode) {
{
// This portion of this function is very hot in certain Dromeao benchmarks.
v8::String::Encoding encoding;
@@ -132,7 +131,7 @@ StringType ToBlinkString(v8::Local<v8::String> v8_string,
: StringTraits<StringType>::template FromV8String<
V8StringTwoBytesTrait>(isolate, v8_string, length));
- if (external != kExternalize || !v8_string->CanMakeExternal())
+ if (mode != kExternalize || !v8_string->CanMakeExternal())
return result;
if (result.Is8Bit()) {
@@ -154,6 +153,33 @@ template String ToBlinkString<String>(v8::Local<v8::String>, ExternalMode);
template AtomicString ToBlinkString<AtomicString>(v8::Local<v8::String>,
ExternalMode);
+StringView ToBlinkStringView(v8::Local<v8::String> v8_string,
+ StringView::StackBackingStore& backing_store,
+ ExternalMode mode) {
+ AtomicString result = ToBlinkString<AtomicString>(v8_string, mode);
+ // IsExternal() only checks for 2-byte external.
+ if (v8_string->IsExternal() || v8_string->IsExternalOneByte()) {
+ // The string has been externalized so v8_string will keep the StringImpl
+ // underlying |result| allow making it safe to just return it as the
+ // StringView.
+ return result;
+ }
+
+ // Externalization has failed meaning |result| cannot be counted on to exist
+ // after this function exits. Copy data in |backing_store| so the returned
+ // StringView can have a well defined lifetime.
+ int length = v8_string->Length();
+ if (result.Is8Bit()) {
+ LChar* lchar = backing_store.Realloc<LChar>(length);
+ memcpy(lchar, result.Characters8(), result.CharactersSizeInBytes());
+ return StringView(lchar, length);
+ } else {
+ UChar* uchar = backing_store.Realloc<UChar>(length);
+ memcpy(uchar, result.Characters16(), result.CharactersSizeInBytes());
+ return StringView(uchar, length);
+ }
+}
+
// Fast but non thread-safe version.
static String ToBlinkStringFast(int value) {
// Caching of small strings below is not thread safe: newly constructed
diff --git a/chromium/third_party/blink/renderer/platform/bindings/string_resource.h b/chromium/third_party/blink/renderer/platform/bindings/string_resource.h
index b8748462e43..02d7d11beb9 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/string_resource.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/string_resource.h
@@ -242,6 +242,16 @@ enum ExternalMode { kExternalize, kDoNotExternalize };
template <typename StringType>
PLATFORM_EXPORT StringType ToBlinkString(v8::Local<v8::String>, ExternalMode);
+
+// This method is similar to ToBlinkString() except when the underlying
+// v8::String cannot be externalized (often happens with short strings like "id"
+// on 64-bit platforms where V8 uses pointer compression) the v8::String is
+// copied into the given StringView::StackBackingStore which avoids creating an
+// AtomicString unnecessarily.
+PLATFORM_EXPORT StringView ToBlinkStringView(v8::Local<v8::String>,
+ StringView::StackBackingStore&,
+ ExternalMode);
+
PLATFORM_EXPORT String ToBlinkString(int value);
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h
index 5af791f5332..b94a604962d 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_string.h
@@ -33,7 +33,7 @@ class GC_PLUGIN_IGNORE("crbug.com/841830")
void Concat(v8::Isolate*, const String&);
String Flatten(v8::Isolate*) const;
- virtual void Trace(Visitor* visitor) { visitor->Trace(string_); }
+ virtual void Trace(Visitor* visitor) const { visitor->Trace(string_); }
const char* NameInHeapSnapshot() const override {
return "TraceWrapperV8String";
diff --git a/chromium/third_party/blink/renderer/platform/bindings/union_base.h b/chromium/third_party/blink/renderer/platform/bindings/union_base.h
index 35ce7f8151b..7db60757497 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/union_base.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/union_base.h
@@ -28,7 +28,7 @@ class PLATFORM_EXPORT UnionBase {
v8::Isolate* isolate,
v8::Local<v8::Object> creation_context) const = 0;
- void Trace(Visitor*) {}
+ void Trace(Visitor*) const {}
protected:
UnionBase() = default;
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
index fc4c903cae3..ece99f6547b 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h
@@ -53,16 +53,19 @@ class PLATFORM_EXPORT V8InterfaceBridgeBase {
FeatureSelector& operator=(const FeatureSelector&) = default;
FeatureSelector& operator=(FeatureSelector&&) = default;
+ // Returns true if all properties that are associated with the features
+ // enabled at this moment should be installed.
+ bool IsAll() const { return does_select_all_; }
+
// Returns true if properties should be installed. Arguments |featureN|
// represent the origin trial features to which the properties are
- // associated. No argument means that the properties are not associated
- // with any origin trial feature.
- bool AnyOf() const { return does_select_all_; }
- bool AnyOf(OriginTrialFeature feature1) const {
- return does_select_all_ || selector_ == feature1;
+ // associated.
+ bool IsAnyOf(OriginTrialFeature feature1) const {
+ return selector_ == feature1;
}
- bool AnyOf(OriginTrialFeature feature1, OriginTrialFeature feature2) const {
- return does_select_all_ || selector_ == feature1 || selector_ == feature2;
+ bool IsAnyOf(OriginTrialFeature feature1,
+ OriginTrialFeature feature2) const {
+ return selector_ == feature1 || selector_ == feature2;
}
private:
diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
index e1378ffcd7e..ac84b104187 100644
--- a/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
+++ b/chromium/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
@@ -45,8 +45,8 @@ v8::MaybeLocal<v8::Object> V8ObjectConstructor::NewInstance(
v8::MicrotasksScope microtasks_scope(
isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
// Construct without side effect only in ConstructorMode::kWrapExistingObject
- // cases. This allows whitelisted methods to correctly set return values
- // without invoking Blink's internal constructors.
+ // cases. Allowed methods can correctly set return values without invoking
+ // Blink's internal constructors.
v8::MaybeLocal<v8::Object> result = function->NewInstanceWithSideEffectType(
isolate->GetCurrentContext(), argc, argv,
v8::SideEffectType::kHasNoSideEffect);
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 aecc59b9d21..c70887a5cfd 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
@@ -107,7 +107,7 @@ class PLATFORM_EXPORT V8PerIsolateData {
public:
virtual ~GarbageCollectedData() = default;
virtual void WillBeDestroyed() {}
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
};
static v8::Isolate* Initialize(scoped_refptr<base::SingleThreadTaskRunner>,
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
index 5c7acd6c99d..52269d57c41 100644
--- 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
@@ -28,6 +28,10 @@ struct V8ReturnValue {
// Support compile-time overload resolution by making each value have its own
// type.
+ // Applies strict typing to IDL primitive types.
+ template <typename T>
+ struct PrimitiveType {};
+
// Nullable or not
enum NonNullable { kNonNullable };
enum Nullable { kNullable };
@@ -166,6 +170,28 @@ void V8SetReturnValue(const CallbackInfo& info, double value) {
info.GetReturnValue().Set(value);
}
+// Primitive types with IDL type
+//
+// |IdlType| represents a C++ type corresponding to an IDL type, and |value| is
+// passed from Blink implementation and its type occasionally does not match
+// the IDL type because Blink is not always respectful to IDL types. These
+// functions fix such a type mismatch.
+template <typename CallbackInfo, typename BlinkType, typename IdlType>
+typename std::enable_if_t<std::is_arithmetic<BlinkType>::value ||
+ std::is_enum<BlinkType>::value>
+V8SetReturnValue(const CallbackInfo& info,
+ BlinkType value,
+ V8ReturnValue::PrimitiveType<IdlType>) {
+ V8SetReturnValue(info, IdlType(value));
+}
+
+template <typename CallbackInfo, typename BlinkType>
+void V8SetReturnValue(const CallbackInfo& info,
+ BlinkType* value,
+ V8ReturnValue::PrimitiveType<bool>) {
+ V8SetReturnValue(info, bool(value));
+}
+
// String types
template <typename CallbackInfo>
void V8SetReturnValue(const CallbackInfo& info,
@@ -334,12 +360,16 @@ void V8SetReturnValue(const CallbackInfo& 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
+template <typename CallbackInfo, typename T, typename... ExtraArgs>
+void V8SetReturnValue(const CallbackInfo& info,
+ base::Optional<T> value,
+ ExtraArgs... extra_args) {
+ if (value.has_value()) {
+ V8SetReturnValue(info, value.value(),
+ std::forward<ExtraArgs>(extra_args)...);
+ } else {
info.GetReturnValue().SetNull();
+ }
}
} // namespace bindings
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 5bcfa0a11ea..e8a6e90786e 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc
index 9387ae06507..e0b406ff4d7 100644
--- a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc
+++ b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc
@@ -26,7 +26,7 @@ void ContextLifecycleObserver::SetContextLifecycleNotifier(
notifier_->AddContextLifecycleObserver(this);
}
-void ContextLifecycleObserver::Trace(Visitor* visitor) {
+void ContextLifecycleObserver::Trace(Visitor* visitor) const {
visitor->Trace(notifier_);
}
diff --git a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h
index cc5ef14ff92..b4a4198fba4 100644
--- a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h
+++ b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h
@@ -27,7 +27,7 @@ class PLATFORM_EXPORT ContextLifecycleObserver : public GarbageCollectedMixin {
virtual bool IsExecutionContextLifecycleObserver() const { return false; }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
protected:
ContextLifecycleObserver() = default;
diff --git a/chromium/third_party/blink/renderer/platform/crypto_result.h b/chromium/third_party/blink/renderer/platform/crypto_result.h
index 9b4e3d54825..32f136658ac 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc b/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc
index 03cce1c2b7d..faffd17396e 100644
--- a/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc
+++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
@@ -179,6 +180,11 @@ int DiskDataAllocator::DoWrite(int64_t offset, const char* data, int size) {
}
void DiskDataAllocator::DoRead(int64_t offset, char* data, int size) {
+ // This happens on the main thread, which is typically not allowed. This is
+ // fine as this is expected to happen rarely, and only be slow with memory
+ // pressure, in which case writing to/reading from disk is better than
+ // swapping out random parts of the memory. See crbug.com/1029320 for details.
+ base::ScopedAllowBlocking allow_blocking;
int rv = file_.Read(offset, data, size);
// Can only crash, since we cannot continue without the data.
PCHECK(rv == size) << "Likely file corruption.";
diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator.h b/chromium/third_party/blink/renderer/platform/disk_data_allocator.h
index f1d92bb80e9..c7376ba2928 100644
--- a/chromium/third_party/blink/renderer/platform/disk_data_allocator.h
+++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator.h
@@ -81,6 +81,11 @@ class PLATFORM_EXPORT DiskDataAllocator : public mojom::blink::DiskAllocator {
return file_tail_;
}
+ size_t free_chunks_size() {
+ MutexLocker locker(mutex_);
+ return free_chunks_size_;
+ }
+
protected:
// Protected methods for testing.
DiskDataAllocator();
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
index e6a49641f41..25e1d65ded0 100644
--- a/chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc
+++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc
@@ -44,6 +44,14 @@ class DiskDataAllocatorTest : public ::testing::Test {
}
protected:
+ void SetUp() override {
+ // On some platforms, initialization takes time, though it happens when
+ // base::ThreadTicks is used. To prevent flakiness depending on test
+ // execution ordering, force initialization.
+ if (base::ThreadTicks::IsSupported())
+ base::ThreadTicks::WaitUntilInitialized();
+ }
+
base::test::TaskEnvironment task_environment_;
};
@@ -178,6 +186,8 @@ TEST_F(DiskDataAllocatorTest, FreeChunksMerging) {
auto allocator = std::make_unique<InMemoryDataAllocator>();
auto chunks = Allocate(allocator.get(), kSize, 4);
+ EXPECT_EQ(static_cast<int64_t>(4 * kSize), allocator->disk_footprint());
+ EXPECT_EQ(0u, allocator->free_chunks_size());
// Layout is (indices in |chunks|):
// | 0 | 1 | 2 | 3 |
@@ -192,9 +202,11 @@ TEST_F(DiskDataAllocatorTest, FreeChunksMerging) {
allocator->Discard(std::move(chunks[2]));
EXPECT_EQ(1u, allocator->FreeChunks().size());
EXPECT_EQ(3 * kSize, allocator->FreeChunks().begin()->second);
+ EXPECT_EQ(3 * kSize, allocator->free_chunks_size());
allocator->Discard(std::move(chunks[3]));
EXPECT_EQ(1u, allocator->FreeChunks().size());
EXPECT_EQ(4 * kSize, allocator->FreeChunks().begin()->second);
+ EXPECT_EQ(static_cast<int64_t>(4 * kSize), allocator->disk_footprint());
allocator = std::make_unique<InMemoryDataAllocator>();
chunks = Allocate(allocator.get(), kSize, 4);
@@ -207,6 +219,7 @@ TEST_F(DiskDataAllocatorTest, FreeChunksMerging) {
EXPECT_EQ(2 * kSize, allocator->FreeChunks().begin()->second);
allocator->Discard(std::move(chunks[0]));
EXPECT_EQ(2u, allocator->FreeChunks().size());
+ EXPECT_EQ(3 * kSize, allocator->free_chunks_size());
// Multiple merges: left, then right.
allocator->Discard(std::move(chunks[1]));
EXPECT_EQ(1u, allocator->FreeChunks().size());
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 be409773273..7d2b8f8d26d 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/mediastream/DEPS b/chromium/third_party/blink/renderer/platform/exported/mediastream/DEPS
deleted file mode 100644
index 0544038e729..00000000000
--- a/chromium/third_party/blink/renderer/platform/exported/mediastream/DEPS
+++ /dev/null
@@ -1,9 +0,0 @@
-include_rules = [
- "+media/base",
-]
-
-specific_include_rules = {
- "media_stream_audio_test\.cc" : [
- "+base/threading/platform_thread.h",
- ],
-}
diff --git a/chromium/third_party/blink/renderer/platform/exported/platform.cc b/chromium/third_party/blink/renderer/platform/exported/platform.cc
index c8f1423009d..214d918ff0e 100644
--- a/chromium/third_party/blink/renderer/platform/exported/platform.cc
+++ b/chromium/third_party/blink/renderer/platform/exported/platform.cc
@@ -48,6 +48,7 @@
#include "third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/heap/gc_task_runner.h"
+#include "third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/instrumentation/instance_counters_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h"
#include "third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.h"
@@ -237,6 +238,9 @@ void Platform::InitializeMainThreadCommon(Platform* platform,
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
ParkableStringManagerDumpProvider::Instance(), "ParkableStrings",
base::ThreadTaskRunnerHandle::Get());
+ base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+ CanvasMemoryDumpProvider::Instance(), "Canvas",
+ base::ThreadTaskRunnerHandle::Get());
RendererResourceCoordinator::MaybeInitialize();
// Use a delayed idle task as this is low priority work that should stop when
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 28d5befee6e..e6518ec2188 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
@@ -13,36 +13,32 @@ namespace blink {
WebBlobInfo::WebBlobInfo(const WebString& uuid,
const WebString& type,
uint64_t size,
- mojo::ScopedMessagePipeHandle handle)
- : WebBlobInfo(
- BlobDataHandle::Create(uuid,
- type,
- size,
- mojo::PendingRemote<mojom::blink::Blob>(
- std::move(handle),
- mojom::blink::Blob::Version_))) {}
+ CrossVariantMojoRemote<mojom::BlobInterfaceBase> blob)
+ : WebBlobInfo(BlobDataHandle::Create(
+ uuid,
+ type,
+ size,
+ mojo::PendingRemote<mojom::blink::Blob>(std::move(blob)))) {}
WebBlobInfo::WebBlobInfo(const WebString& uuid,
const WebString& file_name,
const WebString& type,
const base::Optional<base::Time>& last_modified,
uint64_t size,
- mojo::ScopedMessagePipeHandle handle)
- : WebBlobInfo(
- BlobDataHandle::Create(uuid,
- type,
- size,
- mojo::PendingRemote<mojom::blink::Blob>(
- std::move(handle),
- mojom::blink::Blob::Version_)),
- file_name,
- last_modified) {}
+ CrossVariantMojoRemote<mojom::BlobInterfaceBase> blob)
+ : WebBlobInfo(BlobDataHandle::Create(
+ uuid,
+ type,
+ size,
+ mojo::PendingRemote<mojom::blink::Blob>(std::move(blob))),
+ file_name,
+ last_modified) {}
// static
WebBlobInfo WebBlobInfo::BlobForTesting(const WebString& uuid,
const WebString& type,
uint64_t size) {
- return WebBlobInfo(uuid, type, size, mojo::MessagePipe().handle0);
+ return WebBlobInfo(uuid, type, size, mojo::NullRemote());
}
// static
@@ -50,8 +46,7 @@ WebBlobInfo WebBlobInfo::FileForTesting(const WebString& uuid,
const WebString& file_name,
const WebString& type) {
return WebBlobInfo(uuid, file_name, type, base::nullopt,
- std::numeric_limits<uint64_t>::max(),
- mojo::MessagePipe().handle0);
+ std::numeric_limits<uint64_t>::max(), mojo::NullRemote());
}
WebBlobInfo::~WebBlobInfo() {
@@ -64,10 +59,11 @@ WebBlobInfo::WebBlobInfo(const WebBlobInfo& other) {
WebBlobInfo& WebBlobInfo::operator=(const WebBlobInfo& other) = default;
-mojo::ScopedMessagePipeHandle WebBlobInfo::CloneBlobHandle() const {
+CrossVariantMojoRemote<mojom::BlobInterfaceBase> WebBlobInfo::CloneBlobRemote()
+ const {
if (!blob_handle_)
- return mojo::ScopedMessagePipeHandle();
- return blob_handle_->CloneBlobRemote().PassPipe();
+ return mojo::NullRemote();
+ return blob_handle_->CloneBlobRemote();
}
WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle)
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc b/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc
index 52c392f4fda..5e0c5cb0419 100644
--- a/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc
+++ b/chromium/third_party/blink/renderer/platform/exported/web_drag_data.cc
@@ -33,12 +33,10 @@
namespace blink {
void WebDragData::SetItems(WebVector<Item> item_list) {
- DCHECK(!IsNull());
item_list_.Swap(item_list);
}
void WebDragData::AddItem(const Item& item) {
- DCHECK(!IsNull());
WebVector<Item> item_list(item_list_.size() + 1);
for (unsigned i = 0; i < item_list_.size(); ++i)
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 83c1f68f7d3..6f17710ebc6 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
@@ -92,8 +92,8 @@ bool WebHTTPBody::ElementAt(size_t index, Element& result) const {
result.blob_uuid = element.blob_uuid_;
result.blob_length = std::numeric_limits<uint64_t>::max();
if (element.optional_blob_data_handle_) {
- result.optional_blob_handle =
- element.optional_blob_data_handle_->CloneBlobRemote().PassPipe();
+ result.optional_blob =
+ element.optional_blob_data_handle_->CloneBlobRemote();
result.blob_length = element.optional_blob_data_handle_->size();
}
break;
@@ -103,7 +103,7 @@ bool WebHTTPBody::ElementAt(size_t index, Element& result) const {
data_pipe_getter;
element.data_pipe_getter_->GetDataPipeGetter()->Clone(
data_pipe_getter.InitWithNewPipeAndPassReceiver());
- result.data_pipe_getter = data_pipe_getter.PassPipe();
+ result.data_pipe_getter = std::move(data_pipe_getter);
break;
}
@@ -136,25 +136,21 @@ void WebHTTPBody::AppendBlob(const WebString& uuid) {
private_->AppendBlob(uuid, nullptr);
}
-void WebHTTPBody::AppendBlob(const WebString& uuid,
- uint64_t length,
- mojo::ScopedMessagePipeHandle blob_handle) {
+void WebHTTPBody::AppendBlob(
+ const WebString& uuid,
+ uint64_t length,
+ CrossVariantMojoRemote<mojom::BlobInterfaceBase> blob) {
EnsureMutable();
- mojo::PendingRemote<mojom::blink::Blob> blob_remote(
- std::move(blob_handle), mojom::blink::Blob::Version_);
private_->AppendBlob(
uuid, BlobDataHandle::Create(uuid, "" /* type is not necessary */, length,
- std::move(blob_remote)));
+ std::move(blob)));
}
-void WebHTTPBody::AppendDataPipe(mojo::ScopedMessagePipeHandle message_pipe) {
+void WebHTTPBody::AppendDataPipe(
+ CrossVariantMojoRemote<network::mojom::DataPipeGetterInterfaceBase>
+ data_pipe_getter) {
EnsureMutable();
- // Convert the raw message pipe to
- // mojo::Remote<network::mojom::blink::DataPipeGetter>.
- mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter(
- std::move(message_pipe), 0u);
-
auto wrapped =
base::MakeRefCounted<WrappedDataPipeGetter>(std::move(data_pipe_getter));
private_->AppendDataPipe(std::move(wrapped));
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 2cc5fa666d8..ceca33cba22 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
@@ -50,6 +50,10 @@ void WebRuntimeFeatures::EnableBrowserVerifiedUserActivationMouse(bool enable) {
RuntimeEnabledFeatures::SetBrowserVerifiedUserActivationMouseEnabled(enable);
}
+void WebRuntimeFeatures::EnableClickPointerEvent(bool enable) {
+ RuntimeEnabledFeatures::SetClickPointerEventEnabled(enable);
+}
+
void WebRuntimeFeatures::EnableExperimentalFeatures(bool enable) {
RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(enable);
}
@@ -58,6 +62,12 @@ void WebRuntimeFeatures::EnableWebBluetooth(bool enable) {
RuntimeEnabledFeatures::SetWebBluetoothEnabled(enable);
}
+void WebRuntimeFeatures::EnableWebBluetoothRemoteCharacteristicNewWriteValue(
+ bool enable) {
+ RuntimeEnabledFeatures::
+ SetWebBluetoothRemoteCharacteristicNewWriteValueEnabled(enable);
+}
+
void WebRuntimeFeatures::EnableWebBluetoothScanning(bool enable) {
RuntimeEnabledFeatures::SetWebBluetoothScanningEnabled(enable);
}
@@ -251,6 +261,10 @@ void WebRuntimeFeatures::EnableMediaCapture(bool enable) {
RuntimeEnabledFeatures::SetMediaCaptureEnabled(enable);
}
+void WebRuntimeFeatures::EnableMediaFeeds(bool enable) {
+ RuntimeEnabledFeatures::SetMediaFeedsEnabled(enable);
+}
+
void WebRuntimeFeatures::EnableMediaSession(bool enable) {
RuntimeEnabledFeatures::SetMediaSessionEnabled(enable);
}
@@ -457,18 +471,26 @@ void WebRuntimeFeatures::EnableWebXRARModule(bool enable) {
RuntimeEnabledFeatures::SetWebXRARModuleEnabled(enable);
}
-void WebRuntimeFeatures::EnableWebXRHitTest(bool enable) {
- RuntimeEnabledFeatures::SetWebXRHitTestEnabled(enable);
+void WebRuntimeFeatures::EnableWebXRCameraAccess(bool enable) {
+ RuntimeEnabledFeatures::SetWebXRCameraAccessEnabled(enable);
}
-void WebRuntimeFeatures::EnableWebXRIncubations(bool enable) {
- RuntimeEnabledFeatures::SetWebXRIncubationsEnabled(enable);
+void WebRuntimeFeatures::EnableWebXRHitTest(bool enable) {
+ RuntimeEnabledFeatures::SetWebXRHitTestEnabled(enable);
}
void WebRuntimeFeatures::EnableWebXRLightEstimation(bool enable) {
RuntimeEnabledFeatures::SetWebXRLightEstimationEnabled(enable);
}
+void WebRuntimeFeatures::EnableWebXRPlaneDetection(bool enable) {
+ RuntimeEnabledFeatures::SetWebXRPlaneDetectionEnabled(enable);
+}
+
+void WebRuntimeFeatures::EnableWebXRReflectionEstimation(bool enable) {
+ RuntimeEnabledFeatures::SetWebXRReflectionEstimationEnabled(enable);
+}
+
void WebRuntimeFeatures::EnablePresentationAPI(bool enable) {
RuntimeEnabledFeatures::SetPresentationEnabled(enable);
}
@@ -621,6 +643,10 @@ void WebRuntimeFeatures::EnableSignedExchangeSubresourcePrefetch(bool enable) {
RuntimeEnabledFeatures::SetSignedExchangeSubresourcePrefetchEnabled(enable);
}
+void WebRuntimeFeatures::EnableSubresourceWebBundles(bool enable) {
+ RuntimeEnabledFeatures::SetSubresourceWebBundlesEnabled(enable);
+}
+
void WebRuntimeFeatures::EnableIdleDetection(bool enable) {
RuntimeEnabledFeatures::SetIdleDetectionEnabled(enable);
}
@@ -669,10 +695,18 @@ void WebRuntimeFeatures::EnableInstalledApp(bool enable) {
RuntimeEnabledFeatures::SetInstalledAppEnabled(enable);
}
+void WebRuntimeFeatures::EnableTransformInterop(bool enable) {
+ RuntimeEnabledFeatures::SetTransformInteropEnabled(enable);
+}
+
void WebRuntimeFeatures::EnableVideoWakeLockOptimisationHiddenMuted(
bool enable) {
RuntimeEnabledFeatures::SetVideoWakeLockOptimisationHiddenMutedEnabled(
enable);
}
+void WebRuntimeFeatures::EnableContentIndex(bool enable) {
+ RuntimeEnabledFeatures::SetContentIndexEnabled(enable);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/exported/web_string.cc b/chromium/third_party/blink/renderer/platform/exported/web_string.cc
index fbe4828fcca..6d9cc6ffa84 100644
--- a/chromium/third_party/blink/renderer/platform/exported/web_string.cc
+++ b/chromium/third_party/blink/renderer/platform/exported/web_string.cc
@@ -90,12 +90,6 @@ WebString WebString::FromUTF16(const base::string16& s) {
return WebString(s.data(), s.length());
}
-WebString WebString::FromUTF16(const base::NullableString16& s) {
- if (s.is_null())
- return WebString();
- return WebString(s.string().data(), s.string().length());
-}
-
WebString WebString::FromUTF16(const base::Optional<base::string16>& s) {
if (!s.has_value())
return WebString();
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 6723c059edb..2ec89a1d43a 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
@@ -111,6 +111,9 @@ void WebURLResponse::SetLoadTiming(
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->SetWorkerFetchStart(mojo_timing.service_worker_fetch_start);
+ timing->SetWorkerRespondWithSettled(
+ mojo_timing.service_worker_respond_with_settled);
timing->SetSendStart(mojo_timing.send_start);
timing->SetSendEnd(mojo_timing.send_end);
timing->SetReceiveHeadersStart(mojo_timing.receive_headers_start);
@@ -126,6 +129,10 @@ void WebURLResponse::SetHTTPLoadInfo(const WebHTTPLoadInfo& value) {
resource_response_->SetResourceLoadInfo(value);
}
+base::Time WebURLResponse::ResponseTime() const {
+ return resource_response_->ResponseTime();
+}
+
void WebURLResponse::SetResponseTime(base::Time response_time) {
resource_response_->SetResponseTime(response_time);
}
@@ -340,6 +347,16 @@ void WebURLResponse::SetWasFetchedViaServiceWorker(bool value) {
resource_response_->SetWasFetchedViaServiceWorker(value);
}
+network::mojom::FetchResponseSource
+WebURLResponse::GetServiceWorkerResponseSource() const {
+ return resource_response_->GetServiceWorkerResponseSource();
+}
+
+void WebURLResponse::SetServiceWorkerResponseSource(
+ network::mojom::FetchResponseSource value) {
+ resource_response_->SetServiceWorkerResponseSource(value);
+}
+
void WebURLResponse::SetWasFallbackRequiredByServiceWorker(bool value) {
resource_response_->SetWasFallbackRequiredByServiceWorker(value);
}
@@ -367,6 +384,10 @@ bool WebURLResponse::HasUrlListViaServiceWorker() const {
return resource_response_->UrlListViaServiceWorker().size() > 0;
}
+WebString WebURLResponse::CacheStorageCacheName() const {
+ return resource_response_->CacheStorageCacheName();
+}
+
void WebURLResponse::SetCacheStorageCacheName(
const WebString& cache_storage_cache_name) {
resource_response_->SetCacheStorageCacheName(cache_storage_cache_name);
diff --git a/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md b/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md
index 0f5747cf15d..dc2070493da 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md
+++ b/chromium/third_party/blink/renderer/platform/fonts/LocaleInFonts.md
@@ -52,12 +52,12 @@ Blink uses the following prioritized list to determine the script.
This result is available at `ComputedStyle::getFontDescription().localeOrDefault().script()`.
-[generic-family]: https://drafts.csswg.org/css-fonts-3/#generic-family-value
+[generic-family]: https://drafts.csswg.org/css-fonts/#generic-family-value
[Advanced Font Settings]: https://chrome.google.com/webstore/detail/advanced-font-settings/caclkomlalccbpcdllchkeecicepbmbm
-## System Font Fallback
+## Installed Font Fallback
-[CSS Fonts] defines a concept of [system font fallback],
+[CSS Fonts] defines a concept of [installed font fallback],
though its behavior is UA dependent.
As Blink tries to match the font fallback behavior
@@ -66,8 +66,27 @@ the logic varies by platforms.
While the complete logic varies by platforms,
we try to share parts of the logic where possible.
-[CSS Fonts]: https://drafts.csswg.org/css-fonts-3/
-[system font fallback]: https://drafts.csswg.org/css-fonts-3/#system-font-fallback
+[CSS Fonts]: https://drafts.csswg.org/css-fonts/
+[installed font fallback]: https://drafts.csswg.org/css-fonts/#installed-font-fallback
+
+### Emojis
+
+If we've determined that a character is [emoji-default], also known as "emoji
+in emoji" representation, we treat the character a bit differently. The goal is
+to not only find a font that supports emojis, but also to prioritize color
+emoji fonts over traditional monochrome fonts that happen to have the glyph.
+
+On Android/Skia, Linux, and Windows, Blink will pass the special locale
+`und-Zsye` to the operating system when looking for an emoji font. The [Zsye]
+script tag is defined by UTS #51 as "prefer emoji style for characters that
+have both text and emoji styles available", which is precisely what we need.
+
+On Linux, Blink will additionally always use U+1F46A FAMILY (👪) when matching
+potential candidates to increase the odds of finding the right emoji font, in
+case the installed emoji font doesn't support the actual emoji in question.
+
+[emoji-default]: https://unicode.org/reports/tr51/#Presentation_Style
+[Zsye]: https://unicode.org/reports/tr51/#Emoji_Script
### Unified Han Ideographs
@@ -75,7 +94,7 @@ As seen in [CJK Unified Ideographs code charts] in Unicode,
glyphs of Han Ideographs vary by locales.
To render correct glyphs,
-the system font fallback uses the following prioritized list of locales.
+the installed font fallback uses the following prioritized list of locales.
1. The [language of a node] as defined in HTML, if known.
2. The list of languages the browser sends in the [Accept-Language] header.
@@ -89,7 +108,7 @@ For this purpose,
`LayoutLocale::hasScriptForHan()` determines whether
the locale can choose the correct font for the Unified Han Ideographs or not.
-When the system font fallback needs to determine the font
+When the installed font fallback needs to determine the font
for a Unified Han Ideograph,
it uses `scriptForHan()` of the first locale in the prioritized list
that has `hasScriptForHan()` true.
diff --git a/chromium/third_party/blink/renderer/platform/fonts/README.md b/chromium/third_party/blink/renderer/platform/fonts/README.md
index c926090d963..ef7f2b3a7af 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/README.md
+++ b/chromium/third_party/blink/renderer/platform/fonts/README.md
@@ -157,7 +157,7 @@ Emoji place additional requirements in isolating sub-runs for shaping. Emoji
Unicode code points and code point sequences have different default presentation
styles, text-default, or emoji-default. This is defined in the
section
-[Presentation Style of Unicode Technical Report #51](http://unicode.org/draft/reports/tr51/tr51.html#Presentation_Style). So
+[Presentation Style of Unicode Technical Standard #51](https://unicode.org/reports/tr51/#Presentation_Style). So
in order to select the correct font for emoji presentation — either a color
font, or a regular contour font — the incoming text needs to be segmented and
isolated by its emoji properties as well.
@@ -359,7 +359,9 @@ fonts are searched next. This behavior matches the requirements of the font
style matching algorithm of
the
[CSS Fonts specification](https://drafts.csswg.org/css-fonts/#font-style-matching),
-which mandates to prioritize web fonts over system fonts.
+which mandates to prioritize web fonts over system fonts. Some additional
+details can be found in
+[LocaleInFonts.md](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/fonts/LocaleInFonts.md#Installed-Font-Fallback).
`FontFallbackIterator` is intialized with a `FontFallbackList` and starts
retrieving fonts from this list as its first source for fonts. If during shaping
@@ -379,4 +381,5 @@ additional system fonts pulled in to the shaping process.
In summary, `FontFallbackIterator` feeds fonts from the CSS `font-family` list
as well as system fallback fonts to `HarfBuzzShaper` for use in the shaping
iterations until ideally all gaps are filled and the full text run can be drawn
-with the correct glyphs.
+with the correct glyphs. When there are gaps, and the .notdef tofu character
+must be rendered, the primary font is used for this.
diff --git a/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list_test.cc b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list_test.cc
index ddda0507963..238ccae98f6 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list_test.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list_test.cc
@@ -13,7 +13,7 @@ namespace blink {
#if defined(OS_WIN)
static void TestBitmapGlyphsBlockListed(AtomicString windows_family_name,
- bool blacklisted_expected) {
+ bool block_listed_expected) {
FontCache* font_cache = FontCache::GetFontCache();
FontDescription font_description;
FontFamily font_family;
@@ -24,7 +24,7 @@ static void TestBitmapGlyphsBlockListed(AtomicString windows_family_name,
ASSERT_TRUE(simple_font_data);
const FontPlatformData& font_platform_data = simple_font_data->PlatformData();
ASSERT_TRUE(font_platform_data.Typeface());
- ASSERT_EQ(blacklisted_expected,
+ ASSERT_EQ(block_listed_expected,
BitmapGlyphsBlockList::ShouldAvoidEmbeddedBitmapsForTypeface(
*font_platform_data.Typeface()));
}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font.h b/chromium/third_party/blink/renderer/platform/fonts/font.h
index 0c41798f0a2..b6dbb6f31e6 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font.h
@@ -237,10 +237,14 @@ class PLATFORM_EXPORT Font {
bool IsFallbackValid() const;
bool ShouldSkipDrawing() const {
- return font_fallback_list_ && font_fallback_list_->ShouldSkipDrawing();
+ if (!font_fallback_list_)
+ return false;
+ return EnsureFontFallbackList()->ShouldSkipDrawing();
}
private:
+ // TODO(xiaochengh): The function not only initializes null FontFallbackList,
+ // but also syncs invalid FontFallbackList. Rename it for better readability.
FontFallbackList* EnsureFontFallbackList() const;
void RevalidateFontFallbackList() const;
void ReleaseFontFallbackListRef() 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 f81b484ef10..8dbcd0109c8 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -76,10 +76,7 @@ const base::Feature kFontCacheNoSizeInKey{"FontCacheNoSizeInKey",
base::FEATURE_DISABLED_BY_DEFAULT};
}
-// Special locale for retrieving the color emoji font based on the proposed
-// changes in UTR #51 for introducing an Emoji script code:
-// https://unicode.org/reports/tr51/#Emoji_Script
-static const char kColorEmojiLocale[] = "und-Zsye";
+const char kColorEmojiLocale[] = "und-Zsye";
SkFontMgr* FontCache::static_font_manager_ = nullptr;
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 7e17f71683a..5941ae84eb5 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_cache.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.h
@@ -110,6 +110,10 @@ typedef HashMap<FallbackListCompositeKey,
FallbackListShaperCache;
typedef std::vector<FontEnumerationEntry> FontEnumerationCache;
+// "und-Zsye", the special locale for retrieving the color emoji font defined
+// in UTS #51: https://unicode.org/reports/tr51/#Emoji_Script
+extern const char kColorEmojiLocale[];
+
class PLATFORM_EXPORT FontCache {
friend class FontCachePurgePreventer;
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 6b7c90a227d..89e0da6a85e 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
};
} // namespace blink
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 0f2c270892c..ac9e8e262ff 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
@@ -4,6 +4,10 @@
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include <unicode/unistr.h>
+#include <string>
+#include <tuple>
+
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
@@ -43,6 +47,57 @@ TEST(FontCache, NoFallbackForPrivateUseArea) {
}
}
+#if defined(OS_LINUX)
+TEST(FontCache, FallbackForEmojis) {
+ FontCache* font_cache = FontCache::GetFontCache();
+ ASSERT_TRUE(font_cache);
+ FontCachePurgePreventer purge_preventer;
+
+ FontDescription font_description;
+ font_description.SetGenericFamily(FontDescription::kStandardFamily);
+
+ static constexpr char kNotoColorEmoji[] = "Noto Color Emoji";
+
+ // We should use structured binding when it becomes available...
+ for (auto info : {
+ std::pair<UChar32, bool>{U'☺', true},
+ {U'👪', true},
+ {U'🤣', false},
+ }) {
+ UChar32 character = info.first;
+ // Set to true if the installed contour fonts support this glyph.
+ bool available_in_contour_font = info.second;
+ std::string character_utf8;
+ icu::UnicodeString(character).toUTF8String(character_utf8);
+
+ {
+ scoped_refptr<SimpleFontData> font_data =
+ font_cache->FallbackFontForCharacter(
+ font_description, character, nullptr,
+ FontFallbackPriority::kEmojiEmoji);
+ EXPECT_EQ(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
+ << "Character " << character_utf8
+ << " doesn't match what we expected for kEmojiEmoji.";
+ }
+ {
+ scoped_refptr<SimpleFontData> font_data =
+ font_cache->FallbackFontForCharacter(
+ font_description, character, nullptr,
+ FontFallbackPriority::kEmojiText);
+ if (available_in_contour_font) {
+ EXPECT_NE(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
+ << "Character " << character_utf8
+ << " doesn't match what we expected for kEmojiText.";
+ } else {
+ EXPECT_EQ(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
+ << "Character " << character_utf8
+ << " doesn't match what we expected for kEmojiText.";
+ }
+ }
+ }
+}
+#endif // defined(OS_LINUX)
+
TEST(FontCache, firstAvailableOrFirst) {
EXPECT_TRUE(FontCache::FirstAvailableOrFirst("").IsEmpty());
EXPECT_TRUE(FontCache::FirstAvailableOrFirst(String()).IsEmpty());
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 b00c9e9b10d..baf18a18a5c 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
@@ -182,4 +182,37 @@ bool FontCustomPlatformData::SupportsFormat(const String& format) {
EqualIgnoringASCIICase(format, "woff2-variations");
}
+bool FontCustomPlatformData::MayBeIconFont() const {
+ if (!may_be_icon_font_computed_) {
+ // We observed that many icon fonts define almost all of their glyphs in the
+ // Unicode Private Use Area, while non-icon fonts rarely use PUA. We use
+ // this as a heuristic to determine if a font is an icon font.
+
+ // We first obtain the list of glyphs mapped from PUA codepoint range:
+ // https://unicode.org/charts/PDF/UE000.pdf
+ const SkUnichar pua_start = 0xE000;
+ const SkUnichar pua_end = 0xF900;
+ Vector<SkUnichar> pua_codepoints(pua_end - pua_start);
+ for (wtf_size_t i = 0; i < pua_codepoints.size(); ++i)
+ pua_codepoints[i] = pua_start + i;
+
+ Vector<SkGlyphID> glyphs(pua_codepoints.size());
+ base_typeface_->unicharsToGlyphs(pua_codepoints.data(),
+ pua_codepoints.size(), glyphs.data());
+
+ // Deduplicate and exclude glyph ID 0 (which means undefined glyph)
+ std::sort(glyphs.begin(), glyphs.end());
+ glyphs.erase(std::unique(glyphs.begin(), glyphs.end()), glyphs.end());
+ if (!glyphs[0])
+ glyphs.EraseAt(0);
+
+ // We use the heuristic that if most of the define glyphs are in PUA, then
+ // the font may be an icon font.
+ wtf_size_t pua_glyph_count = glyphs.size();
+ wtf_size_t total_glyphs = base_typeface_->countGlyphs();
+ may_be_icon_font_ = pua_glyph_count * 2 > total_glyphs;
+ }
+ return may_be_icon_font_;
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
index 7b129c1acfc..4c8f4b8cd2b 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.h
@@ -74,11 +74,16 @@ class PLATFORM_EXPORT FontCustomPlatformData
size_t DataSize() const { return data_size_; }
static bool SupportsFormat(const String&);
+ bool MayBeIconFont() const;
+
private:
FontCustomPlatformData(sk_sp<SkTypeface>, size_t data_size);
sk_sp<SkTypeface> base_typeface_;
size_t data_size_;
+ mutable bool may_be_icon_font_computed_ = false;
+ mutable bool may_be_icon_font_ = false;
+
DISALLOW_COPY_AND_ASSIGN(FontCustomPlatformData);
};
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 1a1a62c3ac7..59bd892b515 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_description.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_description.cc
@@ -314,6 +314,18 @@ void FontDescription::UpdateTypesettingFeatures() {
fields_.typesetting_features_ |= blink::kCaps;
}
+namespace {
+
+// This converts -0.0 to 0.0, so that they have the same hash value. This
+// ensures that equal FontDescription have the same hash value.
+float NormalizeSign(float number) {
+ if (UNLIKELY(number == 0.0))
+ return 0.0;
+ return number;
+}
+
+} // namespace
+
unsigned FontDescription::StyleHashWithoutFamilyList() const {
unsigned hash = 0;
StringHasher string_hasher;
@@ -338,12 +350,12 @@ unsigned FontDescription::StyleHashWithoutFamilyList() const {
}
WTF::AddIntToHash(hash, string_hasher.GetHash());
- WTF::AddFloatToHash(hash, specified_size_);
- WTF::AddFloatToHash(hash, computed_size_);
- WTF::AddFloatToHash(hash, adjusted_size_);
- WTF::AddFloatToHash(hash, size_adjust_);
- WTF::AddFloatToHash(hash, letter_spacing_);
- WTF::AddFloatToHash(hash, word_spacing_);
+ WTF::AddFloatToHash(hash, NormalizeSign(specified_size_));
+ WTF::AddFloatToHash(hash, NormalizeSign(computed_size_));
+ WTF::AddFloatToHash(hash, NormalizeSign(adjusted_size_));
+ WTF::AddFloatToHash(hash, NormalizeSign(size_adjust_));
+ WTF::AddFloatToHash(hash, NormalizeSign(letter_spacing_));
+ WTF::AddFloatToHash(hash, NormalizeSign(word_spacing_));
WTF::AddIntToHash(hash, fields_as_unsigned_.parts[0]);
WTF::AddIntToHash(hash, fields_as_unsigned_.parts[1]);
WTF::AddIntToHash(hash, font_selection_request_.GetHash());
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 b6d814798f0..22026787e49 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
@@ -249,4 +249,18 @@ TEST(FontDescriptionTest, DefaultHashTrait) {
EXPECT_FALSE(map.Contains(description3));
}
+// https://crbug.com/1081017
+TEST(FontDescriptionTest, NegativeZeroEmFontSize) {
+ // 'font-size: -0.0em' sets the following
+ FontDescription description1;
+ description1.SetSpecifiedSize(-0.0);
+
+ FontDescription description2;
+ description2.SetSpecifiedSize(0.0);
+
+ // Equal font descriptions must have equal hash values
+ EXPECT_EQ(description1, description2);
+ EXPECT_EQ(description1.GetHash(), description2.GetHash());
+}
+
} // namespace blink
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 eb076725400..e1778f35eea 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
@@ -33,7 +33,7 @@ bool FontFallbackIterator::AlreadyLoadingRangeForHintChar(UChar32 hint_char) {
}
bool FontFallbackIterator::RangeSetContributesForHint(
- const Vector<UChar32> hint_list,
+ const Vector<UChar32>& hint_list,
const FontDataForRangeSet* segmented_face) {
for (auto* it = hint_list.begin(); it != hint_list.end(); ++it) {
if (segmented_face->Contains(*it)) {
@@ -56,6 +56,9 @@ void FontFallbackIterator::WillUseRange(const AtomicString& family,
scoped_refptr<FontDataForRangeSet> FontFallbackIterator::UniqueOrNext(
scoped_refptr<FontDataForRangeSet> candidate,
const Vector<UChar32>& hint_list) {
+ if (!candidate->HasFontData())
+ return Next(hint_list);
+
SkTypeface* candidate_typeface =
candidate->FontData()->PlatformData().Typeface();
if (!candidate_typeface)
@@ -70,6 +73,11 @@ scoped_refptr<FontDataForRangeSet> FontFallbackIterator::UniqueOrNext(
// depends on the subsetting.
if (candidate->IsEntireRange())
unique_font_data_for_range_sets_returned_.insert(candidate_id);
+
+ // Save first candidate to be returned if all other fonts fail, and we need
+ // it to render the .notdef glyph.
+ if (!first_candidate_)
+ first_candidate_ = candidate;
return candidate;
}
@@ -121,14 +129,19 @@ scoped_refptr<FontDataForRangeSet> FontFallbackIterator::Next(
// resort font that has glyphs for everything, for example the Unicode
// LastResort font, not just Times or Arial.
FontCache* font_cache = FontCache::GetFontCache();
- fallback_stage_ = kOutOfLuck;
+ fallback_stage_ = kFirstCandidateForNotdefGlyph;
scoped_refptr<SimpleFontData> last_resort =
font_cache->GetLastResortFallbackFont(font_description_).get();
- if (!last_resort)
+ return UniqueOrNext(
+ base::AdoptRef(new FontDataForRangeSetFromCache(last_resort)),
+ hint_list);
+ }
+
+ if (fallback_stage_ == kFirstCandidateForNotdefGlyph) {
+ fallback_stage_ = kOutOfLuck;
+ if (!first_candidate_)
FontCache::CrashWithFontInfo(&font_description_);
- // Don't skip the LastResort font in uniqueOrNext() since HarfBuzzShaper
- // needs to use this one to place missing glyph boxes.
- return base::AdoptRef(new FontDataForRangeSetFromCache(last_resort));
+ return first_candidate_;
}
DCHECK(fallback_stage_ == kFontGroupFonts ||
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
index b17424f89d8..3eaa249d4fc 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.h
@@ -46,7 +46,7 @@ class FontFallbackIterator {
scoped_refptr<FontDataForRangeSet> Next(const Vector<UChar32>& hint_list);
private:
- bool RangeSetContributesForHint(const Vector<UChar32> hint_list,
+ bool RangeSetContributesForHint(const Vector<UChar32>& hint_list,
const FontDataForRangeSet*);
bool AlreadyLoadingRangeForHintChar(UChar32 hint_char);
void WillUseRange(const AtomicString& family, const FontDataForRangeSet&);
@@ -70,6 +70,7 @@ class FontFallbackIterator {
kSegmentedFace,
kPreferencesFonts,
kSystemFonts,
+ kFirstCandidateForNotdefGlyph,
kOutOfLuck
};
@@ -77,10 +78,13 @@ class FontFallbackIterator {
HashSet<UChar32> previously_asked_for_hint_;
// FontFallbackIterator is meant for single use by HarfBuzzShaper,
// traversing through the fonts for shaping only once. We must not return
- // duplicate FontDataForRangeSet objects from the next() iteration functions
+ // duplicate FontDataForRangeSet objects from the Next() iteration function
// as returning a duplicate value causes a shaping run that won't return any
- // results.
+ // results. The exception is that if all fonts fail, we return the first
+ // candidate to be used for rendering the .notdef glyph, and set HasNext() to
+ // false.
HashSet<uint32_t> unique_font_data_for_range_sets_returned_;
+ scoped_refptr<FontDataForRangeSet> first_candidate_ = nullptr;
Vector<scoped_refptr<FontDataForRangeSet>> tracked_loading_range_sets_;
FontFallbackPriority font_fallback_priority_;
};
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 cf987ead88a..a8659c7e4f4 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
@@ -97,7 +97,10 @@ bool FontFallbackList::LoadingCustomFonts() const {
}
bool FontFallbackList::ShouldSkipDrawing() const {
- DCHECK(IsValid());
+ // The DCHECK hit will be fixed by the runtime enabled feature below, so we
+ // don't fix it in the legacy code paths.
+ DCHECK(IsValid() || !RuntimeEnabledFeatures::
+ CSSReducedFontLoadingLayoutInvalidationsEnabled());
if (!has_loading_fallback_)
return false;
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.cc
index 52a9b618644..518f2466d31 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.cc
@@ -8,7 +8,7 @@
namespace blink {
-void FontFallbackMap::Trace(Visitor* visitor) {
+void FontFallbackMap::Trace(Visitor* visitor) const {
visitor->Trace(font_selector_);
FontCacheClient::Trace(visitor);
FontSelectorClient::Trace(visitor);
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.h
index bff55148012..bb2152f7cf9 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_map.h
@@ -30,7 +30,7 @@ class PLATFORM_EXPORT FontFallbackMap : public FontCacheClient,
scoped_refptr<FontFallbackList> Get(const FontDescription& font_description);
void Remove(const FontDescription& font_description);
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
private:
// FontSelectorClient
diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc b/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc
index 4793438d71b..ea54e9d8c6d 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector.cc
@@ -49,7 +49,7 @@ AtomicString FontSelector::FamilyNameFromSettings(
return g_empty_atom;
}
-void FontSelector::Trace(Visitor* visitor) {
+void FontSelector::Trace(Visitor* visitor) const {
visitor->Trace(font_fallback_map_);
FontCacheClient::Trace(visitor);
}
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 768d5d8fc86..c3f1c84e85b 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/font_selector.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector.h
@@ -97,7 +97,7 @@ class PLATFORM_EXPORT FontSelector : public FontCacheClient {
FontFallbackMap& GetFontFallbackMap();
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
protected:
static AtomicString FamilyNameFromSettings(
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 134c5f36f1c..2a0ac9286c3 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
@@ -18,7 +18,7 @@ class FontSelectorClient : public GarbageCollectedMixin {
virtual void FontsNeedUpdate(FontSelector*, FontInvalidationReason) = 0;
- void Trace(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc b/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
index 39edc68d0bc..132f146e332 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/linux/font_cache_linux.cc
@@ -104,7 +104,10 @@ scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
gfx::FallbackFontData fallback_font;
if (!FontCache::GetFontForCharacter(
- c, font_description.LocaleOrDefault().Ascii().c_str(),
+ c,
+ fallback_priority == FontFallbackPriority::kEmojiEmoji
+ ? kColorEmojiLocale
+ : font_description.LocaleOrDefault().Ascii().c_str(),
&fallback_font))
return nullptr;
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
index 50491a11536..542eeef30f7 100644
--- 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
@@ -13,7 +13,7 @@
namespace {
const UChar32 kLeftBraceCodePoint = '{';
const UChar32 kOverBraceCodePoint = 0x23DE;
-const UChar32 kArabicMathOperatorHahWithDalCodePoint = 0x1EEF1;
+const UChar32 kRightwardsFrontTiltedShadowedWhiteArrowCodePoint = 0x1F8AB;
const UChar32 kNAryWhiteVerticalBarCodePoint = 0x2AFF;
} // namespace
@@ -280,8 +280,7 @@ TEST_F(OpenTypeMathSupportTest, MathVariantsWithoutTable) {
}
}
-// Broken on all platforms by updated to 'operators.woff'. crbug.com/1082250
-TEST_F(OpenTypeMathSupportTest, DISABLED_MathVariantsWithTable) {
+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
@@ -301,7 +300,7 @@ TEST_F(OpenTypeMathSupportTest, DISABLED_MathVariantsWithTable) {
// TODO(https://crbug.com/1057596): Find a better way to access these glyph
// indices.
auto v0 = math.PrimaryFont()->GlyphForCharacter(
- kArabicMathOperatorHahWithDalCodePoint) +
+ kRightwardsFrontTiltedShadowedWhiteArrowCodePoint) +
1;
auto h0 = v0 + 1;
auto v1 = h0 + 1;
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
index c919c686aa6..24f0d3c4c40 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
@@ -319,7 +319,7 @@ void HarfBuzzShaper::CommitGlyphs(RangeData* range_data,
const SimpleFontData* current_font,
UScriptCode current_run_script,
CanvasRotationInVertical canvas_rotation,
- bool is_last_resort,
+ bool is_last_font,
const BufferSlice& slice,
ShapeResult* shape_result) const {
hb_direction_t direction = range_data->HarfBuzzDirection(canvas_rotation);
@@ -346,7 +346,7 @@ void HarfBuzzShaper::CommitGlyphs(RangeData* range_data,
current_slice->num_glyphs - num_glyphs_inserted};
current_slice = &next_slice;
}
- if (is_last_resort)
+ if (is_last_font)
range_data->font->ReportNotDefGlyph();
}
@@ -357,7 +357,7 @@ void HarfBuzzShaper::ExtractShapeResults(
const SimpleFontData* current_font,
UScriptCode current_run_script,
CanvasRotationInVertical canvas_rotation,
- bool is_last_resort,
+ bool is_last_font,
ShapeResult* shape_result) const {
enum ClusterResult { kShaped, kNotDef, kUnknown };
ClusterResult current_cluster_result = kUnknown;
@@ -397,7 +397,7 @@ void HarfBuzzShaper::ExtractShapeResults(
// If the most recent cluster is shaped and there is a state change,
// it means the previous ones were unshaped, so we queue them, unless
// we're using the last resort font.
- if (current_cluster_result == kShaped && !is_last_resort) {
+ if (current_cluster_result == kShaped && !is_last_font) {
QueueCharacters(range_data, current_font, font_cycle_queued, slice);
} else {
// If the most recent cluster is unshaped and there is a state
@@ -405,7 +405,7 @@ void HarfBuzzShaper::ExtractShapeResults(
// the glyphs. We also commit when we've reached the last resort
// font.
CommitGlyphs(range_data, current_font, current_run_script,
- canvas_rotation, is_last_resort, slice, shape_result);
+ canvas_rotation, is_last_font, slice, shape_result);
}
last_change_glyph_index = previous_cluster_start_glyph_index;
}
@@ -427,7 +427,7 @@ void HarfBuzzShaper::ExtractShapeResults(
// End of the run.
if (current_cluster_result != previous_cluster_result &&
- previous_cluster_result != kUnknown && !is_last_resort) {
+ previous_cluster_result != kUnknown && !is_last_font) {
// The last cluster in the run still had shaping status different from
// the cluster(s) before it, we need to submit one shaped and one
// unshaped segment.
@@ -440,13 +440,13 @@ void HarfBuzzShaper::ExtractShapeResults(
ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
previous_cluster_start_glyph_index, num_glyphs);
CommitGlyphs(range_data, current_font, current_run_script,
- canvas_rotation, is_last_resort, slice, shape_result);
+ canvas_rotation, is_last_font, slice, shape_result);
} else {
BufferSlice slice = ComputeSlice(
range_data, current_queue_item, glyph_info, num_glyphs,
last_change_glyph_index, previous_cluster_start_glyph_index);
CommitGlyphs(range_data, current_font, current_run_script,
- canvas_rotation, is_last_resort, slice, shape_result);
+ canvas_rotation, is_last_font, slice, shape_result);
slice =
ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
previous_cluster_start_glyph_index, num_glyphs);
@@ -458,11 +458,11 @@ void HarfBuzzShaper::ExtractShapeResults(
BufferSlice slice =
ComputeSlice(range_data, current_queue_item, glyph_info, num_glyphs,
last_change_glyph_index, num_glyphs);
- if (current_cluster_result == kNotDef && !is_last_resort) {
+ if (current_cluster_result == kNotDef && !is_last_font) {
QueueCharacters(range_data, current_font, font_cycle_queued, slice);
} else {
CommitGlyphs(range_data, current_font, current_run_script,
- canvas_rotation, is_last_resort, slice, shape_result);
+ canvas_rotation, is_last_font, slice, shape_result);
}
}
}
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
index 817cd33d0f3..d7802f0c35f 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
@@ -105,7 +105,7 @@ class PLATFORM_EXPORT HarfBuzzShaper final {
const SimpleFontData*,
UScriptCode,
CanvasRotationInVertical,
- bool is_last_resort,
+ bool is_last_font,
ShapeResult*) const;
bool CollectFallbackHintChars(const Deque<ReshapeQueueItem>&,
@@ -116,7 +116,7 @@ class PLATFORM_EXPORT HarfBuzzShaper final {
const SimpleFontData* current_font,
UScriptCode current_run_script,
CanvasRotationInVertical,
- bool is_last_resort,
+ bool is_last_font,
const BufferSlice&,
ShapeResult*) const;
diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
index 9dcc0d50cab..a3cbc71f16e 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
@@ -38,6 +38,9 @@ class PLATFORM_EXPORT ShapeResultSpacing final {
float WordSpacing() const { return has_spacing_ ? word_spacing_ : .0f; }
bool HasSpacing() const { return has_spacing_; }
bool HasExpansion() const { return expansion_opportunity_count_; }
+ unsigned ExpansionOppotunityCount() const {
+ return expansion_opportunity_count_;
+ }
// Set letter-spacing and word-spacing.
bool SetSpacing(const FontDescription&);
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
index c04d774ce98..3d9827ddb56 100644
--- 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
@@ -18,7 +18,7 @@ namespace {
const UChar32 kLeftBraceCodePoint = '{';
const UChar32 kOverBraceCodePoint = 0x23DE;
-const UChar32 kArabicMathOperatorHahWithDalCodePoint = 0x1EEF1;
+const UChar32 kRightwardsFrontTiltedShadowedWhiteArrowCodePoint = 0x1F8AB;
const UChar32 kNAryWhiteVerticalBarCodePoint = 0x2AFF;
float kSizeError = .1;
@@ -51,8 +51,7 @@ class StretchyOperatorShaperTest : public testing::Test {
// See createStretchy() in
// third_party/blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py
-// Broken on all platforms by updated to 'operators.woff'. crbug.com/1082250
-TEST_F(StretchyOperatorShaperTest, DISABLED_GlyphVariants) {
+TEST_F(StretchyOperatorShaperTest, GlyphVariants) {
Font math = CreateMathFont("operators.woff");
StretchyOperatorShaper vertical_shaper(
@@ -67,7 +66,7 @@ TEST_F(StretchyOperatorShaperTest, DISABLED_GlyphVariants) {
// TODO(https://crbug.com/1057596): Find a better way to access these glyph
// indices.
auto v0 = math.PrimaryFont()->GlyphForCharacter(
- kArabicMathOperatorHahWithDalCodePoint) +
+ kRightwardsFrontTiltedShadowedWhiteArrowCodePoint) +
1;
auto h0 = v0 + 1;
auto v1 = h0 + 1;
diff --git a/chromium/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h b/chromium/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
index a9232d08c08..1e8b1c225f7 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
+++ b/chromium/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
@@ -7,7 +7,7 @@
#include <unicode/uchar.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
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 52427643b20..ee80a855f52 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
@@ -154,7 +154,6 @@ sk_sp<SkTypeface> FindUniqueFontNameFromSideloadedFonts(
return return_typeface;
}
-static const char kColorEmojiLocale[] = "und-Zsye";
static const char kChineseSimplified[] = "zh-Hant";
// For Windows out-of-process fallback calls, there is a limiation: only one
diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
index 591c693d2e0..6f90e69c6f2 100644
--- a/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
+++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
@@ -201,8 +201,8 @@ void InitializeScriptFontMap(ScriptToFontMap& script_font_map) {
static const UChar* const kRunicFonts[] = {L"Segoe UI Historic",
L"Segoe UI Symbol", 0};
static const UChar* const kShavianFonts[] = {L"Segoe UI Historic", 0};
- static const UChar* const kSimplifiedHanFonts[] = {L"simsun",
- L"Microsoft YaHei", 0};
+ static const UChar* const kSimplifiedHanFonts[] = {L"Microsoft YaHei",
+ L"simsun", 0};
static const UChar* const kSinhalaFonts[] = {
L"Iskoola Pota", L"AksharUnicode", L"Nirmala UI", 0};
static const UChar* const kSoraSompengFonts[] = {L"Nirmala UI", 0};
@@ -218,8 +218,8 @@ void InitializeScriptFontMap(ScriptToFontMap& script_font_map) {
static const UChar* const kTibetanFonts[] = {
L"Microsoft Himalaya", L"Jomolhari", L"Tibetan Machine Uni", 0};
static const UChar* const kTifinaghFonts[] = {L"Ebrima", 0};
- static const UChar* const kTraditionalHanFonts[] = {L"pmingliu",
- L"Microsoft JhengHei", 0};
+ static const UChar* const kTraditionalHanFonts[] = {L"Microsoft JhengHei",
+ L"pmingliu", 0};
static const UChar* const kVaiFonts[] = {L"Ebrima", 0};
static const UChar* const kYiFonts[] = {L"Microsoft Yi Baiti", L"Nuosu SIL",
L"Code2000", 0};
diff --git a/chromium/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc b/chromium/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
index f9e1935515c..178741fcb80 100644
--- a/chromium/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
+++ b/chromium/third_party/blink/renderer/platform/geometry/calculation_expression_node.cc
@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/platform/geometry/calculation_expression_node.h"
+#include "base/notreached.h"
+
namespace blink {
// ------ CalculationExpressionLeafNode ------
diff --git a/chromium/third_party/blink/renderer/platform/geometry/length.h b/chromium/third_party/blink/renderer/platform/geometry/length.h
index a520db00925..b1af363e246 100644
--- a/chromium/third_party/blink/renderer/platform/geometry/length.h
+++ b/chromium/third_party/blink/renderer/platform/geometry/length.h
@@ -25,6 +25,7 @@
#include <cstring>
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -56,6 +57,7 @@ class PLATFORM_EXPORT Length {
kFixed,
kMinContent,
kMaxContent,
+ kMinIntrinsic,
kFillAvailable,
kFitContent,
kCalculated,
@@ -144,6 +146,7 @@ class PLATFORM_EXPORT Length {
static Length FillAvailable() { return Length(kFillAvailable); }
static Length MinContent() { return Length(kMinContent); }
static Length MaxContent() { return Length(kMaxContent); }
+ static Length MinIntrinsic() { return Length(kMinIntrinsic); }
static Length ExtendToZoom() { return Length(kExtendToZoom); }
static Length DeviceWidth() { return Length(kDeviceWidth); }
static Length DeviceHeight() { return Length(kDeviceHeight); }
@@ -230,7 +233,8 @@ class PLATFORM_EXPORT Length {
bool IsIntrinsicOrAuto() const { return GetType() == kAuto || IsIntrinsic(); }
bool IsIntrinsic() const {
return GetType() == kMinContent || GetType() == kMaxContent ||
- GetType() == kFillAvailable || GetType() == kFitContent;
+ GetType() == kMinIntrinsic || GetType() == kFillAvailable ||
+ GetType() == kFitContent;
}
bool IsSpecified() const {
return GetType() == kFixed || GetType() == kPercent ||
@@ -241,6 +245,7 @@ class PLATFORM_EXPORT Length {
bool IsCalculatedEqual(const Length&) const;
bool IsMinContent() const { return GetType() == kMinContent; }
bool IsMaxContent() const { return GetType() == kMaxContent; }
+ bool IsMinIntrinsic() const { return GetType() == kMinIntrinsic; }
bool IsFillAvailable() const { return GetType() == kFillAvailable; }
bool IsFitContent() const { return GetType() == kFitContent; }
bool IsPercent() const { return GetType() == kPercent; }
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 dd5a6fed930..f07da329692 100644
--- a/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc
+++ b/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc
@@ -48,6 +48,7 @@ float FloatValueForLength(const Length& length, float maximum_value) {
return length.NonNanCalculatedValue(LayoutUnit(maximum_value));
case Length::kMinContent:
case Length::kMaxContent:
+ case Length::kMinIntrinsic:
case Length::kFitContent:
case Length::kExtendToZoom:
case Length::kDeviceWidth:
@@ -76,6 +77,7 @@ LayoutUnit MinimumValueForLengthInternal(const Length& length,
case Length::kFixed:
case Length::kMinContent:
case Length::kMaxContent:
+ case Length::kMinIntrinsic:
case Length::kFitContent:
case Length::kExtendToZoom:
case Length::kDeviceWidth:
@@ -99,6 +101,7 @@ LayoutUnit ValueForLength(const Length& length, LayoutUnit maximum_value) {
return maximum_value;
case Length::kMinContent:
case Length::kMaxContent:
+ case Length::kMinIntrinsic:
case Length::kFitContent:
case Length::kExtendToZoom:
case Length::kDeviceWidth:
diff --git a/chromium/third_party/blink/renderer/platform/graphics/DEPS b/chromium/third_party/blink/renderer/platform/graphics/DEPS
index bdfff8ba7ff..08cca2d6fac 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/DEPS
+++ b/chromium/third_party/blink/renderer/platform/graphics/DEPS
@@ -9,7 +9,6 @@ include_rules = [
"+base/threading/sequenced_task_runner_handle.h",
"+base/threading/thread_restrictions.h",
"+base/barrier_closure.h",
- "+base/callback_helpers.h",
"+cc",
"+components/paint_preview/common",
"+components/viz/client",
@@ -31,6 +30,7 @@ include_rules = [
"+media/base/media_switches.h",
"+media/base/video_frame.h",
"+media/base/video_types.h",
+ "+media/media_buildflags.h",
"+media/renderers/video_resource_updater.h",
"+services/viz/public/mojom",
"+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
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 7af58b82644..a68e47e32c6 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
@@ -325,9 +325,7 @@ void AcceleratedStaticBitmapImage::InitializeTextureBacking(
sk_image_info_.alphaType(), sk_image_info_.refColorSpace(),
&ReleaseTexture, release_ctx);
- if (!sk_image) {
- ReleaseTexture(release_ctx);
- } else {
+ if (sk_image) {
skia_context_provider_wrapper_ = std::move(context_provider_wrapper);
texture_backing_ = sk_sp<MailboxTextureBacking>(
new MailboxTextureBacking(std::move(sk_image)));
@@ -409,8 +407,8 @@ AcceleratedStaticBitmapImage::ConvertToColorSpace(
->UsageForMailbox(mailbox_);
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
Size(), ContextProviderWrapper(), kLow_SkFilterQuality,
- CanvasColorParams(image_info), IsOriginTopLeft(),
- CanvasResourceProvider::RasterMode::kGPU, usage_flags);
+ CanvasColorParams(image_info), IsOriginTopLeft(), RasterMode::kGPU,
+ usage_flags);
if (!provider) {
return nullptr;
}
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 deaa02ab7c3..46b1a96598a 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
} // namespace blink
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 e912dbf0200..804b84f70e6 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
@@ -98,7 +98,7 @@ void BeginFrameProvider::RequestBeginFrame() {
void BeginFrameProvider::OnBeginFrame(
const viz::BeginFrameArgs& args,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) {
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>&) {
TRACE_EVENT_WITH_FLOW0("blink", "BeginFrameProvider::OnBeginFrame",
TRACE_ID_GLOBAL(args.trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
@@ -124,7 +124,7 @@ void BeginFrameProvider::FinishBeginFrame(const viz::BeginFrameArgs& args) {
compositor_frame_sink_->DidNotProduceFrame(viz::BeginFrameAck(args, false));
}
-void BeginFrameProvider::Trace(Visitor* visitor) {
+void BeginFrameProvider::Trace(Visitor* visitor) const {
visitor->Trace(cfs_receiver_);
visitor->Trace(efs_receiver_);
visitor->Trace(compositor_frame_sink_);
diff --git a/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.h b/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
index 012d1a8e747..7b6834fca84 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.h
@@ -52,7 +52,7 @@ class PLATFORM_EXPORT BeginFrameProvider
}
void OnBeginFrame(
const viz::BeginFrameArgs&,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) final;
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>&) final;
void OnBeginFramePausedChanged(bool paused) final {}
void ReclaimResources(
const WTF::Vector<viz::ReturnedResource>& resources) final {
@@ -69,7 +69,7 @@ class PLATFORM_EXPORT BeginFrameProvider
bool IsValidFrameProvider();
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
~BeginFrameProvider() override = default;
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 882aa8cf139..206b4c0df60 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -34,7 +34,6 @@
#include "base/metrics/histogram_macros.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
#include "third_party/blink/renderer/platform/graphics/image_observer.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
@@ -50,12 +49,6 @@
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
-namespace {
-
-const int kMinImageSizeForClassification1D = 24;
-const int kMaxImageSizeForClassification1D = 100;
-
-} // namespace
int GetRepetitionCountWithPolicyOverride(int actual_count,
ImageAnimationPolicy policy) {
@@ -192,7 +185,7 @@ Image::SizeAvailability BitmapImage::SetData(scoped_refptr<SharedBuffer> data,
return DataChanged(all_data_received);
}
- bool has_enough_data = ImageDecoder::HasSufficientDataToSniffImageType(*data);
+ bool has_enough_data = ImageDecoder::HasSufficientDataToSniffMimeType(*data);
decoder_ = DeferredImageDecoder::Create(std::move(data), all_data_received,
ImageDecoder::kAlphaPremultiplied,
ColorBehavior::Tag());
@@ -446,21 +439,4 @@ void BitmapImage::SetAnimationPolicy(ImageAnimationPolicy policy) {
ResetAnimation();
}
-DarkModeClassification BitmapImage::CheckTypeSpecificConditionsForDarkMode(
- const FloatRect& dest_rect,
- DarkModeImageClassifier* classifier) {
- if (dest_rect.Width() < kMinImageSizeForClassification1D ||
- dest_rect.Height() < kMinImageSizeForClassification1D)
- return DarkModeClassification::kApplyFilter;
-
- if (dest_rect.Width() > kMaxImageSizeForClassification1D ||
- dest_rect.Height() > kMaxImageSizeForClassification1D) {
- return DarkModeClassification::kDoNotApplyFilter;
- }
-
- classifier->SetImageType(DarkModeImageClassifier::ImageType::kBitmap);
-
- return DarkModeClassification::kNotClassified;
-}
-
} // namespace blink
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 b36599efbd7..71b5aa54b34 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h
@@ -102,10 +102,6 @@ class PLATFORM_EXPORT BitmapImage final : public Image {
decoder_ = std::move(decoder);
}
- DarkModeClassification CheckTypeSpecificConditionsForDarkMode(
- const FloatRect& dest_rect,
- DarkModeImageClassifier* classifier) override;
-
protected:
bool IsSizeAvailable() override;
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
index d970b9e6be2..b1479e523ba 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.cc
@@ -6,6 +6,7 @@
#include "base/metrics/histogram_base.h"
#include "base/numerics/safe_conversions.h"
+#include "media/media_buildflags.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
@@ -28,7 +29,11 @@ void BitmapImageMetrics::CountDecodedImageType(const String& type) {
? kImageICO
: type == "bmp"
? kImageBMP
- : DecodedImageType::kImageUnknown;
+#if BUILDFLAG(ENABLE_AV1_DECODER)
+ : type == "avif"
+ ? kImageAVIF
+#endif
+ : DecodedImageType::kImageUnknown;
DEFINE_THREAD_SAFE_STATIC_LOCAL(
EnumerationHistogram, decoded_image_type_histogram,
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
index 84cbfdceb2b..361cb635d89 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h
@@ -29,7 +29,8 @@ class PLATFORM_EXPORT BitmapImageMetrics {
kImageWebP = 4,
kImageICO = 5,
kImageBMP = 6,
- kDecodedImageTypeEnumEnd = kImageBMP + 1
+ kImageAVIF = 7,
+ kDecodedImageTypeEnumEnd = kImageAVIF + 1
};
// Values synced with 'Gamma' in src/tools/metrics/histograms/enums.xml. These
@@ -70,6 +71,7 @@ class PLATFORM_EXPORT BitmapImageMetrics {
kMaxValue = kYCbCrOther,
};
+ // |type| is the return value of ImageDecoder::FilenameExtension().
static void CountDecodedImageType(const String& type);
static void CountImageOrientation(const ImageOrientationEnum);
static void CountImageDensityCorrection(bool densityCorrectionPresent);
diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index 12b46c08194..6c72be37875 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -31,11 +31,14 @@
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
#include "base/bind.h"
+#include "base/feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/skia_paint_canvas.h"
#include "cc/tiles/mipmap_util.h"
+#include "media/media_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
@@ -828,6 +831,12 @@ using DecodedImageTypeHistogramTest =
BitmapHistogramTest<BitmapImageMetrics::DecodedImageType>;
TEST_P(DecodedImageTypeHistogramTest, ImageType) {
+#if BUILDFLAG(ENABLE_AV1_DECODER)
+ if (GetParam().type == BitmapImageMetrics::kImageAVIF &&
+ !base::FeatureList::IsEnabled(features::kAVIF)) {
+ return;
+ }
+#endif
RunTest("Blink.DecodedImageType");
}
@@ -839,7 +848,11 @@ const DecodedImageTypeHistogramTest::ParamType
{"animated-10color.gif", BitmapImageMetrics::kImageGIF},
{"webp-color-profile-lossy.webp", BitmapImageMetrics::kImageWebP},
{"wrong-frame-dimensions.ico", BitmapImageMetrics::kImageICO},
- {"lenna.bmp", BitmapImageMetrics::kImageBMP}};
+ {"lenna.bmp", BitmapImageMetrics::kImageBMP},
+#if BUILDFLAG(ENABLE_AV1_DECODER)
+ {"red-full-ranged-8bpc.avif", BitmapImageMetrics::kImageAVIF},
+#endif
+};
INSTANTIATE_TEST_SUITE_P(
DecodedImageTypeHistogramTest,
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 5292bd370da..94815c0fe44 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
@@ -56,14 +56,13 @@
namespace blink {
Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size,
- AccelerationMode acceleration_mode,
+ RasterMode raster_mode,
const CanvasColorParams& color_params)
: logger_(std::make_unique<Logger>()),
have_recorded_draw_commands_(false),
is_hidden_(false),
is_being_displayed_(false),
- software_rendering_while_hidden_(false),
- acceleration_mode_(acceleration_mode),
+ raster_mode_(raster_mode),
color_params_(color_params),
size_(size),
snapshot_state_(kInitialSnapshotState),
@@ -85,7 +84,7 @@ Canvas2DLayerBridge::~Canvas2DLayerBridge() {
if (!layer_)
return;
- if (acceleration_mode_ != kDisableAcceleration) {
+ if (raster_mode_ == RasterMode::kGPU) {
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,46 +108,30 @@ void Canvas2DLayerBridge::ResetResourceProvider() {
resource_host_->ReplaceResourceProvider(nullptr);
}
-bool Canvas2DLayerBridge::ShouldAccelerate(AccelerationHint hint) const {
- bool accelerate;
- if (software_rendering_while_hidden_) {
- accelerate = false;
- } else if (acceleration_mode_ == kForceAccelerationForTesting) {
- accelerate = true;
- } else if (acceleration_mode_ == kDisableAcceleration) {
- accelerate = false;
- } else if (acceleration_mode_ == kEnableAcceleration) {
- accelerate = true;
- } else {
- accelerate = hint == kPreferAcceleration ||
- hint == kPreferAccelerationAfterVisibilityChange;
- }
+bool Canvas2DLayerBridge::ShouldAccelerate() const {
+ bool use_gpu = raster_mode_ == RasterMode::kGPU;
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
SharedGpuContext::ContextProviderWrapper();
- if (accelerate &&
+ if (use_gpu &&
(!context_provider_wrapper ||
context_provider_wrapper->ContextProvider()->IsContextLost())) {
- accelerate = false;
+ use_gpu = false;
}
- return accelerate;
+ return use_gpu;
}
bool Canvas2DLayerBridge::IsAccelerated() const {
- if (acceleration_mode_ == kDisableAcceleration)
+ if (raster_mode_ == RasterMode::kCPU)
return false;
if (IsHibernating())
return false;
- if (software_rendering_while_hidden_)
- return false;
if (resource_host_ && resource_host_->ResourceProvider())
return resource_host_->ResourceProvider()->IsAccelerated();
- // Whether or not to accelerate is not yet resolved. Determine whether
- // immediate presentation of the canvas would result in the canvas being
- // accelerated. Presentation is assumed to be a 'PreferAcceleration'
- // operation.
- return ShouldAccelerate(kPreferAcceleration);
+ // Whether or not to accelerate is not yet resolved, the canvas cannot be
+ // accelerated if the gpu context is lost.
+ return ShouldAccelerate();
}
static void HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,
@@ -236,8 +219,7 @@ CanvasResourceProvider* Canvas2DLayerBridge::ResourceProvider() const {
return resource_host_ ? resource_host_->ResourceProvider() : nullptr;
}
-CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider(
- AccelerationHint hint) {
+CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider() {
DCHECK(resource_host_);
CanvasResourceProvider* resource_provider = ResourceProvider();
@@ -261,19 +243,22 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider(
return resource_provider;
}
- if (layer_ && !IsHibernating() && hint == kPreferAcceleration &&
- acceleration_mode_ != kDisableAcceleration) {
- return nullptr; // re-creation will happen through restore()
- }
+ // Restore() is tried at most four times in two seconds to recreate the
+ // ResourceProvider before the final attempt, in which a new
+ // Canvas2DLayerBridge is created along with its resource provider.
+
+ bool want_acceleration = ShouldAccelerate();
+ RasterModeHint adjusted_hint = want_acceleration ? RasterModeHint::kPreferGPU
+ : RasterModeHint::kPreferCPU;
- bool want_acceleration = ShouldAccelerate(hint);
- if (CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU && IsHidden() &&
- want_acceleration) {
- want_acceleration = false;
- software_rendering_while_hidden_ = true;
+ // Re-creation will happen through Restore().
+ // If the Canvas2DLayerBridge has just been created, possibly due to failed
+ // attempts of Restore(), the layer would not exist, therefore, it will not
+ // fall through this clause to try Restore() again
+ if (layer_ && !IsHibernating() &&
+ adjusted_hint == RasterModeHint::kPreferGPU) {
+ return nullptr;
}
- AccelerationHint adjusted_hint =
- want_acceleration ? kPreferAcceleration : kPreferNoAcceleration;
// We call GetOrCreateCanvasResourceProviderImpl directly here to prevent a
// circular callstack from HTMLCanvasElement.
@@ -331,10 +316,9 @@ cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() {
return ResourceProvider()->Canvas();
}
-void Canvas2DLayerBridge::UpdateFilterQuality() {
- SkFilterQuality filter_quality = resource_host_->FilterQuality();
- if (GetOrCreateResourceProvider())
- ResourceProvider()->SetFilterQuality(filter_quality);
+void Canvas2DLayerBridge::SetFilterQuality(SkFilterQuality filter_quality) {
+ if (CanvasResourceProvider* resource_provider = ResourceProvider())
+ resource_provider->SetFilterQuality(filter_quality);
if (layer_)
layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
}
@@ -363,28 +347,6 @@ void Canvas2DLayerBridge::SetIsInHiddenPage(bool hidden) {
WTF::Bind(&HibernateWrapper, weak_ptr_factory_.GetWeakPtr()));
}
}
- if (!IsHidden() && software_rendering_while_hidden_) {
- FlushRecording();
- PaintFlags copy_paint;
- copy_paint.setBlendMode(SkBlendMode::kSrc);
-
- std::unique_ptr<CanvasResourceProvider> old_resource_provider =
- resource_host_->ReplaceResourceProvider(nullptr);
-
- software_rendering_while_hidden_ = false;
- GetOrCreateResourceProvider(kPreferAccelerationAfterVisibilityChange);
-
- if (ResourceProvider()) {
- if (old_resource_provider) {
- cc::PaintImage snapshot =
- old_resource_provider->Snapshot()->PaintImageForCurrentFrame();
- ResourceProvider()->Canvas()->drawImage(snapshot, 0, 0, &copy_paint);
- }
- } else {
- // New resource provider could not be created. Stay with old one.
- resource_host_->ReplaceResourceProvider(std::move(old_resource_provider));
- }
- }
if (!IsHidden() && IsHibernating())
GetOrCreateResourceProvider(); // Rude awakening
}
@@ -422,12 +384,14 @@ bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info,
if (!GetOrCreateResourceProvider())
return false;
}
-
- last_record_tainted_by_write_pixels_ = true;
have_recorded_draw_commands_ = false;
- ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y);
- return true;
+ bool wrote_pixels =
+ ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y);
+ if (wrote_pixels)
+ last_record_tainted_by_write_pixels_ = true;
+
+ return wrote_pixels;
}
void Canvas2DLayerBridge::SkipQueuedDrawCommands() {
@@ -591,7 +555,7 @@ bool Canvas2DLayerBridge::IsValid() {
bool Canvas2DLayerBridge::CheckResourceProviderValid() {
if (IsHibernating())
return true;
- if (!layer_ || acceleration_mode_ == kDisableAcceleration)
+ if (!layer_ || raster_mode_ == RasterMode::kCPU)
return true;
if (context_lost_)
return false;
@@ -620,7 +584,7 @@ bool Canvas2DLayerBridge::Restore() {
if (!context_provider_wrapper->ContextProvider()->IsContextLost()) {
CanvasResourceProvider* resource_provider =
resource_host_->GetOrCreateCanvasResourceProviderImpl(
- kPreferAcceleration);
+ RasterModeHint::kPreferGPU);
// The current paradigm does not support switching from accelerated to
// non-accelerated, which would be tricky due to changes to the layer tree,
@@ -651,7 +615,7 @@ bool Canvas2DLayerBridge::PrepareTransferableResource(
rate_limiter_->Reset();
// If hibernating but not hidden, we want to wake up from hibernation.
- if ((IsHibernating() || software_rendering_while_hidden_) && IsHidden())
+ if (IsHibernating() && IsHidden())
return false;
if (!IsValid())
@@ -687,7 +651,7 @@ bool Canvas2DLayerBridge::PrepareTransferableResource(
cc::Layer* Canvas2DLayerBridge::Layer() {
// Trigger lazy layer creation
- GetOrCreateResourceProvider(kPreferAcceleration);
+ GetOrCreateResourceProvider();
return layer_.get();
}
@@ -702,7 +666,7 @@ void Canvas2DLayerBridge::FinalizeFrame() {
// Make sure surface is ready for painting: fix the rendering mode now
// because it will be too late during the paint invalidation phase.
- if (!GetOrCreateResourceProvider(kPreferAcceleration))
+ if (!GetOrCreateResourceProvider())
return;
FlushRecording();
@@ -724,12 +688,11 @@ void Canvas2DLayerBridge::FinalizeFrame() {
}
void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
- if (layer_ && acceleration_mode_ != kDisableAcceleration)
+ if (layer_ && raster_mode_ == RasterMode::kGPU)
layer_->SetNeedsDisplayRect(EnclosingIntRect(dirty_rect));
}
-scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot(
- AccelerationHint hint) {
+scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot() {
if (snapshot_state_ == kInitialSnapshotState)
snapshot_state_ = kDidAcquireSnapshot;
if (IsHibernating())
@@ -739,10 +702,10 @@ scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot(
// GetOrCreateResourceProvider needs to be called before FlushRecording, to
// make sure "hint" is properly taken into account, as well as after
// FlushRecording, in case the playback crashed the GPU context.
- if (!GetOrCreateResourceProvider(hint))
+ if (!GetOrCreateResourceProvider())
return nullptr;
FlushRecording();
- if (!GetOrCreateResourceProvider(hint))
+ if (!GetOrCreateResourceProvider())
return nullptr;
return ResourceProvider()->Snapshot();
}
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 8daad03dd61..8a6a026c0a1 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
@@ -73,20 +73,9 @@ class StaticBitmapImage;
#define CANVAS2D_HIBERNATION_ENABLED 1
#endif
-// TODO: Fix background rendering and remove this workaround. crbug.com/600386
-#define CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU 0
-
class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
public:
- enum AccelerationMode {
- kDisableAcceleration,
- kEnableAcceleration,
- kForceAccelerationForTesting,
- };
-
- Canvas2DLayerBridge(const IntSize&,
- AccelerationMode,
- const CanvasColorParams&);
+ Canvas2DLayerBridge(const IntSize&, RasterMode, const CanvasColorParams&);
~Canvas2DLayerBridge() override;
@@ -100,11 +89,11 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
void FinalizeFrame();
void SetIsInHiddenPage(bool);
void SetIsBeingDisplayed(bool);
+ void SetFilterQuality(SkFilterQuality filter_quality);
void DidDraw(const FloatRect&);
void DoPaintInvalidation(const FloatRect& dirty_rect);
cc::Layer* Layer();
bool Restore();
- void UpdateFilterQuality();
// virtual for unit testing
virtual void WillOverwriteCanvas();
@@ -131,7 +120,7 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; }
- scoped_refptr<StaticBitmapImage> NewImageSnapshot(AccelerationHint);
+ scoped_refptr<StaticBitmapImage> NewImageSnapshot();
cc::TextureLayer* layer_for_testing() { return layer_.get(); }
@@ -164,8 +153,7 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
void SetLoggerForTesting(std::unique_ptr<Logger> logger) {
logger_ = std::move(logger);
}
- CanvasResourceProvider* GetOrCreateResourceProvider(
- AccelerationHint = kPreferAcceleration);
+ CanvasResourceProvider* GetOrCreateResourceProvider();
CanvasResourceProvider* ResourceProvider() const;
void FlushRecording();
@@ -191,7 +179,8 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
void SkipQueuedDrawCommands();
void EnsureCleared();
- bool ShouldAccelerate(AccelerationHint) const;
+ // Check if the Raster Mode is GPU and if the GPU context is not lost
+ bool ShouldAccelerate() const;
sk_sp<SkImage> hibernation_image_;
scoped_refptr<cc::TextureLayer> layer_;
@@ -201,7 +190,6 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
bool have_recorded_draw_commands_;
bool is_hidden_;
bool is_being_displayed_;
- bool software_rendering_while_hidden_;
bool hibernation_scheduled_ = false;
bool dont_use_idle_scheduling_for_testing_ = false;
bool context_lost_ = false;
@@ -211,7 +199,7 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient {
// WritePixels, the recording is now missing that information.
bool last_record_tainted_by_write_pixels_ = false;
- const AccelerationMode acceleration_mode_;
+ const RasterMode raster_mode_;
const CanvasColorParams color_params_;
const IntSize size_;
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 fa1c188fb95..29165aaf2aa 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
@@ -126,12 +126,11 @@ class Canvas2DLayerBridgeTest : public Test {
public:
std::unique_ptr<Canvas2DLayerBridge> MakeBridge(
const IntSize& size,
- Canvas2DLayerBridge::AccelerationMode acceleration_mode,
+ RasterMode raster_mode,
const CanvasColorParams& color_params,
std::unique_ptr<FakeCanvasResourceHost> custom_host = nullptr) {
std::unique_ptr<Canvas2DLayerBridge> bridge =
- std::make_unique<Canvas2DLayerBridge>(size, acceleration_mode,
- color_params);
+ std::make_unique<Canvas2DLayerBridge>(size, raster_mode, color_params);
bridge->DontUseIdleSchedulingForTesting();
if (custom_host)
host_ = std::move(custom_host);
@@ -167,22 +166,19 @@ class Canvas2DLayerBridgeTest : public Test {
TEST_F(Canvas2DLayerBridgeTest, DisableAcceleration) {
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kDisableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 150), RasterMode::kCPU, CanvasColorParams());
- GrBackendTexture backend_texture =
- bridge->NewImageSnapshot(kPreferAcceleration)
- ->PaintImageForCurrentFrame()
- .GetSkImage()
- ->getBackendTexture(true);
+ GrBackendTexture backend_texture = bridge->NewImageSnapshot()
+ ->PaintImageForCurrentFrame()
+ .GetSkImage()
+ ->getBackendTexture(true);
EXPECT_FALSE(backend_texture.isValid());
}
TEST_F(Canvas2DLayerBridgeTest, NoDrawOnContextLost) {
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsValid());
PaintFlags flags;
uint32_t gen_id = bridge->GetOrCreateResourceProvider()->ContentUniqueID();
@@ -191,13 +187,12 @@ TEST_F(Canvas2DLayerBridgeTest, NoDrawOnContextLost) {
test_context_provider_->TestContextGL()->set_context_lost(true);
EXPECT_EQ(nullptr, bridge->GetOrCreateResourceProvider());
// The following passes by not crashing
- bridge->NewImageSnapshot(kPreferAcceleration);
+ bridge->NewImageSnapshot();
}
TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhenContextIsLost) {
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsAccelerated());
bridge->FinalizeFrame(); // Trigger the creation of a backing store
@@ -213,9 +208,8 @@ TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhenContextIsLost) {
TEST_F(Canvas2DLayerBridgeTest,
PrepareMailboxWhenContextIsLostWithFailedRestore) {
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->GetOrCreateResourceProvider();
EXPECT_TRUE(bridge->IsValid());
@@ -240,9 +234,8 @@ TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxAndLoseResource) {
// Prepare a mailbox, then report the resource as lost.
// This test passes by not crashing and not triggering assertions.
{
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->FinalizeFrame();
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
@@ -259,9 +252,8 @@ TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxAndLoseResource) {
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
{
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->FinalizeFrame();
bridge->PrepareTransferableResource(nullptr, &resource,
&release_callback);
@@ -283,9 +275,8 @@ TEST_F(Canvas2DLayerBridgeTest, ReleaseCallbackWithNullContextProviderWrapper) {
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
{
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->FinalizeFrame();
EXPECT_TRUE(bridge->PrepareTransferableResource(nullptr, &resource,
&release_callback));
@@ -300,51 +291,43 @@ TEST_F(Canvas2DLayerBridgeTest, ReleaseCallbackWithNullContextProviderWrapper) {
release_callback->Run(gpu::SyncToken(), lost_resource);
}
-TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) {
+TEST_F(Canvas2DLayerBridgeTest, RasterModeHint) {
{
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
PaintFlags flags;
bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
- scoped_refptr<StaticBitmapImage> image =
- bridge->NewImageSnapshot(kPreferAcceleration);
+ scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
EXPECT_TRUE(bridge->IsValid());
EXPECT_TRUE(bridge->IsAccelerated());
}
{
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
PaintFlags flags;
bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
- scoped_refptr<StaticBitmapImage> image =
- bridge->NewImageSnapshot(kPreferNoAcceleration);
+ scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
EXPECT_TRUE(bridge->IsValid());
EXPECT_TRUE(bridge->IsAccelerated());
}
{
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kDisableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kCPU, CanvasColorParams());
PaintFlags flags;
bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
- scoped_refptr<StaticBitmapImage> image =
- bridge->NewImageSnapshot(kPreferAcceleration);
+ scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
EXPECT_TRUE(bridge->IsValid());
EXPECT_FALSE(bridge->IsAccelerated());
}
{
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kDisableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kCPU, CanvasColorParams());
PaintFlags flags;
bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags);
- scoped_refptr<StaticBitmapImage> image =
- bridge->NewImageSnapshot(kPreferNoAcceleration);
+ scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
EXPECT_TRUE(bridge->IsValid());
EXPECT_FALSE(bridge->IsAccelerated());
}
@@ -353,8 +336,7 @@ TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) {
TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareIfContextLost) {
test_context_provider_->TestContextGL()->set_context_lost(true);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsValid());
EXPECT_FALSE(bridge->IsAccelerated());
}
@@ -363,19 +345,17 @@ void DrawSomething(Canvas2DLayerBridge* bridge) {
bridge->DidDraw(FloatRect(0, 0, 1, 1));
bridge->FinalizeFrame();
// Grabbing an image forces a flush
- bridge->NewImageSnapshot(kPreferAcceleration);
+ bridge->NewImageSnapshot();
}
TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) {
{
// No fallback case.
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsValid());
EXPECT_TRUE(bridge->IsAccelerated());
- scoped_refptr<StaticBitmapImage> snapshot =
- bridge->NewImageSnapshot(kPreferAcceleration);
+ scoped_refptr<StaticBitmapImage> snapshot = bridge->NewImageSnapshot();
EXPECT_TRUE(bridge->IsAccelerated());
EXPECT_TRUE(snapshot->IsTextureBacked());
}
@@ -387,8 +367,7 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) {
->GetGrContext();
std::unique_ptr<Canvas2DLayerBridge> bridge =
std::make_unique<Canvas2DLayerBridge>(
- IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
EXPECT_TRUE(bridge->IsValid());
EXPECT_TRUE(bridge->IsAccelerated()); // We don't yet know that
@@ -399,8 +378,7 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) {
host_ = std::make_unique<FakeCanvasResourceHost>(IntSize(300, 150));
bridge->SetCanvasResourceHost(host_.get());
DrawSomething(bridge.get());
- scoped_refptr<StaticBitmapImage> snapshot =
- bridge->NewImageSnapshot(kPreferAcceleration);
+ scoped_refptr<StaticBitmapImage> snapshot = bridge->NewImageSnapshot();
EXPECT_FALSE(bridge->IsAccelerated());
EXPECT_FALSE(snapshot->IsTextureBacked());
}
@@ -422,8 +400,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationLifeCycle)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
EXPECT_TRUE(bridge->IsAccelerated());
@@ -468,8 +445,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -508,56 +484,6 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationReEntry)
EXPECT_TRUE(bridge->IsValid());
}
-#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
-TEST_F(Canvas2DLayerBridgeTest, BackgroundRenderingWhileHibernating)
-#else
-TEST_F(Canvas2DLayerBridgeTest, DISABLED_BackgroundRenderingWhileHibernating)
-#endif
-{
- ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
- std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
- bridge->DontUseIdleSchedulingForTesting();
- DrawSomething(bridge.get());
-
- // Register an alternate Logger for tracking hibernation events
- std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
- MockLogger* mock_logger_ptr = mock_logger.get();
- bridge->SetLoggerForTesting(std::move(mock_logger));
-
- // Test entering hibernation
- EXPECT_CALL(
- *mock_logger_ptr,
- ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
- EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
- bridge->SetIsInHiddenPage(true);
- platform->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
- EXPECT_FALSE(bridge->IsAccelerated());
- EXPECT_TRUE(bridge->IsHibernating());
- EXPECT_TRUE(bridge->IsValid());
-
- // Rendering in the background -> temp switch to SW
- EXPECT_CALL(*mock_logger_ptr,
- ReportHibernationEvent(
- Canvas2DLayerBridge::
- kHibernationEndedWithSwitchToBackgroundRendering));
- DrawSomething(bridge.get());
- testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
- EXPECT_FALSE(bridge->IsAccelerated());
- EXPECT_FALSE(bridge->IsHibernating());
- EXPECT_TRUE(bridge->IsValid());
-
- // Unhide
- bridge->SetIsInHiddenPage(false);
- testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
- EXPECT_TRUE(
- bridge->IsAccelerated()); // Becoming visible causes switch back to GPU
- EXPECT_FALSE(bridge->IsHibernating());
- EXPECT_TRUE(bridge->IsValid());
-}
-
#if CANVAS2D_HIBERNATION_ENABLED
TEST_F(Canvas2DLayerBridgeTest, TeardownWhileHibernating)
#else
@@ -566,8 +492,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernating)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -604,8 +529,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -627,8 +551,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_SnapshotWhileHibernating)
EXPECT_TRUE(bridge->IsValid());
// Take a snapshot and verify that it is not accelerated due to hibernation
- scoped_refptr<StaticBitmapImage> image =
- bridge->NewImageSnapshot(kPreferAcceleration);
+ scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot();
EXPECT_FALSE(image->IsTextureBacked());
image = nullptr;
@@ -653,8 +576,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_TeardownWhileHibernationIsPending)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -687,8 +609,7 @@ TEST_F(Canvas2DLayerBridgeTest,
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -723,8 +644,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_HibernationAbortedDueToLostContext)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -758,8 +678,7 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating)
{
ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, CanvasColorParams());
bridge->DontUseIdleSchedulingForTesting();
DrawSomething(bridge.get());
@@ -793,53 +712,6 @@ TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileHibernating)
testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
}
-#if CANVAS2D_HIBERNATION_ENABLED && CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU
-TEST_F(Canvas2DLayerBridgeTest, PrepareMailboxWhileBackgroundRendering)
-#else
-TEST_F(Canvas2DLayerBridgeTest, DISABLED_PrepareMailboxWhileBackgroundRendering)
-#endif
-{
- ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform;
- std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- CanvasColorParams());
- DrawSomething(bridge.get());
-
- // Register an alternate Logger for tracking hibernation events
- std::unique_ptr<MockLogger> mock_logger = std::make_unique<MockLogger>();
- MockLogger* mock_logger_ptr = mock_logger.get();
- bridge->SetLoggerForTesting(std::move(mock_logger));
-
- // Test entering hibernation
- std::unique_ptr<base::WaitableEvent> hibernation_started_event =
- std::make_unique<base::WaitableEvent>();
- EXPECT_CALL(
- *mock_logger_ptr,
- ReportHibernationEvent(Canvas2DLayerBridge::kHibernationScheduled));
- EXPECT_CALL(*mock_logger_ptr, DidStartHibernating()).Times(1);
- bridge->SetIsInHiddenPage(true);
- platform->RunUntilIdle();
- testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
-
- // Rendering in the background -> temp switch to SW
- EXPECT_CALL(*mock_logger_ptr,
- ReportHibernationEvent(
- Canvas2DLayerBridge::
- kHibernationEndedWithSwitchToBackgroundRendering));
- DrawSomething(bridge.get());
- testing::Mock::VerifyAndClearExpectations(mock_logger_ptr);
- EXPECT_FALSE(bridge->IsAccelerated());
- EXPECT_FALSE(bridge->IsHibernating());
- EXPECT_TRUE(bridge->IsValid());
-
- // Test prepareMailbox while background rendering
- viz::TransferableResource resource;
- std::unique_ptr<viz::SingleReleaseCallback> release_callback;
- EXPECT_FALSE(bridge->PrepareTransferableResource(nullptr, &resource,
- &release_callback));
- EXPECT_TRUE(bridge->IsValid());
-}
-
TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) {
ScopedCanvas2dImageChromiumForTest canvas_2d_image_chromium(true);
const_cast<gpu::Capabilities&>(SharedGpuContext::ContextProviderWrapper()
@@ -851,9 +723,8 @@ TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) {
std::unique_ptr<viz::SingleReleaseCallback> callbacks[3];
PaintFlags flags;
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
DrawSomething(bridge.get());
ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0],
@@ -891,9 +762,8 @@ TEST_F(Canvas2DLayerBridgeTest, NoResourceRecyclingWhenPageHidden) {
std::unique_ptr<viz::SingleReleaseCallback> callbacks[2];
PaintFlags flags;
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
DrawSomething(bridge.get());
ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0],
@@ -929,9 +799,8 @@ TEST_F(Canvas2DLayerBridgeTest, ReleaseResourcesAfterBridgeDestroyed) {
viz::TransferableResource resource;
std::unique_ptr<viz::SingleReleaseCallback> release_callback;
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
DrawSomething(bridge.get());
bridge->PrepareTransferableResource(nullptr, &resource, &release_callback);
@@ -949,8 +818,7 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) {
CanvasPixelFormat::kF16, kOpaque);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- color_params);
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, color_params);
gfx::ColorSpace expected_color_space = gfx::ColorSpace::CreateSRGB();
Vector<cc::DrawImage> images = {
cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
@@ -963,8 +831,8 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) {
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);
+ nullptr, SkCanvas::kFast_SrcRectConstraint);
+ bridge->NewImageSnapshot();
EXPECT_EQ(image_decode_cache_.decoded_images(), images);
}
@@ -974,8 +842,7 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) {
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kOpaque);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- color_params);
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, color_params);
Vector<cc::DrawImage> images = {
cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
SkIRect::MakeWH(10, 10), kNone_SkFilterQuality,
@@ -987,8 +854,8 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) {
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);
+ nullptr, SkCanvas::kFast_SrcRectConstraint);
+ bridge->NewImageSnapshot();
EXPECT_EQ(image_decode_cache_.decoded_images(), images);
}
@@ -997,8 +864,7 @@ TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) {
auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB,
CanvasPixelFormat::kF16, kOpaque);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- color_params);
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, color_params);
Vector<cc::DrawImage> images = {
cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
@@ -1033,8 +899,7 @@ TEST_F(Canvas2DLayerBridgeTest, QueuesCleanupTaskForLockedImages) {
auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB,
CanvasPixelFormat::kF16, kOpaque);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- color_params);
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, color_params);
auto image =
cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
@@ -1053,8 +918,7 @@ TEST_F(Canvas2DLayerBridgeTest, ImageCacheOnContextLost) {
auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB,
CanvasPixelFormat::kF16, kOpaque);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration,
- color_params);
+ MakeBridge(IntSize(300, 300), RasterMode::kGPU, color_params);
PaintFlags flags;
Vector<cc::DrawImage> images = {
cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)),
@@ -1076,8 +940,8 @@ TEST_F(Canvas2DLayerBridgeTest, ImageCacheOnContextLost) {
TEST_F(Canvas2DLayerBridgeTest,
PrepareTransferableResourceTracksCanvasChanges) {
IntSize size = IntSize(300, 300);
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(size, RasterMode::kGPU, CanvasColorParams());
bridge->GetPaintCanvas()->clear(SK_ColorRED);
DrawSomething(bridge.get());
@@ -1111,8 +975,7 @@ TEST_F(Canvas2DLayerBridgeTest, WritePixelsRestoresClipStack) {
IntSize size = IntSize(300, 300);
auto host = std::make_unique<CustomFakeCanvasResourceHost>(size);
std::unique_ptr<Canvas2DLayerBridge> bridge =
- MakeBridge(size, Canvas2DLayerBridge::kEnableAcceleration, color_params,
- std::move(host));
+ MakeBridge(size, RasterMode::kGPU, color_params, std::move(host));
PaintFlags flags;
// MakeBridge() results in a call to restore the matrix. So we already have 1.
@@ -1138,9 +1001,8 @@ TEST_F(Canvas2DLayerBridgeTest, WritePixelsRestoresClipStack) {
}
TEST_F(Canvas2DLayerBridgeTest, DisplayedCanvasIsRateLimited) {
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsValid());
bridge->SetIsBeingDisplayed(true);
EXPECT_FALSE(bridge->HasRateLimiterForTesting());
@@ -1150,9 +1012,8 @@ TEST_F(Canvas2DLayerBridgeTest, DisplayedCanvasIsRateLimited) {
}
TEST_F(Canvas2DLayerBridgeTest, NonDisplayedCanvasIsNotRateLimited) {
- std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
- IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
- CanvasColorParams());
+ std::unique_ptr<Canvas2DLayerBridge> bridge =
+ MakeBridge(IntSize(300, 150), RasterMode::kGPU, CanvasColorParams());
EXPECT_TRUE(bridge->IsValid());
bridge->SetIsBeingDisplayed(true);
bridge->FinalizeFrame();
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 d8868023915..3dc0fb19b33 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
@@ -17,21 +17,22 @@ namespace blink {
namespace {
-gfx::ColorSpace::PrimaryID GetPrimaryID(CanvasColorSpace color_space) {
- gfx::ColorSpace::PrimaryID primary_id = gfx::ColorSpace::PrimaryID::BT709;
+// The CanvasColorSpace value definitions are specified in the CSS Color Level 4
+// specification.
+gfx::ColorSpace CanvasColorSpaceToGfxColorSpace(CanvasColorSpace color_space) {
switch (color_space) {
case CanvasColorSpace::kSRGB:
- case CanvasColorSpace::kLinearRGB:
- primary_id = gfx::ColorSpace::PrimaryID::BT709;
+ return gfx::ColorSpace::CreateSRGB();
break;
case CanvasColorSpace::kRec2020:
- primary_id = gfx::ColorSpace::PrimaryID::BT2020;
+ return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
+ gfx::ColorSpace::TransferID::GAMMA24);
break;
case CanvasColorSpace::kP3:
- primary_id = gfx::ColorSpace::PrimaryID::SMPTEST432_1;
+ return gfx::ColorSpace::CreateDisplayP3D65();
break;
}
- return primary_id;
+ NOTREACHED();
}
} // namespace
@@ -89,53 +90,21 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
}
gfx::ColorSpace CanvasColorParams::GetSamplerGfxColorSpace() const {
- gfx::ColorSpace::PrimaryID primary_id = GetPrimaryID(color_space_);
-
- // TODO(ccameron): This needs to take into account whether or not this texture
- // will be sampled in linear or nonlinear space.
- gfx::ColorSpace::TransferID transfer_id =
- gfx::ColorSpace::TransferID::IEC61966_2_1;
- if (pixel_format_ == CanvasPixelFormat::kF16)
- transfer_id = gfx::ColorSpace::TransferID::LINEAR_HDR;
-
- return gfx::ColorSpace(primary_id, transfer_id);
+ // TODO(ccameron): If we add support for uint8srgb as a pixel format, this
+ // will need to take into account whether or not this texture will be sampled
+ // in linear or nonlinear space.
+ return CanvasColorSpaceToGfxColorSpace(color_space_);
}
gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const {
- gfx::ColorSpace::PrimaryID primary_id = GetPrimaryID(color_space_);
-
- gfx::ColorSpace::TransferID transfer_id =
- gfx::ColorSpace::TransferID::IEC61966_2_1;
- // Only sRGB and e-sRGB use sRGB transfer function. Other canvas color spaces,
- // i.e., linear-rgb, p3 and rec2020 use linear transfer function.
- if (color_space_ != CanvasColorSpace::kSRGB)
- transfer_id = gfx::ColorSpace::TransferID::LINEAR_HDR;
-
- return gfx::ColorSpace(primary_id, transfer_id);
+ return CanvasColorSpaceToGfxColorSpace(color_space_);
}
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_) {
- case CanvasColorSpace::kSRGB:
- break;
- case CanvasColorSpace::kLinearRGB:
- transferFn = SkNamedTransferFn::kLinear;
- break;
- case CanvasColorSpace::kRec2020:
- gamut = SkNamedGamut::kRec2020;
- transferFn = SkNamedTransferFn::kLinear;
- break;
- case CanvasColorSpace::kP3:
- gamut = SkNamedGamut::kDCIP3;
- transferFn = SkNamedTransferFn::kLinear;
- break;
- }
- return SkColorSpace::MakeRGB(transferFn, gamut);
+ return CanvasColorSpaceToGfxColorSpace(color_space_).ToSkColorSpace();
}
gfx::BufferFormat CanvasColorParams::GetBufferFormat() const {
@@ -202,37 +171,29 @@ viz::ResourceFormat CanvasColorParams::TransferableResourceFormat() const {
return viz::GetResourceFormat(GetBufferFormat());
}
-CanvasColorParams::CanvasColorParams(const sk_sp<SkColorSpace> color_space,
- SkColorType color_type) {
+CanvasColorParams::CanvasColorParams(const sk_sp<SkColorSpace> sk_color_space,
+ SkColorType sk_color_type) {
color_space_ = CanvasColorSpace::kSRGB;
pixel_format_ = GetNativeCanvasPixelFormat();
- // When there is no color space information, the SkImage is in legacy mode and
- // the color type is kRGBA8 canvas pixel format.
- if (!color_space)
- return;
-
- // CanvasColorSpace::kSRGB covers sRGB and e-sRGB. We need to check for
- // linear-rgb, rec2020 and p3.
- if (SkColorSpace::Equals(color_space.get(),
- SkColorSpace::MakeSRGB()->makeLinearGamma().get())) {
- color_space_ = CanvasColorSpace::kLinearRGB;
- } else if (SkColorSpace::Equals(
- color_space.get(),
- SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
- SkNamedGamut::kRec2020)
- .get())) {
- color_space_ = CanvasColorSpace::kRec2020;
- } else if (SkColorSpace::Equals(
- color_space.get(),
- SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
- SkNamedGamut::kDCIP3)
- .get())) {
- color_space_ = CanvasColorSpace::kP3;
+
+ CanvasColorSpace color_spaces[] = {
+ CanvasColorSpace::kSRGB,
+ CanvasColorSpace::kRec2020,
+ CanvasColorSpace::kP3,
+ };
+ for (const auto& color_space : color_spaces) {
+ if (SkColorSpace::Equals(sk_color_space.get(),
+ CanvasColorSpaceToGfxColorSpace(color_space)
+ .ToSkColorSpace()
+ .get())) {
+ color_space_ = color_space;
+ break;
+ }
}
- if (color_type == kRGBA_F16_SkColorType)
+ if (sk_color_type == kRGBA_F16_SkColorType)
pixel_format_ = CanvasPixelFormat::kF16;
- else if (color_type == kRGBA_8888_SkColorType)
+ else if (sk_color_type == kRGBA_8888_SkColorType)
pixel_format_ = CanvasPixelFormat::kRGBA8;
}
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 3920ea5c865..c35b29ed0b6 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
@@ -27,7 +27,6 @@ namespace blink {
enum class CanvasColorSpace {
kSRGB,
- kLinearRGB,
kRec2020,
kP3,
};
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 b34686a8065..bbee512d832 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
@@ -19,11 +19,10 @@ namespace blink {
TEST(CanvasColorParamsTest, MatchSkColorSpaceWithGfxColorSpace) {
CanvasColorSpace canvas_color_spaces[] = {
CanvasColorSpace::kSRGB,
- CanvasColorSpace::kLinearRGB,
CanvasColorSpace::kRec2020,
CanvasColorSpace::kP3,
};
- for (int iter_color_space = 0; iter_color_space < 4; iter_color_space++) {
+ for (int iter_color_space = 0; iter_color_space < 3; iter_color_space++) {
CanvasColorParams color_params(canvas_color_spaces[iter_color_space],
CanvasPixelFormat::kF16, kNonOpaque);
sk_sp<SkColorSpace> canvas_drawing_color_space =
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 3566c549188..86bc4ef4175 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -1149,11 +1149,9 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSwapChain::Bitmap() {
Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
ColorParams().GetSkAlphaType(), ColorParams().GetSkColorSpace());
- // It's safe to share the front buffer texture id if we're on the same thread
+ // It's safe to share the back 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 (!is_cross_thread())
- shared_texture_id = front_buffer_texture_id_;
+ GLuint shared_texture_id = !is_cross_thread() ? back_buffer_texture_id_ : 0u;
// The |release_callback| keeps a ref on this resource to ensure the backing
// shared image is kept alive until the lifetime of the image.
@@ -1163,8 +1161,11 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSwapChain::Bitmap() {
},
base::RetainedRef(this)));
+ // Use an empty sync token so that the image lazily generates its own sync
+ // token so that it can synchronize with Skia commands as well as the copy
+ // from the front buffer to back buffer after present.
return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
- front_buffer_mailbox_, sync_token_, shared_texture_id, image_info,
+ back_buffer_mailbox_, gpu::SyncToken(), shared_texture_id, image_info,
GL_TEXTURE_2D, true /*is_origin_top_left*/, context_provider_wrapper_,
owning_thread_ref_, owning_thread_task_runner_,
std::move(release_callback));
@@ -1184,9 +1185,6 @@ void CanvasResourceSwapChain::TearDown() {
auto* raster_interface =
context_provider_wrapper_->ContextProvider()->RasterInterface();
DCHECK(raster_interface);
- raster_interface->EndSharedImageAccessDirectCHROMIUM(
- front_buffer_texture_id_);
- raster_interface->DeleteGpuRasterTexture(front_buffer_texture_id_);
raster_interface->EndSharedImageAccessDirectCHROMIUM(back_buffer_texture_id_);
raster_interface->DeleteGpuRasterTexture(back_buffer_texture_id_);
// No synchronization is needed here because the GL SharedImageRepresentation
@@ -1243,6 +1241,7 @@ void CanvasResourceSwapChain::PresentSwapChain() {
GL_TEXTURE_2D, 0, 0, 0, 0, size_.Width(),
size_.Height(), false /* unpack_flip_y */,
false /* unpack_premultiply_alpha */);
+ // Don't generate sync token here so that the copy is not on critical path.
}
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
@@ -1284,11 +1283,6 @@ CanvasResourceSwapChain::CanvasResourceSwapChain(
DCHECK(raster_interface);
raster_interface->WaitSyncTokenCHROMIUM(sync_token_.GetData());
- front_buffer_texture_id_ =
- raster_interface->CreateAndConsumeForGpuRaster(front_buffer_mailbox_);
- raster_interface->BeginSharedImageAccessDirectCHROMIUM(
- front_buffer_texture_id_, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
-
back_buffer_texture_id_ =
raster_interface->CreateAndConsumeForGpuRaster(back_buffer_mailbox_);
raster_interface->BeginSharedImageAccessDirectCHROMIUM(
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 5c1628ab62f..d694a7b92b8 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
#include "components/viz/common/resources/shared_bitmap.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
@@ -636,9 +637,8 @@ class PLATFORM_EXPORT CanvasResourceSwapChain final : public CanvasResource {
scoped_refptr<StaticBitmapImage> Bitmap() override;
GLenum TextureTarget() const final { return GL_TEXTURE_2D; }
- GLuint GetBackingTextureHandleForOverwrite() {
- return back_buffer_texture_id_;
- }
+
+ GLuint GetBackBufferTextureId() const { return back_buffer_texture_id_; }
void PresentSwapChain();
const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override;
@@ -662,7 +662,6 @@ class PLATFORM_EXPORT CanvasResourceSwapChain final : public CanvasResource {
const IntSize size_;
gpu::Mailbox front_buffer_mailbox_;
gpu::Mailbox back_buffer_mailbox_;
- GLuint front_buffer_texture_id_ = 0u;
GLuint back_buffer_texture_id_ = 0u;
gpu::SyncToken sync_token_;
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 528df308b96..89dcc5190d4 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
@@ -169,7 +169,7 @@ void CanvasResourceDispatcher::DispatchFrameSync(
sink_->SubmitCompositorFrameSync(
parent_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(),
- std::move(frame), nullptr, 0, &resources);
+ std::move(frame), base::nullopt, 0, &resources);
DidReceiveCompositorFrameAck(resources);
}
@@ -190,7 +190,7 @@ void CanvasResourceDispatcher::DispatchFrame(
sink_->SubmitCompositorFrame(
parent_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(),
- std::move(frame), nullptr, 0);
+ std::move(frame), base::nullopt, 0);
}
bool CanvasResourceDispatcher::PrepareFrame(
@@ -334,7 +334,7 @@ bool CanvasResourceDispatcher::HasTooManyPendingFrames() const {
void CanvasResourceDispatcher::OnBeginFrame(
const viz::BeginFrameArgs& begin_frame_args,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) {
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>&) {
current_begin_frame_ack_ = viz::BeginFrameAck(begin_frame_args, false);
if (HasTooManyPendingFrames() ||
(begin_frame_args.type == viz::BeginFrameArgs::MISSED &&
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 ef9aabfc2e2..b8549abc864 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
@@ -75,7 +75,7 @@ class PLATFORM_EXPORT CanvasResourceDispatcher
const WTF::Vector<viz::ReturnedResource>& resources) final;
void OnBeginFrame(
const viz::BeginFrameArgs&,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>) final;
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>&) final;
void OnBeginFramePausedChanged(bool paused) final {}
void ReclaimResources(
const WTF::Vector<viz::ReturnedResource>& resources) final;
diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
index 5f9829655ff..44ddf30a275 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
@@ -87,17 +87,9 @@ class CanvasResourceDispatcherTest
void CreateCanvasResourceDispatcher() {
dispatcher_ = std::make_unique<MockCanvasResourceDispatcher>();
- // TODO(crbug/1035589) Previously a call to the more generic function
- // `CanvasResourceProvider::Create` was used but due to `presentationMode =
- // kDefaultPresentationMode` created a sharedBitmap 100% of the time.
- // Investigate study if the Bitmap fallback makes sense or not.
resource_provider_ = CanvasResourceProvider::CreateSharedBitmapProvider(
- IntSize(kWidth, kHeight), nullptr /* context_provider_wrapper */,
- kLow_SkFilterQuality, CanvasColorParams(), dispatcher_->GetWeakPtr());
- if (!resource_provider_) {
- resource_provider_ = CanvasResourceProvider::CreateBitmapProvider(
- IntSize(kWidth, kHeight), kLow_SkFilterQuality, CanvasColorParams());
- }
+ IntSize(kWidth, kHeight), kLow_SkFilterQuality, CanvasColorParams(),
+ dispatcher_->GetWeakPtr());
}
MockCanvasResourceDispatcher* Dispatcher() { return dispatcher_.get(); }
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 16dbe14e169..f918d35619d 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
@@ -26,9 +26,9 @@ class PLATFORM_EXPORT CanvasResourceHost {
virtual void RestoreCanvasMatrixClipStack(cc::PaintCanvas*) const = 0;
virtual void UpdateMemoryUsage() = 0;
virtual CanvasResourceProvider* GetOrCreateCanvasResourceProvider(
- AccelerationHint hint) = 0;
+ RasterModeHint hint) = 0;
virtual CanvasResourceProvider* GetOrCreateCanvasResourceProviderImpl(
- AccelerationHint hint) = 0;
+ RasterModeHint hint) = 0;
virtual SkFilterQuality FilterQuality() const = 0;
virtual bool LowLatencyEnabled() const { return false; }
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 dbf90ff01ac..4635d4a3883 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
@@ -7,6 +7,8 @@
#include "base/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/stl_util.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
#include "build/build_config.h"
#include "cc/paint/decode_stashing_image_provider.h"
#include "cc/paint/display_item_list.h"
@@ -24,6 +26,7 @@
#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/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -47,7 +50,8 @@ class CanvasResourceProvider::CanvasImageProvider : public cc::ImageProvider {
cc::ImageDecodeCache* cache_f16,
const gfx::ColorSpace& target_color_space,
SkColorType target_color_type,
- bool is_hardware_decode_cache);
+ bool is_hardware_decode_cache,
+ bool use_oop_raster);
~CanvasImageProvider() override = default;
// cc::ImageProvider implementation.
@@ -63,7 +67,7 @@ class CanvasResourceProvider::CanvasImageProvider : public cc::ImageProvider {
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_n32_;
base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_;
base::WeakPtrFactory<CanvasImageProvider> weak_factory_{this};
@@ -82,7 +86,6 @@ class CanvasResourceProviderBitmap : public CanvasResourceProvider {
base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
: CanvasResourceProvider(kBitmap,
size,
- 0 /*msaa_sample_count*/,
filter_quality,
color_params,
true /*is_origin_top_left*/,
@@ -172,12 +175,10 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
public:
CanvasResourceProviderSharedImage(
const IntSize& size,
- unsigned msaa_sample_count,
SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
context_provider_wrapper,
- base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
bool is_origin_top_left,
bool is_accelerated,
bool use_webgpu,
@@ -185,7 +186,6 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
: CanvasResourceProvider(
use_webgpu ? kWebGPUSharedImage : kSharedImage,
size,
- msaa_sample_count,
filter_quality,
// TODO(khushalsagar): The software path seems to be assuming N32
// somewhere in the later pipeline but for offscreen canvas only.
@@ -197,13 +197,13 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
color_params.GetOpacityMode()),
is_origin_top_left,
std::move(context_provider_wrapper),
- std::move(resource_dispatcher)),
+ nullptr /* resource_dispatcher */),
is_accelerated_(is_accelerated),
shared_image_usage_flags_(shared_image_usage_flags),
- use_oop_rasterization_(ContextProviderWrapper()
- ->ContextProvider()
- ->GetCapabilities()
- .supports_oop_raster) {
+ use_oop_rasterization_(is_accelerated && ContextProviderWrapper()
+ ->ContextProvider()
+ ->GetCapabilities()
+ .supports_oop_raster) {
resource_ = NewOrRecycledResource();
if (resource_)
EnsureWriteAccess();
@@ -239,6 +239,26 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
return resource()->TextureTarget();
}
+ bool WritePixels(const SkImageInfo& orig_info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) override {
+ if (!use_oop_rasterization_) {
+ return CanvasResourceProvider::WritePixels(orig_info, pixels, row_bytes,
+ x, y);
+ }
+
+ TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::WritePixels");
+ if (IsGpuContextLost())
+ return false;
+
+ RasterInterface()->WritePixels(
+ GetBackingMailboxForOverwrite(kOrderingBarrier), x, y,
+ GetBackingTextureTarget(), row_bytes, orig_info, pixels);
+ return true;
+ }
+
scoped_refptr<CanvasResource> CreateResource() final {
TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::CreateResource");
if (IsGpuContextLost())
@@ -258,6 +278,8 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
shared_image_usage_flags_);
}
+ bool UseOopRasterization() final { return use_oop_rasterization_; }
+
void NotifyTexParamsModified(const CanvasResource* resource) override {
if (!is_accelerated_ || use_oop_rasterization_)
return;
@@ -419,7 +441,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
gfx::Vector2dF post_translate(0.f, 0.f);
ri->BeginRasterCHROMIUM(
- background_color, GetMSAASampleCount(), use_lcd,
+ background_color, 0 /* msaa_sample_count */, use_lcd,
ColorParams().GetStorageGfxColorSpace(),
resource()->GetOrCreateGpuMailbox(kUnverifiedSyncToken).name);
@@ -452,7 +474,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
// flushing once to see if that releases the read refs. We can avoid a copy
// by queuing this work before writing to this resource.
if (is_accelerated_)
- surface_->flush();
+ surface_->flushAndSubmit();
return !resource_->HasOneRef();
}
@@ -465,7 +487,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
if (is_accelerated_) {
return SkSurface::MakeFromBackendTexture(
GetGrContext(), CreateGrTextureForResource(), GetGrSurfaceOrigin(),
- GetMSAASampleCount(), ColorParams().GetSkColorType(),
+ 0 /* msaa_sample_count */, ColorParams().GetSkColorType(),
ColorParams().GetSkColorSpaceForSkSurfaces(),
ColorParams().GetSkSurfaceProps());
}
@@ -494,7 +516,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
// SkSurface here.
if (IsGpuContextLost())
return;
- GetGrContext()->flush();
+ GetGrContext()->flushAndSubmit();
}
void EnsureWriteAccess() {
@@ -579,7 +601,6 @@ class CanvasResourceProviderPassThrough final : public CanvasResourceProvider {
bool is_origin_top_left)
: CanvasResourceProvider(kPassThrough,
size,
- /*msaa_sample_count=*/0,
filter_quality,
color_params,
is_origin_top_left,
@@ -625,7 +646,6 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
public:
CanvasResourceProviderSwapChain(
const IntSize& size,
- unsigned msaa_sample_count,
SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>
@@ -633,16 +653,18 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
: CanvasResourceProvider(kSwapChain,
size,
- msaa_sample_count,
filter_quality,
color_params,
true /*is_origin_top_left*/,
std::move(context_provider_wrapper),
- std::move(resource_dispatcher)),
- msaa_sample_count_(msaa_sample_count) {
+ std::move(resource_dispatcher)) {
resource_ = CanvasResourceSwapChain::Create(
Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
FilterQuality());
+ // CanvasResourceProviderSwapChain can only operate in a single buffered
+ // mode so enable it as soon as possible.
+ TryEnableSingleBuffering();
+ DCHECK(IsSingleBuffered());
}
~CanvasResourceProviderSwapChain() override = default;
@@ -652,7 +674,10 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
bool SupportsSingleBuffering() const override { return true; }
private:
- void WillDraw() override { dirty_ = true; }
+ void WillDraw() override {
+ needs_present_ = true;
+ needs_flush_ = true;
+ }
scoped_refptr<CanvasResource> CreateResource() final {
TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::CreateResource");
@@ -665,10 +690,12 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
"CanvasResourceProviderSwapChain::ProduceCanvasResource");
if (!IsValid())
return nullptr;
- FlushCanvas();
- if (dirty_) {
+
+ FlushIfNeeded();
+
+ if (needs_present_) {
resource_->PresentSwapChain();
- dirty_ = false;
+ needs_present_ = false;
}
return resource_;
}
@@ -676,11 +703,12 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
scoped_refptr<StaticBitmapImage> Snapshot(const ImageOrientation&) override {
TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::Snapshot");
- // Use ProduceCanvasResource to ensure any queued commands are flushed and
- // the resource is updated.
- if (auto resource = ProduceCanvasResource())
- return resource->Bitmap();
- return nullptr;
+ if (!IsValid())
+ return nullptr;
+
+ FlushIfNeeded();
+
+ return resource_->Bitmap();
}
sk_sp<SkSurface> CreateSkSurface() const override {
@@ -688,24 +716,34 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
if (IsGpuContextLost() || !resource_)
return nullptr;
- DCHECK(resource_);
GrGLTextureInfo texture_info = {};
- texture_info.fID = resource_->GetBackingTextureHandleForOverwrite();
+ texture_info.fID = resource_->GetBackBufferTextureId();
texture_info.fTarget = resource_->TextureTarget();
texture_info.fFormat = ColorParams().GLSizedInternalFormat();
auto backend_texture = GrBackendTexture(Size().Width(), Size().Height(),
GrMipMapped::kNo, texture_info);
- return SkSurface::MakeFromBackendTextureAsRenderTarget(
+ return SkSurface::MakeFromBackendTexture(
GetGrContext(), backend_texture, kTopLeft_GrSurfaceOrigin,
- msaa_sample_count_, ColorParams().GetSkColorType(),
+ 0 /* msaa_sample_count */, ColorParams().GetSkColorType(),
ColorParams().GetSkColorSpaceForSkSurfaces(),
ColorParams().GetSkSurfaceProps());
}
- const unsigned msaa_sample_count_;
- bool dirty_ = false;
+ void FlushIfNeeded() {
+ if (needs_flush_) {
+ // This only flushes recorded draw ops.
+ FlushCanvas();
+ // Call flushAndSubmit() explicitly so that any non-draw-op rendering by
+ // Skia is flushed to GL. This is needed specifically for WritePixels().
+ GetGrContext()->flushAndSubmit();
+ needs_flush_ = false;
+ }
+ }
+
+ bool needs_present_ = false;
+ bool needs_flush_ = false;
scoped_refptr<CanvasResourceSwapChain> resource_;
};
@@ -720,222 +758,8 @@ enum class CanvasResourceType {
kWebGPUSharedImage,
};
-const Vector<CanvasResourceType>& GetResourceTypeFallbackList(
- CanvasResourceProvider::ResourceUsage usage) {
-
- static const Vector<CanvasResourceType> kCompositedFallbackList({
- CanvasResourceType::kSharedImage,
- CanvasResourceType::kSharedBitmap,
- // Fallback to no direct compositing support
- CanvasResourceType::kBitmap,
- });
-
- static const Vector<CanvasResourceType> kCompositedFallbackListWithDawn({
- CanvasResourceType::kWebGPUSharedImage,
- CanvasResourceType::kSharedImage,
- CanvasResourceType::kSharedBitmap,
- // Fallback to no direct compositing support
- CanvasResourceType::kBitmap,
- });
-
- static const Vector<CanvasResourceType> kAcceleratedDirect2DFallbackList({
- // Needed for low latency canvas on Windows.
- CanvasResourceType::kDirect2DSwapChain,
- // The rest is equal to |kCompositedFallbackList|.
- CanvasResourceType::kSharedImage,
- CanvasResourceType::kSharedBitmap,
- CanvasResourceType::kBitmap,
- });
- DCHECK(std::equal(kAcceleratedDirect2DFallbackList.begin() + 1,
- kAcceleratedDirect2DFallbackList.end(),
- kCompositedFallbackList.begin(),
- kCompositedFallbackList.end()));
-
- static const Vector<CanvasResourceType> kAcceleratedDirect3DFallbackList({
- // This is used with single-buffered WebGL where the resource comes
- // from an external source. The external site should take care of
- // using SharedImages since the resource will be used by the display
- // compositor.
- CanvasResourceType::kDirect3DPassThrough,
- // The rest is equal to |kCompositedFallbackList|.
- CanvasResourceType::kSharedImage,
- CanvasResourceType::kSharedBitmap,
- CanvasResourceType::kBitmap,
- });
- DCHECK(std::equal(kAcceleratedDirect3DFallbackList.begin() + 1,
- kAcceleratedDirect3DFallbackList.end(),
- kCompositedFallbackList.begin(),
- kCompositedFallbackList.end()));
-
- static const Vector<CanvasResourceType> kEmptyList;
- switch (usage) {
- // All these usages have been deprecated.
- case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage:
- case CanvasResourceProvider::ResourceUsage::kSoftwareResourceUsage:
- case CanvasResourceProvider::ResourceUsage::
- kSoftwareCompositedDirect2DResourceUsage:
- case CanvasResourceProvider::ResourceUsage::
- kSoftwareCompositedResourceUsage:
- NOTREACHED();
- return kEmptyList;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedCompositedResourceUsage:
- if (base::FeatureList::IsEnabled(blink::features::kDawn2dCanvas)) {
- return kCompositedFallbackListWithDawn;
- }
- return kCompositedFallbackList;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedDirect2DResourceUsage:
- return kAcceleratedDirect2DFallbackList;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedDirect3DResourceUsage:
- return kAcceleratedDirect3DFallbackList;
- }
- NOTREACHED();
- return kEmptyList;
-}
-
} // unnamed namespace
-std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create(
- 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) {
- DCHECK_EQ(msaa_sample_count, 0u);
- // These usages have been deprecated.
- DCHECK(usage != ResourceUsage::kSoftwareResourceUsage);
- DCHECK(usage != ResourceUsage::kAcceleratedResourceUsage);
- DCHECK(usage != ResourceUsage::kSoftwareCompositedDirect2DResourceUsage);
- DCHECK(usage != ResourceUsage::kSoftwareCompositedResourceUsage);
-
- std::unique_ptr<CanvasResourceProvider> provider;
-
- bool is_gpu_memory_buffer_image_allowed = false;
- bool is_swap_chain_allowed = false;
-
- if (SharedGpuContext::IsGpuCompositingEnabled() &&
- !base::FeatureList::IsEnabled(blink::features::kDawn2dCanvas) &&
- context_provider_wrapper) {
- const auto& context_capabilities =
- context_provider_wrapper->ContextProvider()->GetCapabilities();
-
- const int max_texture_size = context_capabilities.max_texture_size;
-
- if (size.Width() > max_texture_size || size.Height() > max_texture_size)
- return CreateBitmapProvider(size, filter_quality, color_params);
-
- is_gpu_memory_buffer_image_allowed =
- (presentation_mode & kAllowImageChromiumPresentationMode) &&
- Platform::Current()->GetGpuMemoryBufferManager() &&
- IsGMBAllowed(size, color_params, context_capabilities);
-
- is_swap_chain_allowed =
- (presentation_mode & kAllowSwapChainPresentationMode) &&
- context_capabilities.shared_image_swap_chain;
- }
-
- const Vector<CanvasResourceType>& fallback_list =
- GetResourceTypeFallbackList(usage);
-
- for (CanvasResourceType resource_type : fallback_list) {
- // Note: We are deliberately not using std::move() on
- // |context_provider_wrapper| and |resource_dispatcher| to ensure that the
- // pointers remain valid for the next iteration of this loop if necessary.
- switch (resource_type) {
- case CanvasResourceType::kDirect2DSwapChain:
- if (!is_swap_chain_allowed)
- continue;
- DCHECK(is_origin_top_left);
- provider = std::make_unique<CanvasResourceProviderSwapChain>(
- size, msaa_sample_count, filter_quality, color_params,
- context_provider_wrapper, resource_dispatcher);
- break;
- case CanvasResourceType::kDirect3DPassThrough:
- if (!is_gpu_memory_buffer_image_allowed && !is_swap_chain_allowed)
- continue;
- provider = std::make_unique<CanvasResourceProviderPassThrough>(
- size, filter_quality, color_params, context_provider_wrapper,
- resource_dispatcher, is_origin_top_left);
- break;
- case CanvasResourceType::kSharedBitmap:
- if (!resource_dispatcher)
- continue;
- provider = std::make_unique<CanvasResourceProviderSharedBitmap>(
- size, filter_quality, color_params, resource_dispatcher);
- break;
- case CanvasResourceType::kBitmap:
- provider = std::make_unique<CanvasResourceProviderBitmap>(
- size, filter_quality, color_params, resource_dispatcher);
- break;
- case CanvasResourceType::kSharedImage:
- case CanvasResourceType::kWebGPUSharedImage: {
- if (!context_provider_wrapper)
- continue;
-
- const bool can_use_overlays =
- is_gpu_memory_buffer_image_allowed &&
- context_provider_wrapper->ContextProvider()
- ->GetCapabilities()
- .texture_storage_image;
-
- 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_accelerated,
- resource_type == CanvasResourceType::kWebGPUSharedImage,
- shared_image_usage_flags);
-
- } break;
- }
- if (!provider->IsValid())
- continue;
- return provider;
- }
-
- return nullptr;
-}
-
std::unique_ptr<CanvasResourceProvider>
CanvasResourceProvider::CreateBitmapProvider(
const IntSize& size,
@@ -952,26 +776,11 @@ CanvasResourceProvider::CreateBitmapProvider(
std::unique_ptr<CanvasResourceProvider>
CanvasResourceProvider::CreateSharedBitmapProvider(
const IntSize& size,
- base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) {
- // TODO(crbug/1035589). The former CanvasResourceProvider::Create was doing
- // this check as well for SharedBitmapProvider, while this should not make
- // sense, will be left for a later CL to address this issue and the failing
- // tests due to not having this check here.
- if (SharedGpuContext::IsGpuCompositingEnabled() && context_provider_wrapper) {
- const auto& max_texture_size = context_provider_wrapper->ContextProvider()
- ->GetCapabilities()
- .max_texture_size;
- if (size.Width() > max_texture_size || size.Height() > max_texture_size) {
- return nullptr;
- }
- }
-
- // TODO(crbug/1035589). The former CanvasResourceProvider::Create was doing
- // this check as well for SharedBitmapProvider, as we are passing a weap_ptr
- // maybe the caller could ensure an always valid weakptr.
+ // SharedBitmapProvider has to have a valid resource_dispatecher to be able to
+ // be created.
if (!resource_dispatcher)
return nullptr;
@@ -991,8 +800,7 @@ CanvasResourceProvider::CreateSharedImageProvider(
const CanvasColorParams& color_params,
bool is_origin_top_left,
RasterMode raster_mode,
- uint32_t shared_image_usage_flags,
- unsigned msaa_sample_count) {
+ uint32_t shared_image_usage_flags) {
if (!context_provider_wrapper)
return nullptr;
@@ -1016,14 +824,16 @@ CanvasResourceProvider::CreateSharedImageProvider(
if (raster_mode == RasterMode::kCPU && !is_gpu_memory_buffer_image_allowed)
return nullptr;
- // If we cannot use overlay, we have to remove scanout flag flag.
+ // If we cannot use overlay, we have to remove the scanout flag and the
+ // concurrent read write flag.
if (!is_gpu_memory_buffer_image_allowed ||
- !capabilities.texture_storage_image)
+ !capabilities.texture_storage_image) {
+ shared_image_usage_flags &= ~gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
shared_image_usage_flags &= ~gpu::SHARED_IMAGE_USAGE_SCANOUT;
+ }
auto provider = std::make_unique<CanvasResourceProviderSharedImage>(
- size, msaa_sample_count, filter_quality, color_params,
- context_provider_wrapper, nullptr /* resource_dispatcher */,
+ size, filter_quality, color_params, context_provider_wrapper,
is_origin_top_left, raster_mode == RasterMode::kGPU, use_webgpu,
shared_image_usage_flags);
if (provider->IsValid())
@@ -1046,13 +856,14 @@ CanvasResourceProvider::CreatePassThroughProvider(
const auto& capabilities =
context_provider_wrapper->ContextProvider()->GetCapabilities();
if (size.Width() > capabilities.max_texture_size ||
- size.Height() > capabilities.max_texture_size ||
- !capabilities.shared_image_swap_chain) {
+ size.Height() > capabilities.max_texture_size) {
return nullptr;
}
- if (!IsGMBAllowed(size, color_params, capabilities) ||
- !Platform::Current()->GetGpuMemoryBufferManager())
+ // Either swap_chain or gpu memory buffer should be enabled for this be used
+ if (!capabilities.shared_image_swap_chain &&
+ (!IsGMBAllowed(size, color_params, capabilities) ||
+ !Platform::Current()->GetGpuMemoryBufferManager()))
return nullptr;
auto provider = std::make_unique<CanvasResourceProviderPassThrough>(
@@ -1064,22 +875,57 @@ CanvasResourceProvider::CreatePassThroughProvider(
return nullptr;
}
+std::unique_ptr<CanvasResourceProvider>
+CanvasResourceProvider::CreateSwapChainProvider(
+ const IntSize& size,
+ base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
+ SkFilterQuality filter_quality,
+ const CanvasColorParams& color_params,
+ bool is_origin_top_left,
+ base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) {
+ DCHECK(is_origin_top_left);
+ if (!SharedGpuContext::IsGpuCompositingEnabled() || !context_provider_wrapper)
+ return nullptr;
+
+ const auto& capabilities =
+ context_provider_wrapper->ContextProvider()->GetCapabilities();
+ if (size.Width() > capabilities.max_texture_size ||
+ size.Height() > capabilities.max_texture_size ||
+ !capabilities.shared_image_swap_chain) {
+ return nullptr;
+ }
+
+ auto provider = std::make_unique<CanvasResourceProviderSwapChain>(
+ size, filter_quality, color_params, context_provider_wrapper,
+ resource_dispatcher);
+ if (provider->IsValid())
+ return provider;
+
+ return nullptr;
+}
+
CanvasResourceProvider::CanvasImageProvider::CanvasImageProvider(
cc::ImageDecodeCache* cache_n32,
cc::ImageDecodeCache* cache_f16,
const gfx::ColorSpace& target_color_space,
SkColorType canvas_color_type,
- bool is_hardware_decode_cache)
- : is_hardware_decode_cache_(is_hardware_decode_cache),
- playback_image_provider_n32_(cache_n32,
- target_color_space,
- cc::PlaybackImageProvider::Settings()) {
+ bool is_hardware_decode_cache,
+ bool use_oop_raster)
+ : is_hardware_decode_cache_(is_hardware_decode_cache) {
+ base::Optional<cc::PlaybackImageProvider::Settings> settings =
+ cc::PlaybackImageProvider::Settings();
+ settings->use_oop_raster = use_oop_raster;
+
+ playback_image_provider_n32_.emplace(cache_n32, target_color_space,
+ std::move(settings));
// If the image provider may require to decode to half float instead of
// uint8, create a f16 PlaybackImageProvider with the passed cache.
if (canvas_color_type == kRGBA_F16_SkColorType) {
DCHECK(cache_f16);
+ settings = cc::PlaybackImageProvider::Settings();
+ settings->use_oop_raster = use_oop_raster;
playback_image_provider_f16_.emplace(cache_f16, target_color_space,
- cc::PlaybackImageProvider::Settings());
+ std::move(settings));
}
}
@@ -1098,7 +944,7 @@ CanvasResourceProvider::CanvasImageProvider::GetRasterContent(
playback_image_provider_f16_->GetRasterContent(draw_image);
} else {
scoped_decoded_image =
- playback_image_provider_n32_.GetRasterContent(draw_image);
+ playback_image_provider_n32_->GetRasterContent(draw_image);
}
// Holding onto locked images here is a performance optimization for the
@@ -1154,7 +1000,6 @@ void CanvasResourceProvider::CanvasImageProvider::CleanupLockedImages() {
CanvasResourceProvider::CanvasResourceProvider(
const ResourceProviderType& type,
const IntSize& size,
- unsigned msaa_sample_count,
SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
bool is_origin_top_left,
@@ -1164,13 +1009,14 @@ CanvasResourceProvider::CanvasResourceProvider(
context_provider_wrapper_(std::move(context_provider_wrapper)),
resource_dispatcher_(resource_dispatcher),
size_(size),
- msaa_sample_count_(msaa_sample_count),
filter_quality_(filter_quality),
color_params_(color_params),
is_origin_top_left_(is_origin_top_left),
- snapshot_paint_image_id_(cc::PaintImage::GetNextId()) {
+ snapshot_paint_image_id_(cc::PaintImage::GetNextId()),
+ identifiability_paint_op_digest_(size_) {
if (context_provider_wrapper_)
context_provider_wrapper_->AddObserver(this);
+ CanvasMemoryDumpProvider::Instance()->RegisterClient(this);
}
CanvasResourceProvider::~CanvasResourceProvider() {
@@ -1178,6 +1024,7 @@ CanvasResourceProvider::~CanvasResourceProvider() {
max_inflight_resources_, 20);
if (context_provider_wrapper_)
context_provider_wrapper_->RemoveObserver(this);
+ CanvasMemoryDumpProvider::Instance()->UnregisterClient(this);
}
SkSurface* CanvasResourceProvider::GetSkSurface() const {
@@ -1216,7 +1063,8 @@ CanvasResourceProvider::GetOrCreateCanvasImageProvider() {
cache_f16 = ImageDecodeCacheF16();
canvas_image_provider_ = std::make_unique<CanvasImageProvider>(
ImageDecodeCacheRGBA8(), cache_f16, gfx::ColorSpace::CreateSRGB(),
- color_params_.GetSkColorType(), use_hardware_decode_cache());
+ color_params_.GetSkColorType(), use_hardware_decode_cache(),
+ UseOopRasterization());
}
return canvas_image_provider_.get();
}
@@ -1301,6 +1149,9 @@ GrContext* CanvasResourceProvider::GetGrContext() const {
sk_sp<cc::PaintRecord> CanvasResourceProvider::FlushCanvas() {
if (!HasRecordedDrawOps())
return nullptr;
+ // Get PaintOp count before finishRecordingAsPicture() adds more, as these
+ // additional ops don't correspond to canvas context operations.
+ const size_t initial_paint_ops = recorder_->num_paint_ops();
sk_sp<cc::PaintRecord> last_recording = recorder_->finishRecordingAsPicture();
RasterRecord(last_recording);
needs_flush_ = false;
@@ -1308,6 +1159,12 @@ sk_sp<cc::PaintRecord> CanvasResourceProvider::FlushCanvas() {
recorder_->beginRecording(Size().Width(), Size().Height());
if (restore_clip_stack_callback_)
restore_clip_stack_callback_.Run(canvas);
+ identifiability_paint_op_digest_.MaybeUpdateDigest(last_recording,
+ initial_paint_ops);
+ // restore_clip_stack_callback_ also adds PaintOps -- these need to be skipped
+ // during identifiability digest calculation.
+ identifiability_paint_op_digest_.SetPrefixSkipCount(
+ recorder_->num_paint_ops());
return last_recording;
}
@@ -1315,7 +1172,7 @@ void CanvasResourceProvider::RasterRecord(
sk_sp<cc::PaintRecord> last_recording) {
EnsureSkiaCanvas();
skia_canvas_->drawPicture(std::move(last_recording));
- GetSkSurface()->flush();
+ GetSkSurface()->flushAndSubmit();
}
bool CanvasResourceProvider::IsGpuContextLost() const {
@@ -1339,27 +1196,28 @@ bool CanvasResourceProvider::WritePixels(const SkImageInfo& orig_info,
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);
}
void CanvasResourceProvider::Clear() {
- // Clear the background transparent or opaque, as required. It would be nice
- // if this wasn't required, but the canvas is currently filled with the magic
- // transparency color. Can we have another way to manage this?
+ // We don't have an SkCanvas in OOPR mode so we can't do the clear below, plus
+ // OOPR already clears the canvas in BeginRaster.
+ if (UseOopRasterization()) {
+ return;
+ }
+
+ // Clear the background transparent or opaque, as required. This should only
+ // be called when a new resource provider is created to ensure that we're
+ // not leaking data or displaying bad pixels (in the case of kOpaque
+ // canvases). Instead of adding these commands to our deferred queue, we'll
+ // send them directly through to Skia so that they're not replayed for
+ // printing operations. See crbug.com/1003114
DCHECK(IsValid());
if (color_params_.GetOpacityMode() == kOpaque)
- Canvas()->clear(SK_ColorBLACK);
+ GetSkSurface()->getCanvas()->clear(SK_ColorBLACK);
else
- Canvas()->clear(SK_ColorTRANSPARENT);
+ GetSkSurface()->getCanvas()->clear(SK_ColorTRANSPARENT);
}
uint32_t CanvasResourceProvider::ContentUniqueID() const {
@@ -1373,7 +1231,6 @@ scoped_refptr<CanvasResource> CanvasResourceProvider::CreateResource() {
}
cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCacheRGBA8() {
-
if (use_hardware_decode_cache()) {
return context_provider_wrapper_->ContextProvider()->ImageDecodeCache(
kN32_SkColorType);
@@ -1383,7 +1240,6 @@ cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCacheRGBA8() {
}
cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCacheF16() {
-
if (use_hardware_decode_cache()) {
return context_provider_wrapper_->ContextProvider()->ImageDecodeCache(
kRGBA_F16_SkColorType);
@@ -1420,6 +1276,11 @@ void CanvasResourceProvider::OnDestroyResource() {
--num_inflight_resources_;
}
+uint64_t CanvasResourceProvider::GetIdentifiabilityDigest() {
+ FlushCanvas();
+ return identifiability_paint_op_digest_.digest();
+}
+
scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
if (canvas_resources_.IsEmpty()) {
canvas_resources_.push_back(CreateResource());
@@ -1497,4 +1358,41 @@ bool CanvasResourceProvider::HasRecordedDrawOps() const {
return recorder_ && recorder_->ListHasDrawOps();
}
+size_t CanvasResourceProvider::ComputeSurfaceSize() const {
+ if (!surface_)
+ return 0;
+
+ SkImageInfo info = surface_->imageInfo();
+ return info.computeByteSize(info.minRowBytes());
+}
+
+void CanvasResourceProvider::OnMemoryDump(
+ base::trace_event::ProcessMemoryDump* pmd) {
+ if (!surface_)
+ return;
+
+ std::string dump_name =
+ base::StringPrintf("canvas/ResourceProvider/SkSurface/0x%" PRIXPTR,
+ reinterpret_cast<uintptr_t>(surface_.get()));
+ auto* dump = pmd->CreateAllocatorDump(dump_name);
+
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ ComputeSurfaceSize());
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+ base::trace_event::MemoryAllocatorDump::kUnitsObjects, 1);
+
+ // SkiaMemoryDumpProvider reports only sk_glyph_cache and sk_resource_cache.
+ // So the SkSurface is suballocation of malloc, not SkiaDumpProvider.
+ if (const char* system_allocator_name =
+ base::trace_event::MemoryDumpManager::GetInstance()
+ ->system_allocator_pool_name()) {
+ pmd->AddSuballocation(dump->guid(), system_allocator_name);
+ }
+}
+
+size_t CanvasResourceProvider::GetSize() const {
+ return ComputeSurfaceSize();
+}
+
} // 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 29e0b2f141d..ef6c6e27c6c 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
@@ -9,8 +9,10 @@
#include "cc/raster/playback_image_provider.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
+#include "third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.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/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h"
#include "third_party/skia/include/core/SkSurface.h"
class GrContext;
@@ -55,35 +57,11 @@ class WebGraphicsContext3DProviderWrapper;
// 3) Call Snapshot() to acquire a bitmap with the rendered image in it.
class PLATFORM_EXPORT CanvasResourceProvider
- : public WebGraphicsContext3DProviderWrapper::DestructionObserver {
+ : public WebGraphicsContext3DProviderWrapper::DestructionObserver,
+ public CanvasMemoryDumpClient {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
- // TODO(juanmihd@ bug/1035589) ResourceUsage will be removed soon, try
- // avoiding using this.
- enum class ResourceUsage {
- kSoftwareResourceUsage = 0, // deprecated
- kSoftwareCompositedResourceUsage = 1, // deprecated
- kAcceleratedResourceUsage = 2, // deprecated
- kAcceleratedCompositedResourceUsage = 3,
- kAcceleratedDirect2DResourceUsage = 4,
- kAcceleratedDirect3DResourceUsage = 5,
- kSoftwareCompositedDirect2DResourceUsage = 6, // deprecated
- kMaxValue = kSoftwareCompositedDirect2DResourceUsage,
- };
-
- // Bitmask of allowed presentation modes.
- enum : uint8_t {
- // GPU Texture or shared memory bitmap
- kDefaultPresentationMode = 0,
- // Allow CHROMIUM_image gl extension
- kAllowImageChromiumPresentationMode = 1 << 0,
- // Allow swap chains (only on Windows)
- kAllowSwapChainPresentationMode = 1 << 1,
- };
-
- // These values are persisted to logs. Entries should not be renumbered and
- // numeric values should never be reused.
enum ResourceProviderType {
kTexture = 0,
kBitmap = 1,
@@ -102,7 +80,8 @@ class PLATFORM_EXPORT CanvasResourceProvider
base::RepeatingCallback<void(cc::PaintCanvas*)>;
// TODO(juanmihd@ bug/1078518) Check whether SkFilterQuality is needed in all
- // of this, or just call setFilterQuality explicitly.
+ // these Create methods below, or just call setFilterQuality explicitly.
+
static std::unique_ptr<CanvasResourceProvider> CreateBitmapProvider(
const IntSize&,
SkFilterQuality,
@@ -110,18 +89,10 @@ class PLATFORM_EXPORT CanvasResourceProvider
static std::unique_ptr<CanvasResourceProvider> CreateSharedBitmapProvider(
const IntSize&,
- base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
SkFilterQuality,
const CanvasColorParams&,
base::WeakPtr<CanvasResourceDispatcher>);
- // 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&,
base::WeakPtr<WebGraphicsContext3DProviderWrapper>,
@@ -129,8 +100,7 @@ class PLATFORM_EXPORT CanvasResourceProvider
const CanvasColorParams&,
bool is_origin_top_left,
RasterMode raster_mode,
- uint32_t shared_image_usage_flags,
- unsigned msaa_sample_count = 0u);
+ uint32_t shared_image_usage_flags);
static std::unique_ptr<CanvasResourceProvider> CreatePassThroughProvider(
const IntSize&,
@@ -140,17 +110,13 @@ class PLATFORM_EXPORT CanvasResourceProvider
bool is_origin_top_left,
base::WeakPtr<CanvasResourceDispatcher>);
- // TODO(juanmihd): Clean up creation methods/usage. See crbug.com/1035589.
- static std::unique_ptr<CanvasResourceProvider> Create(
+ static std::unique_ptr<CanvasResourceProvider> CreateSwapChainProvider(
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,
+ base::WeakPtr<CanvasResourceDispatcher>);
// Use Snapshot() for capturing a frame that is intended to be displayed via
// the compositor. Cases that are destined to be transferred via a
@@ -205,11 +171,11 @@ class PLATFORM_EXPORT CanvasResourceProvider
SkSurface* GetSkSurface() const;
bool IsGpuContextLost() const;
- bool WritePixels(const SkImageInfo& orig_info,
- const void* pixels,
- size_t row_bytes,
- int x,
- int y);
+ virtual bool WritePixels(const SkImageInfo& orig_info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y);
virtual gpu::Mailbox GetBackingMailboxForOverwrite(
MailboxSyncMode sync_mode) {
@@ -246,6 +212,10 @@ class PLATFORM_EXPORT CanvasResourceProvider
void OnDestroyResource();
+ // Returns the identifiability digest computed from the set of PaintOps
+ // flushed from FlushCanvas().
+ uint64_t GetIdentifiabilityDigest();
+
protected:
class CanvasImageProvider;
@@ -255,7 +225,6 @@ class PLATFORM_EXPORT CanvasResourceProvider
base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() {
return context_provider_wrapper_;
}
- unsigned GetMSAASampleCount() const { return msaa_sample_count_; }
GrSurfaceOrigin GetGrSurfaceOrigin() const {
return is_origin_top_left_ ? kTopLeft_GrSurfaceOrigin
: kBottomLeft_GrSurfaceOrigin;
@@ -266,7 +235,6 @@ class PLATFORM_EXPORT CanvasResourceProvider
CanvasResourceProvider(const ResourceProviderType&,
const IntSize&,
- unsigned msaa_sample_count,
SkFilterQuality,
const CanvasColorParams&,
bool is_origin_top_left,
@@ -289,6 +257,7 @@ class PLATFORM_EXPORT CanvasResourceProvider
private:
virtual sk_sp<SkSurface> CreateSkSurface() const = 0;
virtual scoped_refptr<CanvasResource> CreateResource();
+ virtual bool UseOopRasterization() { return false; }
bool use_hardware_decode_cache() const {
return IsAccelerated() && context_provider_wrapper_;
}
@@ -296,6 +265,10 @@ class PLATFORM_EXPORT CanvasResourceProvider
// provider.
virtual void WillDraw() {}
+ size_t ComputeSurfaceSize() const;
+ void OnMemoryDump(base::trace_event::ProcessMemoryDump*) override;
+ size_t GetSize() const override;
+
cc::ImageDecodeCache* ImageDecodeCacheRGBA8();
cc::ImageDecodeCache* ImageDecodeCacheF16();
void EnsureSkiaCanvas();
@@ -304,7 +277,6 @@ class PLATFORM_EXPORT CanvasResourceProvider
base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher_;
const IntSize size_;
- const unsigned msaa_sample_count_;
SkFilterQuality filter_quality_;
const CanvasColorParams color_params_;
const bool is_origin_top_left_;
@@ -336,6 +308,10 @@ class PLATFORM_EXPORT CanvasResourceProvider
RestoreMatrixClipStackCb restore_clip_stack_callback_;
+ // For identifiability metrics -- PaintOps are serialized so that digests can
+ // be calculated using hashes of the serialized output.
+ IdentifiabilityPaintOpDigest identifiability_paint_op_digest_;
+
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 c3e9f8547ed..114cd185663 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
@@ -78,13 +78,14 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderAcceleratedOverlay) {
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::kAcceleratedDirect2DResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kMedium_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ const uint32_t shared_image_usage_flags =
+ gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT |
+ gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
+
+ auto provider = CanvasResourceProvider::CreateSharedImageProvider(
+ kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, RasterMode::kGPU,
+ shared_image_usage_flags);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
@@ -111,7 +112,7 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderTexture) {
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams,
- true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU,
+ true /*is_origin_top_left*/, RasterMode::kGPU,
0u /*shared_image_usage_flags*/);
EXPECT_EQ(provider->Size(), kSize);
@@ -140,7 +141,7 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderUnacceleratedOverlay) {
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams,
- true /* is_origin_top_left */, CanvasResourceProvider::RasterMode::kCPU,
+ true /* is_origin_top_left */, RasterMode::kCPU,
shared_image_usage_flags);
EXPECT_EQ(provider->Size(), kSize);
@@ -166,14 +167,13 @@ TEST_F(CanvasResourceProviderTest,
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::
- kAcceleratedCompositedResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kMedium_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ const uint32_t shared_image_usage_flags =
+ gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+ auto provider = CanvasResourceProvider::CreateSharedImageProvider(
+ kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, RasterMode::kGPU,
+ shared_image_usage_flags);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
@@ -222,14 +222,14 @@ TEST_F(CanvasResourceProviderTest,
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::
- kAcceleratedCompositedResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kMedium_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ const uint32_t shared_image_usage_flags =
+ gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+ auto provider = CanvasResourceProvider::CreateSharedImageProvider(
+ kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, RasterMode::kGPU,
+ shared_image_usage_flags);
+
ASSERT_TRUE(provider->IsValid());
// Same resource returned until the canvas is updated.
@@ -270,14 +270,14 @@ TEST_F(CanvasResourceProviderTest,
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::
- kAcceleratedCompositedResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kMedium_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ const uint32_t shared_image_usage_flags =
+ gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
+
+ auto provider = CanvasResourceProvider::CreateSharedImageProvider(
+ kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, RasterMode::kGPU,
+ shared_image_usage_flags);
+
ASSERT_TRUE(provider->IsValid());
// Disabling copy-on-write forces a copy each time the resource is queried.
@@ -321,7 +321,7 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedBitmap) {
1 /* placeholder_canvas_id */, kSize);
auto provider = CanvasResourceProvider::CreateSharedBitmapProvider(
- kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams,
+ kSize, kLow_SkFilterQuality, kColorParams,
resource_dispatcher.GetWeakPtr());
EXPECT_EQ(provider->Size(), kSize);
@@ -346,13 +346,14 @@ TEST_F(CanvasResourceProviderTest,
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::kAcceleratedDirect2DResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kLow_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ const uint32_t shared_image_usage_flags =
+ gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT |
+ gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
+
+ auto provider = CanvasResourceProvider::CreateSharedImageProvider(
+ kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, RasterMode::kGPU,
+ shared_image_usage_flags);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
@@ -378,13 +379,9 @@ TEST_F(CanvasResourceProviderTest,
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::kAcceleratedDirect3DResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kLow_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowImageChromiumPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ auto provider = CanvasResourceProvider::CreatePassThroughProvider(
+ kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, nullptr /* resource_dispatcher */);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
@@ -414,48 +411,6 @@ TEST_F(CanvasResourceProviderTest,
EXPECT_EQ(provider->NewOrRecycledResource(), resource);
}
-// Verifies that Accelerated Direct 3D resources are backed by SharedImages.
-// https://crbug.com/985366
-TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect3DTexture) {
- const IntSize kSize(10, 10);
- const CanvasColorParams kColorParams(
- CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
- kNonOpaque);
-
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::kAcceleratedDirect3DResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kLow_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kDefaultPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
-
- EXPECT_EQ(provider->Size(), kSize);
- EXPECT_TRUE(provider->IsValid());
- EXPECT_TRUE(provider->IsAccelerated());
- EXPECT_TRUE(provider->SupportsDirectCompositing());
- EXPECT_FALSE(provider->SupportsSingleBuffering());
- EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
- // 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());
-
- EXPECT_FALSE(provider->IsSingleBuffered());
- provider->TryEnableSingleBuffering();
- EXPECT_FALSE(provider->IsSingleBuffered());
-
- auto resource = provider->ProduceCanvasResource();
- viz::TransferableResource transferable_resource;
- std::unique_ptr<viz::SingleReleaseCallback> callback;
- resource->PrepareTransferableResource(&transferable_resource, &callback,
- kOrderingBarrier);
- EXPECT_TRUE(transferable_resource.mailbox_holder.mailbox.IsSharedImage());
- EXPECT_FALSE(transferable_resource.is_overlay_candidate);
- callback->Run(gpu::SyncToken(), true /* is_lost */);
-}
-
TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize_Bitmap) {
const CanvasColorParams kColorParams(
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
@@ -483,91 +438,67 @@ TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize_SharedImage) {
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*/);
+ 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*/);
+ 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*/);
+ 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) {
+TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize_SwapChain) {
const CanvasColorParams kColorParams(
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
+ auto provider = CanvasResourceProvider::CreateSwapChainProvider(
+ IntSize(kMaxTextureSize - 1, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
+ EXPECT_TRUE(provider->SupportsDirectCompositing());
+ provider = CanvasResourceProvider::CreateSwapChainProvider(
+ IntSize(kMaxTextureSize, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
+ EXPECT_TRUE(provider->SupportsDirectCompositing());
+ provider = CanvasResourceProvider::CreateSwapChainProvider(
+ IntSize(kMaxTextureSize + 1, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
- for (int i = 0;
- i < static_cast<int>(CanvasResourceProvider::ResourceUsage::kMaxValue);
- ++i) {
- 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:
- continue;
- case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage:
- continue;
- case CanvasResourceProvider::ResourceUsage::
- kSoftwareCompositedResourceUsage:
- continue;
- case CanvasResourceProvider::ResourceUsage::
- kSoftwareCompositedDirect2DResourceUsage:
- FALLTHROUGH;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedCompositedResourceUsage:
- FALLTHROUGH;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedDirect2DResourceUsage:
- FALLTHROUGH;
- case CanvasResourceProvider::ResourceUsage::
- kAcceleratedDirect3DResourceUsage:
- should_support_compositing = true;
- break;
- }
-
- 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 */);
-
- 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 */);
-
- EXPECT_FALSE(provider->SupportsDirectCompositing());
- }
+ // The CanvasResourceProvider for SwapChain 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_PassThrough) {
+ const CanvasColorParams kColorParams(
+ CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
+ kNonOpaque);
+ auto provider = CanvasResourceProvider::CreatePassThroughProvider(
+ IntSize(kMaxTextureSize - 1, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
+ EXPECT_TRUE(provider->SupportsDirectCompositing());
+ provider = CanvasResourceProvider::CreatePassThroughProvider(
+ IntSize(kMaxTextureSize, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
+ EXPECT_TRUE(provider->SupportsDirectCompositing());
+ provider = CanvasResourceProvider::CreatePassThroughProvider(
+ IntSize(kMaxTextureSize + 1, kMaxTextureSize), context_provider_wrapper_,
+ kLow_SkFilterQuality, kColorParams, true /* is_origin_top_left */,
+ nullptr /* resource_dispatcher */);
+ // The CanvasResourceProvider for PassThrough should not be created or valid
+ // if the texture size is greater than the maximum value
+ EXPECT_TRUE(!provider || !provider->IsValid());
}
TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect2DSwapChain) {
@@ -576,27 +507,21 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect2DSwapChain) {
CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
kNonOpaque);
- auto provider = CanvasResourceProvider::Create(
- kSize,
- CanvasResourceProvider::ResourceUsage::kAcceleratedDirect2DResourceUsage,
- context_provider_wrapper_, 0 /* msaa_sample_count */,
- kLow_SkFilterQuality, kColorParams,
- CanvasResourceProvider::kAllowSwapChainPresentationMode,
- nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+ auto provider = CanvasResourceProvider::CreateSwapChainProvider(
+ kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams,
+ true /* is_origin_top_left */, nullptr /* resource_dispatcher */);
+ ASSERT_TRUE(provider);
EXPECT_EQ(provider->Size(), kSize);
EXPECT_TRUE(provider->IsValid());
EXPECT_TRUE(provider->IsAccelerated());
EXPECT_TRUE(provider->SupportsDirectCompositing());
EXPECT_TRUE(provider->SupportsSingleBuffering());
+ EXPECT_TRUE(provider->IsSingleBuffered());
EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace());
EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat());
EXPECT_EQ(provider->ColorParams().GetOpacityMode(),
kColorParams.GetOpacityMode());
-
- EXPECT_FALSE(provider->IsSingleBuffered());
- provider->TryEnableSingleBuffering();
- EXPECT_TRUE(provider->IsSingleBuffered());
}
} // namespace blink
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 20929011633..af96ca204fd 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/graphics/color_correction_test_utils.h"
+#include "base/notreached.h"
#include "base/sys_byteorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/third_party/skcms/skcms.h"
@@ -67,33 +68,6 @@ sk_sp<SkColorSpace> ColorCorrectionTestUtils::ColorSpinSkColorSpace() {
return SkColorSpace::Make(colorspin_profile);
}
-sk_sp<SkColorSpace>
-ColorCorrectionTestUtils::ColorSpaceConversionToSkColorSpace(
- ColorSpaceConversion conversion) {
- if (conversion == kColorSpaceConversion_Default ||
- conversion == kColorSpaceConversion_SRGB) {
- return SkColorSpace::MakeSRGB();
- }
- if (conversion == kColorSpaceConversion_LinearRGB)
- return SkColorSpace::MakeSRGBLinear();
- if (conversion == kColorSpaceConversion_P3) {
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
- SkNamedGamut::kDCIP3);
- }
- if (conversion == kColorSpaceConversion_Rec2020) {
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear,
- SkNamedGamut::kRec2020);
- }
- return nullptr;
-}
-
-String ColorCorrectionTestUtils::ColorSpaceConversionToString(
- ColorSpaceConversion color_space_conversion) {
- static const Vector<String> kConversions = {
- "none", "default", "preserve", "srgb", "linear-rgb", "p3", "rec2020"};
- return kConversions[static_cast<uint8_t>(color_space_conversion)];
-}
-
void ColorCorrectionTestUtils::CompareColorCorrectedPixels(
const void* actual_pixels,
const void* expected_pixels,
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 3323b5d2bf9..8a6313571c5 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
@@ -32,27 +32,12 @@ enum UnpremulRoundTripTolerance {
kUnpremulRoundTripTolerance,
};
-enum ColorSpaceConversion {
- kColorSpaceConversion_None,
- kColorSpaceConversion_Default,
- kColorSpaceConversion_Preserve,
- kColorSpaceConversion_SRGB,
- kColorSpaceConversion_LinearRGB,
- kColorSpaceConversion_P3,
- kColorSpaceConversion_Rec2020,
- kColorSpaceConversion_Last = kColorSpaceConversion_Rec2020,
-};
-
class ColorCorrectionTestUtils {
STATIC_ONLY(ColorCorrectionTestUtils);
public:
// ImageBitmap color space conversion test utils
static sk_sp<SkColorSpace> ColorSpinSkColorSpace();
- static sk_sp<SkColorSpace> ColorSpaceConversionToSkColorSpace(
- ColorSpaceConversion conversion);
- static String ColorSpaceConversionToString(
- ColorSpaceConversion color_space_conversion);
static void CompareColorCorrectedPixels(
const void* actual_pixels,
diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
index adc395431c2..2284c784ac8 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/color_space_gamut.cc
@@ -42,8 +42,8 @@ ColorSpaceGamut GetColorSpaceGamut(const skcms_ICCProfile* color_profile) {
in[1][1] = 255;
in[2][2] = 255;
bool color_converison_successful = skcms_Transform(
- in, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Opaque, color_profile,
- out, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque, &sc_rgb, 3);
+ in, skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul, color_profile,
+ out, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Unpremul, &sc_rgb, 3);
DCHECK(color_converison_successful);
float score = out[0][0] * out[1][1] * out[2][2];
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 9f8c025c661..f34ebcb50a5 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
@@ -58,7 +58,7 @@ TEST_F(ChunkToLayerMapperTest, OneChunkUsingLayerState) {
auto chunk = Chunk(LayerState());
mapper.SwitchToChunk(chunk);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
EXPECT_EQ(IntRect(20, 10, 88, 99),
mapper.MapVisualRect(IntRect(30, 30, 88, 99)));
@@ -72,7 +72,7 @@ TEST_F(ChunkToLayerMapperTest, TwoChunkUsingLayerState) {
mapper.SwitchToChunk(chunk1);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
EXPECT_EQ(IntRect(20, 10, 88, 99),
mapper.MapVisualRect(IntRect(30, 30, 88, 99)));
@@ -80,7 +80,7 @@ TEST_F(ChunkToLayerMapperTest, TwoChunkUsingLayerState) {
mapper.SwitchToChunk(chunk2);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
EXPECT_EQ(IntRect(20, 10, 88, 99),
mapper.MapVisualRect(IntRect(30, 30, 88, 99)));
@@ -99,7 +99,7 @@ TEST_F(ChunkToLayerMapperTest, TwoChunkSameState) {
mapper.SwitchToChunk(chunk1);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- SkMatrix expected_transform = SkMatrix::MakeTrans(-10, -20);
+ SkMatrix expected_transform = SkMatrix::Translate(-10, -20);
expected_transform.preScale(2, 2);
EXPECT_EQ(expected_transform, mapper.Transform());
EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect());
@@ -134,7 +134,7 @@ TEST_F(ChunkToLayerMapperTest, TwoChunkDifferentState) {
mapper.SwitchToChunk(chunk1);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- SkMatrix expected_transform = SkMatrix::MakeTrans(-10, -20);
+ SkMatrix expected_transform = SkMatrix::Translate(-10, -20);
expected_transform.preScale(2, 2);
EXPECT_EQ(expected_transform, mapper.Transform());
EXPECT_EQ(FloatRect(0, -10, 100, 100), mapper.ClipRect().Rect());
@@ -184,12 +184,12 @@ TEST_F(ChunkToLayerMapperTest, SlowPath) {
mapper.SwitchToChunk(chunk1);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
mapper.SwitchToChunk(chunk2);
EXPECT_TRUE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_TRUE(mapper.ClipRect().IsInfinite());
EXPECT_EQ(IntRect(-40, -50, 208, 219),
mapper.MapVisualRect(IntRect(30, 30, 88, 99)));
@@ -197,7 +197,7 @@ TEST_F(ChunkToLayerMapperTest, SlowPath) {
mapper.SwitchToChunk(chunk3);
EXPECT_TRUE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_TRUE(mapper.ClipRect().IsInfinite());
EXPECT_EQ(IntRect(-40, -50, 208, 219),
mapper.MapVisualRect(IntRect(30, 30, 88, 99)));
@@ -205,12 +205,12 @@ TEST_F(ChunkToLayerMapperTest, SlowPath) {
mapper.SwitchToChunk(chunk4);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
mapper.SwitchToChunk(chunk5);
EXPECT_FALSE(HasFilterThatMovesPixels(mapper));
- EXPECT_EQ(SkMatrix::MakeTrans(-10, -20), mapper.Transform());
+ EXPECT_EQ(SkMatrix::Translate(-10, -20), mapper.Transform());
EXPECT_EQ(FloatClipRect(), mapper.ClipRect());
}
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 1e7e0c1f113..53a05526dce 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
@@ -115,9 +115,10 @@ scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer(
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());
+ // Backdrop effects and filters require the layer to be drawable even if
+ // the layer draws nothing.
+ layer_state.Effect().HasBackdropEffect() ||
+ !layer_state.Effect().Filter().IsEmpty());
auto safe_opaque_background_color =
paint_artifact->SafeOpaqueBackgroundColor(paint_chunks);
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 b4b306f3472..52e787acf70 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
@@ -48,6 +48,8 @@ std::unique_ptr<JSONObject> CCLayerAsJSON(const cc::Layer* layer,
if (layer->contents_opaque())
json->SetBoolean("contentsOpaque", true);
+ else if (layer->contents_opaque_for_text())
+ json->SetBoolean("contentsOpaqueForText", true);
if (!layer->DrawsContent())
json->SetBoolean("drawsContent", false);
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 48bc91937b3..70cc31c7e63 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
@@ -125,7 +125,6 @@ std::unique_ptr<JSONObject> PaintArtifactCompositor::GetLayersAsJSON(
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
@@ -856,7 +855,6 @@ static bool IsCompositedScrollbar(const DisplayItem& item) {
void PaintArtifactCompositor::LayerizeGroup(
const PaintArtifact& paint_artifact,
- const Settings& settings,
const EffectPaintPropertyNode& current_group,
Vector<PaintChunk>::const_iterator& chunk_it) {
// Skip paint chunks that are effectively invisible due to opacity and don't
@@ -921,7 +919,7 @@ void PaintArtifactCompositor::LayerizeGroup(
// Case C: The following chunks belong to a subgroup. Process them by
// a recursion call.
wtf_size_t first_layer_in_subgroup = pending_layers_.size();
- LayerizeGroup(paint_artifact, settings, *unaliased_subgroup, chunk_it);
+ LayerizeGroup(paint_artifact, *unaliased_subgroup, chunk_it);
// The above LayerizeGroup generated new layers in pending_layers_
// [first_layer_in_subgroup .. pending_layers.size() - 1]. If it
// generated 2 or more layer that we already know can't be merged
@@ -959,14 +957,12 @@ void PaintArtifactCompositor::LayerizeGroup(
}
void PaintArtifactCompositor::CollectPendingLayers(
- const PaintArtifact& paint_artifact,
- const Settings& settings) {
+ const PaintArtifact& paint_artifact) {
Vector<PaintChunk>::const_iterator cursor =
paint_artifact.PaintChunks().begin();
// Shrink, but do not release the backing. Re-use it from the last frame.
pending_layers_.Shrink(0);
- LayerizeGroup(paint_artifact, settings, EffectPaintPropertyNode::Root(),
- cursor);
+ LayerizeGroup(paint_artifact, EffectPaintPropertyNode::Root(), cursor);
DCHECK_EQ(paint_artifact.PaintChunks().end(), cursor);
pending_layers_.ShrinkToReasonableCapacity();
}
@@ -981,6 +977,9 @@ void SynthesizedClip::UpdateLayer(bool needs_layer,
if (!layer_) {
layer_ = cc::PictureLayer::Create(this);
layer_->SetIsDrawable(true);
+ // The clip layer must be hit testable because the compositor may not know
+ // whether the hit test is clipped out.
+ // See: cc::LayerTreeHostImpl::IsInitialScrollHitTestReliable().
layer_->SetHitTestable(true);
}
@@ -1224,7 +1223,6 @@ void PaintArtifactCompositor::DecompositeTransforms(
void PaintArtifactCompositor::Update(
scoped_refptr<const PaintArtifact> paint_artifact,
const ViewportProperties& viewport_properties,
- const Settings& settings,
const Vector<const TransformPaintPropertyNode*>& scroll_translation_nodes) {
DCHECK(scroll_translation_nodes.IsEmpty() ||
RuntimeEnabledFeatures::ScrollUnificationEnabled());
@@ -1246,7 +1244,7 @@ void PaintArtifactCompositor::Update(
PropertyTreeManager property_tree_manager(*this, *host->property_trees(),
*root_layer_, layer_list_builder,
g_s_property_tree_sequence_number);
- CollectPendingLayers(*paint_artifact, settings);
+ CollectPendingLayers(*paint_artifact);
UpdateCompositorViewportProperties(viewport_properties, property_tree_manager,
host);
@@ -1615,9 +1613,6 @@ CompositingReasons PaintArtifactCompositor::GetCompositingReasons(
const PaintArtifact& paint_artifact) const {
DCHECK(layer_debug_info_enabled_);
- if (layer.compositing_type == PendingLayer::kOverlap)
- return CompositingReason::kOverlap;
-
if (layer.compositing_type == PendingLayer::kRequiresOwnLayer) {
const auto& first_chunk = layer.FirstPaintChunk(paint_artifact);
if (IsCompositedScrollHitTest(first_chunk))
@@ -1645,12 +1640,32 @@ CompositingReasons PaintArtifactCompositor::GetCompositingReasons(
&previous_layer->property_tree_state.Transform()) {
reasons |= layer.property_tree_state.Transform()
.DirectCompositingReasonsForDebugging();
+ if (!layer.property_tree_state.Transform().BackfaceVisibilitySameAsParent())
+ reasons |= CompositingReason::kBackfaceVisibilityHidden;
}
+
if (!previous_layer || &layer.property_tree_state.Effect() !=
&previous_layer->property_tree_state.Effect()) {
- reasons |= layer.property_tree_state.Effect()
- .DirectCompositingReasonsForDebugging();
+ const auto& effect = layer.property_tree_state.Effect();
+ if (effect.HasDirectCompositingReasons())
+ reasons |= effect.DirectCompositingReasonsForDebugging();
+ if (reasons == CompositingReason::kNone &&
+ layer.compositing_type == PendingLayer::kOther) {
+ if (effect.Opacity() != 1.0f)
+ reasons |= CompositingReason::kOpacityWithCompositedDescendants;
+ if (!effect.Filter().IsEmpty())
+ reasons |= CompositingReason::kFilterWithCompositedDescendants;
+ if (effect.BlendMode() == SkBlendMode::kDstIn)
+ reasons |= CompositingReason::kMaskWithCompositedDescendants;
+ else if (effect.BlendMode() != SkBlendMode::kSrcOver)
+ reasons |= CompositingReason::kBlendingWithCompositedDescendants;
+ }
}
+
+ if (reasons == CompositingReason::kNone &&
+ layer.compositing_type == PendingLayer::kOverlap)
+ reasons = CompositingReason::kOverlap;
+
return reasons;
}
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 1d3c8c16e71..627d8b85de9 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
@@ -136,10 +136,6 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
const TransformPaintPropertyNode* outer_scroll_translation = nullptr;
};
- struct Settings {
- bool prefer_compositing_to_lcd_text = false;
- };
-
// Updates the layer tree to match the provided paint artifact.
//
// Populates |animation_element_ids| with the CompositorElementId of all
@@ -150,7 +146,6 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
// property tree.
void Update(scoped_refptr<const PaintArtifact>,
const ViewportProperties& viewport_properties,
- const Settings& settings,
const Vector<const TransformPaintPropertyNode*>&
scroll_translation_nodes);
@@ -287,7 +282,7 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
// Collects the PaintChunks into groups which will end up in the same
// cc layer. This is the entry point of the layerization algorithm.
- void CollectPendingLayers(const PaintArtifact&, const Settings& settings);
+ void CollectPendingLayers(const PaintArtifact&);
// This is the internal recursion of collectPendingLayers. This function
// loops over the list of paint chunks, scoped by an isolated group
@@ -307,7 +302,6 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
// overlap with other chunks in the parent group, if grouping requirement
// can be satisfied (and the effect node has no direct reason).
void LayerizeGroup(const PaintArtifact&,
- const Settings& settings,
const EffectPaintPropertyNode&,
Vector<PaintChunk>::const_iterator& chunk_cursor);
static bool MightOverlap(const PendingLayer&, const PendingLayer&);
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 62e66795e0d..c0fe836c71f 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
@@ -142,16 +142,14 @@ class PaintArtifactCompositorTest : public testing::Test,
}
using ViewportProperties = PaintArtifactCompositor::ViewportProperties;
- using Settings = PaintArtifactCompositor::Settings;
void Update(
scoped_refptr<const PaintArtifact> artifact,
const ViewportProperties& viewport_properties = ViewportProperties(),
- const Settings& settings = Settings(),
const WTF::Vector<const TransformPaintPropertyNode*>&
scroll_translation_nodes = {}) {
paint_artifact_compositor_->SetNeedsUpdate();
- paint_artifact_compositor_->Update(artifact, viewport_properties, settings,
+ paint_artifact_compositor_->Update(artifact, viewport_properties,
scroll_translation_nodes);
layer_tree_->layer_tree_host()->LayoutAndUpdateLayers();
}
@@ -3702,7 +3700,7 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) {
CompositorFilterOperations non_trivial_filter;
non_trivial_filter.AppendBlurFilter(5);
- auto e1 = CreateFilterEffect(e0(), non_trivial_filter, FloatPoint(),
+ auto e1 = CreateFilterEffect(e0(), non_trivial_filter,
CompositingReason::kActiveFilterAnimation);
TestPaintArtifact artifact;
@@ -3870,8 +3868,15 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
auto t1 = Create2DTranslation(t0(), 10, 20);
CompositorFilterOperations blur_filter;
blur_filter.AppendBlurFilter(5);
- auto e1 = CreateBackdropFilterEffect(e0(), *t1, c2.get(), blur_filter,
- FloatPoint());
+ EffectPaintPropertyNode::State state;
+ state.local_transform_space = t1.get();
+ state.output_clip = c2.get();
+ state.backdrop_filter.AppendBlurFilter(5);
+ state.direct_compositing_reasons = CompositingReason::kBackdropFilter;
+ state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
+ NewUniqueObjectId(), CompositorElementIdNamespace::kPrimary);
+ state.opacity = 0.5;
+ auto e1 = EffectPaintPropertyNode::Create(e0(), std::move(state));
TestPaintArtifact artifact;
artifact.Chunk(*t1, *c1, e0())
@@ -3894,9 +3899,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
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.
+ // Three synthesized layers, two of which are null 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.
+ ASSERT_EQ(3u, SynthesizedClipLayerCount());
EXPECT_FALSE(SynthesizedClipLayerAt(0));
EXPECT_EQ(clip_mask1, SynthesizedClipLayerAt(1));
EXPECT_FALSE(SynthesizedClipLayerAt(2));
@@ -3916,6 +3922,7 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
EXPECT_EQ(c1_id, mask_isolation_0.clip_id);
EXPECT_TRUE(mask_isolation_0.backdrop_filters.IsEmpty());
EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner);
+ EXPECT_EQ(1.0f, mask_isolation_0.opacity);
EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
mask_isolation_0.rounded_corner_bounds);
@@ -3928,6 +3935,7 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
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(1.0f, cc_e1.opacity);
EXPECT_EQ(t1_id, cc_e1.transform_id);
EXPECT_EQ(c2_id, cc_e1.clip_id);
EXPECT_FALSE(cc_e1.backdrop_mask_element_id);
@@ -3941,6 +3949,8 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
EXPECT_EQ(c2_id, mask_isolation_1.clip_id);
EXPECT_FALSE(mask_isolation_1.backdrop_filters.IsEmpty());
EXPECT_FALSE(mask_isolation_1.is_fast_rounded_corner);
+ // Opacity should also be moved to mask_isolation_1.
+ EXPECT_EQ(0.5f, mask_isolation_1.opacity);
EXPECT_EQ(gfx::RRectF(), mask_isolation_1.rounded_corner_bounds);
EXPECT_EQ(t0_id, clip_mask1->transform_tree_index());
@@ -3967,10 +3977,82 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) {
EXPECT_EQ(c1_id, mask_isolation_2.clip_id);
EXPECT_TRUE(mask_isolation_2.backdrop_filters.IsEmpty());
EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner);
+ EXPECT_EQ(1.0f, mask_isolation_2.opacity);
EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
mask_isolation_2.rounded_corner_bounds);
}
+TEST_P(PaintArtifactCompositorTest, SynthesizedClipMultipleNonBackdropEffects) {
+ // This tests the case that multiple non-backdrop effects can share the
+ // synthesized mask.
+ FloatSize corner(5, 5);
+ FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner,
+ corner);
+ auto c1 = CreateClip(c0(), t0(), rrect);
+ auto c2 = CreateClip(*c1, t0(), FloatRoundedRect(60, 60, 200, 100));
+
+ auto e1 =
+ CreateOpacityEffect(e0(), t0(), c2.get(), 0.5, CompositingReason::kAll);
+ auto e2 =
+ CreateOpacityEffect(e0(), t0(), c1.get(), 0.75, CompositingReason::kAll);
+
+ TestPaintArtifact artifact;
+ artifact.Chunk(t0(), *c2, *e1)
+ .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), *c1, *e2)
+ .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack);
+ artifact.Chunk(t0(), c0(), e0())
+ .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack);
+ Update(artifact.Build());
+
+ // Expectation in effect stack diagram:
+ // content0 content1 content2
+ // [ e1 ][ e2 ]
+ // [ mask_isolation ]
+ // [ e0 ]
+ // Three content layers.
+ ASSERT_EQ(3u, LayerCount());
+ const cc::Layer* content0 = LayerAt(0);
+ const cc::Layer* content1 = LayerAt(1);
+ const cc::Layer* content2 = LayerAt(2);
+
+ // One synthesized layer, which is null because it uses fast rounded corners.
+ ASSERT_EQ(1u, SynthesizedClipLayerCount());
+ EXPECT_FALSE(SynthesizedClipLayerAt(0));
+
+ int c2_id = content0->clip_tree_index();
+ const cc::ClipNode& cc_c2 = *GetPropertyTrees().clip_tree.Node(c2_id);
+ int e1_id = content0->effect_tree_index();
+ const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id);
+ int c1_id = content1->clip_tree_index();
+ const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id);
+ int e2_id = content1->effect_tree_index();
+ const cc::EffectNode& cc_e2 = *GetPropertyTrees().effect_tree.Node(e2_id);
+ int mask_isolation_id = cc_e1.parent_id;
+ const cc::EffectNode& mask_isolation =
+ *GetPropertyTrees().effect_tree.Node(mask_isolation_id);
+
+ EXPECT_EQ(c2_id, cc_e1.clip_id);
+ EXPECT_EQ(0.5f, cc_e1.opacity);
+ EXPECT_EQ(gfx::RectF(60, 60, 200, 100), cc_c2.clip);
+ ASSERT_EQ(c1_id, cc_c2.parent_id);
+
+ EXPECT_EQ(c1_id, cc_e2.clip_id);
+ EXPECT_EQ(mask_isolation_id, cc_e2.parent_id);
+ EXPECT_EQ(0.75f, cc_e2.opacity);
+ EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip);
+ ASSERT_EQ(c0_id, cc_c1.parent_id);
+
+ ASSERT_EQ(e0_id, mask_isolation.parent_id);
+ EXPECT_EQ(c1_id, mask_isolation.clip_id);
+ EXPECT_TRUE(mask_isolation.is_fast_rounded_corner);
+ EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5),
+ mask_isolation.rounded_corner_bounds);
+
+ EXPECT_EQ(c0_id, content2->clip_tree_index());
+ EXPECT_EQ(e0_id, content2->effect_tree_index());
+}
+
TEST_P(PaintArtifactCompositorTest, WillBeRemovedFromFrame) {
auto effect = CreateSampleEffectNodeWithElementId();
TestPaintArtifact artifact;
@@ -4395,9 +4477,9 @@ TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfacesWithFilterChildren) {
auto opacity = CreateOpacityEffect(e0(), 0.1f);
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto filter1 = CreateFilterEffect(*opacity, filter, FloatPoint(),
+ auto filter1 = CreateFilterEffect(*opacity, filter,
CompositingReason::kActiveFilterAnimation);
- auto filter2 = CreateFilterEffect(*opacity, filter, FloatPoint(),
+ auto filter2 = CreateFilterEffect(*opacity, filter,
CompositingReason::kActiveFilterAnimation);
IntRect r(150, 150, 100, 100);
@@ -4489,7 +4571,7 @@ TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfacesWithBackdropChildren) {
auto a = CreateOpacityEffect(*e, 0.5f);
CompositorFilterOperations blur_filter;
blur_filter.AppendBlurFilter(5);
- auto bd = CreateBackdropFilterEffect(*a, blur_filter, FloatPoint());
+ auto bd = CreateBackdropFilterEffect(*a, blur_filter);
TestPaintArtifact artifact;
IntRect r(150, 150, 100, 100);
@@ -4620,7 +4702,7 @@ TEST_P(PaintArtifactCompositorTest,
TEST_P(PaintArtifactCompositorTest, FilterCreatesRenderSurface) {
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto e1 = CreateFilterEffect(e0(), filter, FloatPoint(),
+ auto e1 = CreateFilterEffect(e0(), filter,
CompositingReason::kActiveFilterAnimation);
Update(TestPaintArtifact()
.Chunk(t0(), c0(), *e1)
@@ -4643,7 +4725,7 @@ TEST_P(PaintArtifactCompositorTest, FilterAnimationCreatesRenderSurface) {
TEST_P(PaintArtifactCompositorTest, BackdropFilterCreatesRenderSurface) {
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto e1 = CreateBackdropFilterEffect(e0(), filter, FloatPoint());
+ auto e1 = CreateBackdropFilterEffect(e0(), filter);
Update(TestPaintArtifact()
.Chunk(t0(), c0(), *e1)
.RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite)
@@ -4959,8 +5041,7 @@ TEST_P(PaintArtifactCompositorTest, AddNonCompositedScrollNodes) {
scroll_translation_nodes.push_back(scroll_translation.get());
TestPaintArtifact artifact;
- Update(artifact.Build(), ViewportProperties(), Settings(),
- scroll_translation_nodes);
+ Update(artifact.Build(), ViewportProperties(), scroll_translation_nodes);
auto& scroll_tree = GetPropertyTrees().scroll_tree;
auto* scroll_node = scroll_tree.FindNodeFromElementId(scroll_element_id);
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 7ea7c8ab662..58049a1ae9c 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,10 +158,9 @@ class ConversionContext {
*current_transform_);
}
- void AppendRestore(wtf_size_t n) {
+ void AppendRestore() {
cc_list_.StartPaint();
- while (n--)
- cc_list_.push<cc::RestoreOp>();
+ cc_list_.push<cc::RestoreOp>();
cc_list_.EndPaintOfPairedEnd();
}
@@ -195,7 +194,6 @@ class ConversionContext {
// Remembers the type of paired begin that caused a state to be saved.
// This is for checking integrity of the algorithm.
enum PairedType { kClip, kEffect } type;
- int saved_count;
// These fields are neve nullptr.
const TransformPaintPropertyNode* transform;
@@ -207,7 +205,7 @@ class ConversionContext {
bool has_pre_cap_effect_hierarchy_issue = false;
#endif
};
- void PushState(StateEntry::PairedType, int saved_count);
+ void PushState(StateEntry::PairedType);
void PopState();
Vector<StateEntry> state_stack_;
@@ -262,7 +260,7 @@ ConversionContext::~ConversionContext() {
}
EndTransform();
if (translated_for_layer_offset_)
- AppendRestore(1);
+ AppendRestore();
}
void ConversionContext::TranslateForLayerOffsetOnce() {
@@ -429,7 +427,7 @@ void ConversionContext::StartClip(
}
cc_list_.EndPaintOfPairedBegin();
- PushState(StateEntry::kClip, 1);
+ PushState(StateEntry::kClip);
current_clip_ = &lowest_combined_clip_node;
current_transform_ = &local_transform;
}
@@ -523,7 +521,6 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& effect) {
else
EndClips();
- int saved_count = 0;
size_t save_layer_id = kNotFound;
// Adjust transform first. Though a non-filter effect itself doesn't depend on
@@ -562,15 +559,8 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& effect) {
} else {
save_layer_id = cc_list_.push<cc::SaveLayerAlphaOp>(nullptr, alpha);
}
- saved_count++;
} else {
// Handle filter effect.
- FloatPoint filter_origin = effect.FiltersOrigin();
- if (filter_origin != FloatPoint()) {
- cc_list_.push<cc::SaveOp>();
- cc_list_.push<cc::TranslateOp>(filter_origin.X(), filter_origin.Y());
- saved_count++;
- }
// The size parameter is only used to computed the origin of zoom
// operation, which we never generate.
gfx::SizeF empty;
@@ -578,20 +568,15 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& effect) {
filter_flags.setImageFilter(cc::RenderSurfaceFilters::BuildImageFilter(
effect.Filter().AsCcFilterOperations(), empty));
save_layer_id = cc_list_.push<cc::SaveLayerOp>(nullptr, &filter_flags);
- saved_count++;
- if (filter_origin != FloatPoint())
- cc_list_.push<cc::TranslateOp>(-filter_origin.X(), -filter_origin.Y());
}
cc_list_.EndPaintOfPairedBegin();
- DCHECK_GT(saved_count, 0);
- DCHECK_LE(saved_count, 2);
DCHECK_NE(save_layer_id, kNotFound);
// Adjust state and push previous state onto effect stack.
// TODO(trchen): Change input clip to expansion hint once implemented.
const ClipPaintPropertyNode* input_clip = current_clip_;
- PushState(StateEntry::kEffect, saved_count);
+ PushState(StateEntry::kEffect);
effect_bounds_stack_.emplace_back(
EffectBoundsInfo{save_layer_id, current_transform_});
current_clip_ = input_clip;
@@ -599,7 +584,6 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& 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
@@ -640,14 +624,9 @@ void ConversionContext::EndEffect() {
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 an empty bounds for empty filter to avoid performance issue of
+ // PDF renderer. See crbug.com/740824.
+ cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, bounds);
// We need to propagate the filtered bounds to the parent.
bounds = current_effect_->MapRect(bounds);
}
@@ -671,11 +650,9 @@ void ConversionContext::EndClip() {
PopState();
}
-void ConversionContext::PushState(StateEntry::PairedType type,
- int saved_count) {
- state_stack_.emplace_back(StateEntry{type, saved_count, current_transform_,
- current_clip_, current_effect_,
- previous_transform_});
+void ConversionContext::PushState(StateEntry::PairedType type) {
+ state_stack_.emplace_back(StateEntry{type, current_transform_, current_clip_,
+ current_effect_, previous_transform_});
previous_transform_ = nullptr;
}
@@ -683,7 +660,7 @@ void ConversionContext::PopState() {
DCHECK_EQ(nullptr, previous_transform_);
const auto& previous_state = state_stack_.back();
- AppendRestore(previous_state.saved_count);
+ AppendRestore();
current_transform_ = previous_state.transform;
previous_transform_ = previous_state.previous_transform;
current_clip_ = previous_state.clip;
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 1291b765dde..db75011f889 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
@@ -259,7 +259,7 @@ TEST_P(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) {
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto e2 = CreateFilterEffect(*e1, filter, FloatPoint(60, 60));
+ auto e2 = CreateFilterEffect(*e1, filter);
TestChunks chunks;
chunks.AddChunk(*t2, c0(), *e1, IntRect(0, 0, 50, 50));
chunks.AddChunk(*t1, c0(), *e2, IntRect(20, 20, 70, 70));
@@ -275,29 +275,21 @@ TEST_P(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) {
{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(chunk2.bounds)
+ EXPECT_EFFECT_BOUNDS(70, 70, 70, 70, *output, 4);
// t2^1
EXPECT_TRANSLATE(-t2->Translation2D().Width(), -t2->Translation2D().Height(),
- *output, 9);
+ *output, 6);
}
TEST_P(PaintChunksToCcLayerTest, InterleavedClipEffect) {
@@ -424,7 +416,7 @@ TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2.f));
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto e1 = CreateFilterEffect(e0(), *t1, &c0(), filter, FloatPoint(66, 88));
+ auto e1 = CreateFilterEffect(e0(), *t1, &c0(), filter);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), *e1);
@@ -437,20 +429,15 @@ TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) {
*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);
+ EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 2);
+ EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4);
}
TEST_P(PaintChunksToCcLayerTest, NonRootLayerSimple) {
@@ -1333,7 +1320,7 @@ TEST_P(PaintChunksToCcLayerTest, AllowChunkEscapeLayerNoopEffects) {
TEST_P(PaintChunksToCcLayerTest, EmptyChunkRect) {
CompositorFilterOperations filter;
filter.AppendBlurFilter(5);
- auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter, FloatPoint(0, 0));
+ auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter);
TestChunks chunks;
chunks.AddChunk(nullptr, t0(), c0(), *e1, {0, 0, 0, 0});
@@ -1354,7 +1341,7 @@ TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnEmptyChunk) {
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));
+ auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter);
TestChunks chunks;
chunks.AddEmptyChunk(t0(), c0(), *e1, IntRect(0, 0, 200, 300));
@@ -1363,25 +1350,22 @@ TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnEmptyChunk) {
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);
+ ASSERT_EQ(5u, cc_list->TotalOpCount());
+ // (16 32) is (11, 22) + layer_offset.
+ gfx::Rect expected_visual_rect(6, 12, 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);
+ EXPECT_THAT(*output, PaintRecordMatcher::Make(
+ {cc::PaintOpType::Save,
+ cc::PaintOpType::Translate, // layer offset
+ cc::PaintOpType::SaveLayer, // <e1>
+ cc::PaintOpType::Restore, // </e1>
+ cc::PaintOpType::Restore}));
+ EXPECT_EFFECT_BOUNDS(11, 22, 33, 44, *output, 2);
}
TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnChunkWithDrawingDisplayItem) {
@@ -1390,7 +1374,7 @@ TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnChunkWithDrawingDisplayItem) {
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));
+ auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), *e1, IntRect(5, 10, 200, 300),
IntRect(10, 15, 20, 30));
@@ -1400,17 +1384,17 @@ TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnChunkWithDrawingDisplayItem) {
PaintChunksToCcLayer::ConvertInto(chunks.chunks, PropertyTreeState::Root(),
gfx::Vector2dF(5, 10), FloatSize(),
chunks.items, *cc_list);
- ASSERT_EQ(11u, cc_list->TotalOpCount());
+ ASSERT_EQ(7u, 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);
+ gfx::Rect expected_filter_visual_rect(5, 5, 34, 51);
// 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,
+ EXPECT_EQ(i == 3 ? expected_draw_visual_rect : expected_filter_visual_rect,
cc_list->VisualRectForTesting(i));
}
@@ -1419,16 +1403,13 @@ TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnChunkWithDrawingDisplayItem) {
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}));
+ 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);
+ EXPECT_EFFECT_BOUNDS(10, 15, 34, 51, *output, 2);
}
} // 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 63c80873c9d..08cea3f0789 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
@@ -424,6 +424,8 @@ int PropertyTreeManager::EnsureCompositorTransformNode(
compositor_node.flattens_inherited_transform =
transform_node.FlattensInheritedTransform();
compositor_node.sorting_context_id = transform_node.RenderingContextId();
+ compositor_node.delegates_to_parent_for_backface =
+ transform_node.DelegatesToParentForBackface();
if (transform_node.IsAffectedByOuterViewportBoundsDelta()) {
compositor_node.moved_by_outer_viewport_bounds_delta_y = true;
@@ -620,10 +622,9 @@ void PropertyTreeManager::EmitClipMaskLayer() {
*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);
+ // Now we know the actual mask_isolation.stable_id.
+ // This overrides the stable_id set in PopulateCcEffectNode() if the
+ // backdrop effect was moved up to |mask_isolation|.
mask_isolation->stable_id = mask_isolation_id.GetStableId();
if (!needs_layer)
@@ -873,44 +874,12 @@ bool PropertyTreeManager::SupportsShaderBasedRoundedCorner(
return true;
}
-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(
+int PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
const ClipPaintPropertyNode& target_clip_arg,
const EffectPaintPropertyNode* next_effect) {
const auto* target_clip = &target_clip_arg.Unalias();
- int clip_id = EnsureCompositorClipNode(*target_clip);
- auto backdrop_effect_state = kNoBackdropEffect;
+ int backdrop_effect_clip_id = cc::ClipTree::kInvalidNodeId;
+ bool should_realize_backdrop_effect = false;
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
@@ -922,7 +891,8 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
// effect, in order to define the scope of the backdrop.
SetCurrentEffectRenderSurfaceReason(
cc::RenderSurfaceReason::kBackdropScope);
- backdrop_effect_state = kBackdropEffectToBeSetOnCcEffectNode;
+ should_realize_backdrop_effect = true;
+ backdrop_effect_clip_id = EnsureCompositorClipNode(*target_clip);
} else {
// Exit synthetic effects until there are no more synthesized clips below
// our lowest common ancestor.
@@ -935,7 +905,7 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
// In CompositeAfterPaint this should never happen.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
NOTREACHED();
- return backdrop_effect_state;
+ return cc::EffectTree::kInvalidNodeId;
}
const auto* pre_exit_clip = current_.clip;
CloseCcEffect();
@@ -964,14 +934,16 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
// In CompositeAfterPaint this should never happen.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
NOTREACHED();
- return backdrop_effect_state;
+ return cc::EffectTree::kInvalidNodeId;
}
if (pending_clips.IsEmpty())
- return backdrop_effect_state;
+ return cc::EffectTree::kInvalidNodeId;
+ int cc_effect_id_for_backdrop_effect = cc::EffectTree::kInvalidNodeId;
for (auto i = pending_clips.size(); i--;) {
const auto& pending_clip = pending_clips[i];
+ int clip_id = backdrop_effect_clip_id;
// For a non-trivial clip, the synthetic effect is an isolation to enclose
// only the layers that should be masked by the synthesized clip.
@@ -981,7 +953,8 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
GetEffectTree().Insert(cc::EffectNode(), current_.effect_id));
if (pending_clip.type & CcEffectType::kSyntheticForNonTrivialClip) {
- synthetic_effect.clip_id = clip_id;
+ if (clip_id == cc::ClipTree::kInvalidNodeId)
+ 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().
@@ -1019,8 +992,7 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
// 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());
+ clip_id = EnsureCompositorClipNode(*pending_clip.clip->Parent());
}
if (pending_clip.type & CcEffectType::kSyntheticFor2dAxisAlignment) {
@@ -1029,16 +1001,20 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
}
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
+ if (should_realize_backdrop_effect) {
+ // Move the effect node containing backdrop effects up to the outermost
+ // synthetic effect to ensure the backdrop effects can access the correct
// backdrop.
DCHECK(next_effect);
+ DCHECK_EQ(cc_effect_id_for_backdrop_effect,
+ cc::EffectTree::kInvalidNodeId);
transform = &next_effect->LocalTransformSpace();
- PopulateCcEffectNodeBackdropEffect(synthetic_effect, *next_effect);
- backdrop_effect_state = kBackdropEffectHasSetOnSyntheticEffect;
+ PopulateCcEffectNode(synthetic_effect, *next_effect, clip_id);
+ cc_effect_id_for_backdrop_effect = synthetic_effect.id;
+ should_realize_backdrop_effect = false;
} else {
transform = &pending_clip.clip->LocalTransformSpace();
+ synthetic_effect.clip_id = clip_id;
}
synthetic_effect.transform_id = EnsureCompositorTransformNode(*transform);
@@ -1049,7 +1025,7 @@ PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(
*pending_clip.clip, *transform);
}
- return backdrop_effect_state;
+ return cc_effect_id_for_backdrop_effect;
}
void PropertyTreeManager::BuildEffectNodesRecursively(
@@ -1077,11 +1053,11 @@ void PropertyTreeManager::BuildEffectNodesRecursively(
}
}
- auto backdrop_effect_state = kNoBackdropEffect;
+ int real_effect_node_id = cc::EffectTree::kInvalidNodeId;
int output_clip_id = 0;
const auto* output_clip = SafeUnalias(next_effect.OutputClip());
if (output_clip) {
- backdrop_effect_state =
+ real_effect_node_id =
SynthesizeCcEffectsForClipsIfNeeded(*output_clip, &next_effect);
output_clip_id = EnsureCompositorClipNode(*output_clip);
} else {
@@ -1091,23 +1067,29 @@ void PropertyTreeManager::BuildEffectNodesRecursively(
while (IsCurrentCcEffectSynthetic())
CloseCcEffect();
- 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);
+ auto& effect_node = *GetEffectTree().Node(
+ GetEffectTree().Insert(cc::EffectNode(), current_.effect_id));
+ if (real_effect_node_id == cc::EffectTree::kInvalidNodeId) {
+ real_effect_node_id = effect_node.id;
+ PopulateCcEffectNode(effect_node, next_effect, output_clip_id);
+ } else {
+ // We have used the outermost synthetic effect for |next_effect| in
+ // SynthesizeCcEffectsForClipsIfNeeded(), so |effect_node| is just a dummy
+ // node to mark the end of continuous synthetic effects for |next_effect|.
+ effect_node.clip_id = output_clip_id;
+ effect_node.transform_id =
+ EnsureCompositorTransformNode(next_effect.LocalTransformSpace());
+ effect_node.stable_id = next_effect.GetCompositorElementId().GetStableId();
+ }
if (!has_multiple_groups)
- next_effect.SetCcNodeId(new_sequence_number_, effect_node_id);
-
- PopulateCcEffectNode(effect_node, next_effect, output_clip_id,
- backdrop_effect_state);
+ next_effect.SetCcNodeId(new_sequence_number_, real_effect_node_id);
CompositorElementId compositor_element_id =
next_effect.GetCompositorElementId();
@@ -1115,7 +1097,7 @@ void PropertyTreeManager::BuildEffectNodesRecursively(
DCHECK(!property_trees_.element_id_to_effect_node_index.contains(
compositor_element_id));
property_trees_.element_id_to_effect_node_index[compositor_element_id] =
- effect_node.id;
+ real_effect_node_id;
}
effect_stack_.emplace_back(current_);
@@ -1123,27 +1105,33 @@ void PropertyTreeManager::BuildEffectNodesRecursively(
*output_clip, next_effect.LocalTransformSpace());
}
+static cc::RenderSurfaceReason RenderSurfaceReasonForEffect(
+ const EffectPaintPropertyNode& effect) {
+ if (!effect.Filter().IsEmpty())
+ return cc::RenderSurfaceReason::kFilter;
+ if (effect.HasActiveFilterAnimation())
+ return cc::RenderSurfaceReason::kFilterAnimation;
+ if (!effect.BackdropFilter().IsEmpty())
+ return cc::RenderSurfaceReason::kBackdropFilter;
+ if (effect.HasActiveBackdropFilterAnimation())
+ return cc::RenderSurfaceReason::kBackdropFilterAnimation;
+ if (effect.BlendMode() != SkBlendMode::kSrcOver &&
+ // For optimization, we will set render surface reason for DstIn later in
+ // PaintArtifactCompositor::UpdateRenderSurfaceForEffects() if it controls
+ // more than one layer.
+ effect.BlendMode() != SkBlendMode::kDstIn) {
+ return cc::RenderSurfaceReason::kBlendMode;
+ }
+ return cc::RenderSurfaceReason::kNone;
+}
+
void PropertyTreeManager::PopulateCcEffectNode(
cc::EffectNode& effect_node,
const EffectPaintPropertyNode& effect,
- int output_clip_id,
- BackdropEffectState backdrop_effect_state) {
+ int output_clip_id) {
effect_node.stable_id = effect.GetCompositorElementId().GetStableId();
effect_node.clip_id = output_clip_id;
-
- // 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.
- if (!effect.Filter().IsEmpty()) {
- effect_node.render_surface_reason = cc::RenderSurfaceReason::kFilter;
- } else if (effect.HasActiveFilterAnimation()) {
- effect_node.render_surface_reason =
- cc::RenderSurfaceReason::kFilterAnimation;
- }
- // If needed, render surface reason for backdrop effect will be set in
- // PopuluateCcEffectNodeBackdropEffect() below.
-
+ effect_node.render_surface_reason = RenderSurfaceReasonForEffect(effect);
effect_node.opacity = effect.Opacity();
if (effect.GetColorFilter() != kColorFilterNone) {
// Currently color filter is only used by SVG masks.
@@ -1158,14 +1146,16 @@ void PropertyTreeManager::PopulateCcEffectNode(
} else {
effect_node.transform_id =
EnsureCompositorTransformNode(effect.LocalTransformSpace());
- if (backdrop_effect_state == kBackdropEffectToBeSetOnCcEffectNode) {
+ if (effect.HasBackdropEffect()) {
// We never have backdrop effect and filter on the same effect node.
DCHECK(effect.Filter().IsEmpty());
- PopulateCcEffectNodeBackdropEffect(effect_node, effect);
+ effect_node.backdrop_filters =
+ effect.BackdropFilter().AsCcFilterOperations();
+ effect_node.backdrop_filter_bounds = effect.BackdropFilterBounds();
+ effect_node.blend_mode = effect.BlendMode();
effect_node.backdrop_mask_element_id = effect.BackdropMaskElementId();
} else {
effect_node.filters = effect.Filter().AsCcFilterOperations();
- effect_node.filters_origin = effect.FiltersOrigin();
}
}
effect_node.double_sided = !effect.LocalTransformSpace().IsBackfaceHidden();
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 4a5553488b5..78a1f5b2f4e 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
@@ -234,17 +234,16 @@ class PropertyTreeManager {
void BuildEffectNodesRecursively(const EffectPaintPropertyNode& next_effect);
void ForceRenderSurfaceIfSyntheticRoundedCornerClip(EffectState& state);
- // 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(
+ // When entering |target_clip| and |next_effect|, we may need to synthesize
+ // cc clips and effects for particular types of masks. See CcEffectType.
+ // Returns the id of the cc effect node created for |next_effect| or
+ // kInvalidNodeId. Normally this function doesn't create cc effect node for
+ // |next_effect|, thus returns kInvalidNodeId, except when |next_effect| has
+ // backdrop effects and we need to move the effect up to the outermost
+ // synthetic effect to allow the backdrop effects to access the correct
+ // backdrop, in which case this function returns the id of the synthetic cc
+ // effect node that contains the converted |next_effect| effects.
+ int SynthesizeCcEffectsForClipsIfNeeded(
const ClipPaintPropertyNode& target_clip,
const EffectPaintPropertyNode* next_effect);
@@ -252,11 +251,7 @@ class PropertyTreeManager {
void CloseCcEffect();
void PopulateCcEffectNode(cc::EffectNode&,
const EffectPaintPropertyNode& effect,
- int output_clip_id,
- BackdropEffectState);
- void PopulateCcEffectNodeBackdropEffect(
- cc::EffectNode& effect_node,
- const EffectPaintPropertyNode& backdrop_effect);
+ int output_clip_id);
bool IsCurrentCcEffectSynthetic() const { return current_.effect_type; }
bool IsCurrentCcEffectSyntheticForNonTrivialClip() const {
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 1f66c2ce654..61a2df75ff0 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
@@ -19,6 +19,8 @@ struct CompositingReasonStringMap {
constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
{CompositingReason::k3DTransform, "transform3D", "Has a 3d transform"},
+ {CompositingReason::kTrivial3DTransform, "trivialTransform3D",
+ "Has a trivial 3d transform"},
{CompositingReason::kVideo, "video", "Is an accelerated video"},
{CompositingReason::kCanvas, "canvas",
"Is an accelerated canvas, or is a display list backed canvas that was "
@@ -90,10 +92,6 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
"blendingWithCompositedDescendants",
"Has a blending effect that needs to be known by compositor because of "
"composited descendants"},
- {CompositingReason::kClipsCompositingDescendants,
- "clipsCompositingDescendants",
- "Has a clip that needs to be known by compositor because of composited "
- "descendants"},
{CompositingReason::kPerspectiveWith3DDescendants,
"perspectiveWith3DDescendants",
"Has a perspective transform that needs to be known by compositor because "
@@ -105,9 +103,9 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
{CompositingReason::kIsolateCompositedDescendants,
"isolateCompositedDescendants",
"Should isolate descendants to apply a blend effect"},
- {CompositingReason::kPositionFixedWithCompositedDescendants,
- "positionFixedWithCompositedDescendants"
- "Is a position:fixed element with composited descendants"},
+ {CompositingReason::kFullscreenVideoWithCompositedDescendants,
+ "fullscreenVideoWithCompositedDescendants",
+ "Is a fullscreen video element with composited descendants"},
{CompositingReason::kRoot, "root", "Is the root layer"},
{CompositingReason::kLayerForHorizontalScrollbar,
"layerForHorizontalScrollbar",
@@ -121,16 +119,8 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
"Secondary layer, the scroll corner layer"},
{CompositingReason::kLayerForScrollingContents, "layerForScrollingContents",
"Secondary layer, to house contents that can be scrolled"},
- {CompositingReason::kLayerForScrollingContainer,
- "layerForScrollingContainer",
- "Secondary layer, used to position the scrolling contents while "
- "scrolling"},
{CompositingReason::kLayerForSquashingContents, "layerForSquashingContents",
"Secondary layer, home for a group of squashable content"},
- {CompositingReason::kLayerForSquashingContainer,
- "layerForSquashingContainer",
- "Secondary layer, no-op layer to place the squashing layer correctly in "
- "the composited layer tree"},
{CompositingReason::kLayerForForeground, "layerForForeground",
"Secondary layer, to contain any normal flow and positive z-index "
"contents on top of a negative z-index layer"},
@@ -140,6 +130,9 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = {
"Layer painted on top of other layers as decoration"},
{CompositingReason::kLayerForOther, "layerForOther",
"Layer for link highlight, frame overlay, etc."},
+ {CompositingReason::kBackfaceInvisibility3DAncestor,
+ "BackfaceInvisibility3DAncestor",
+ "Ancestor in same 3D rendering context has a hidden backface"},
};
} // anonymous namespace
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 3bae565f482..7f1b134718c 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -18,6 +18,7 @@ using CompositingReasons = uint64_t;
#define FOR_EACH_COMPOSITING_REASON(V) \
/* Intrinsic reasons that can be known right away by the layer. */ \
V(3DTransform) \
+ V(Trivial3DTransform) \
V(Video) \
V(Canvas) \
V(Plugin) \
@@ -36,6 +37,8 @@ using CompositingReasons = uint64_t;
V(WillChangeOpacity) \
V(WillChangeFilter) \
V(WillChangeBackdropFilter) \
+ /* Reasons that depend on ancestor properties */ \
+ V(BackfaceInvisibility3DAncestor) \
/* This flag is needed only when none of the explicit kWillChange* reasons \
are set. */ \
V(WillChangeOther) \
@@ -57,11 +60,10 @@ using CompositingReasons = uint64_t;
V(ReflectionWithCompositedDescendants) \
V(FilterWithCompositedDescendants) \
V(BlendingWithCompositedDescendants) \
- V(ClipsCompositingDescendants) \
V(PerspectiveWith3DDescendants) \
V(Preserve3DWith3DDescendants) \
V(IsolateCompositedDescendants) \
- V(PositionFixedWithCompositedDescendants) \
+ V(FullscreenVideoWithCompositedDescendants) \
\
/* The root layer is a special case. It may be forced to be a layer, but it \
also needs to be a layer if anything else in the subtree is composited. */ \
@@ -74,9 +76,7 @@ using CompositingReasons = uint64_t;
V(LayerForOverflowControlsHost) \
V(LayerForScrollCorner) \
V(LayerForScrollingContents) \
- V(LayerForScrollingContainer) \
V(LayerForSquashingContents) \
- V(LayerForSquashingContainer) \
V(LayerForForeground) \
V(LayerForMask) \
/* Composited layer painted on top of all other layers as decoration. */ \
@@ -119,18 +119,24 @@ class PLATFORM_EXPORT CompositingReason {
kActiveFilterAnimation | kActiveBackdropFilterAnimation,
kComboAllDirectStyleDeterminedReasons =
- k3DTransform | kBackfaceVisibilityHidden | kComboActiveAnimation |
- kWillChangeTransform | kWillChangeOpacity | kWillChangeFilter |
- kWillChangeOther | kBackdropFilter | kWillChangeBackdropFilter,
+ k3DTransform | kTrivial3DTransform | kBackfaceVisibilityHidden |
+ kComboActiveAnimation | kWillChangeTransform | kWillChangeOpacity |
+ kWillChangeFilter | kWillChangeOther | kBackdropFilter |
+ kWillChangeBackdropFilter,
kComboAllDirectNonStyleDeterminedReasons =
kVideo | kCanvas | kPlugin | kIFrame | kOverflowScrollingParent |
kOutOfFlowClipping | kVideoOverlay | kXrOverlay | kRoot |
- kRootScroller | kScrollDependentPosition,
+ kRootScroller | kScrollDependentPosition |
+ kBackfaceInvisibility3DAncestor,
kComboAllDirectReasons = kComboAllDirectStyleDeterminedReasons |
kComboAllDirectNonStyleDeterminedReasons,
+ kComboTransformedRasterizationDisallowedReasons =
+ kComboAllDirectReasons & ~kScrollDependentPosition &
+ ~kTrivial3DTransform & ~kBackfaceVisibilityHidden,
+
kComboAllCompositedScrollingDeterminedReasons =
kScrollDependentPosition | kOverflowScrolling,
@@ -138,7 +144,7 @@ class PLATFORM_EXPORT CompositingReason {
kIsolateCompositedDescendants | kOpacityWithCompositedDescendants |
kMaskWithCompositedDescendants | kFilterWithCompositedDescendants |
kBlendingWithCompositedDescendants |
- kReflectionWithCompositedDescendants | kClipsCompositingDescendants,
+ kReflectionWithCompositedDescendants,
kCombo3DDescendants =
kPreserve3DWith3DDescendants | kPerspectiveWith3DDescendants,
@@ -154,9 +160,9 @@ class PLATFORM_EXPORT CompositingReason {
kScrollDependentPosition | kVideo | kCanvas | kPlugin | kIFrame,
kDirectReasonsForTransformProperty =
- k3DTransform | kWillChangeTransform | kWillChangeOther |
- kPerspectiveWith3DDescendants | kPreserve3DWith3DDescendants |
- kActiveTransformAnimation,
+ k3DTransform | kTrivial3DTransform | kWillChangeTransform |
+ kWillChangeOther | kPerspectiveWith3DDescendants |
+ kPreserve3DWith3DDescendants | kActiveTransformAnimation,
kDirectReasonsForScrollTranslationProperty =
kRootScroller | kOverflowScrolling,
kDirectReasonsForEffectProperty =
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
index af3d3147d39..f9109619f6a 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
+#include "base/check_op.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
namespace blink {
@@ -21,7 +22,7 @@ class SimpleColorClassifier : public DarkModeColorClassifier {
new SimpleColorClassifier(DarkModeClassification::kApplyFilter));
}
- DarkModeClassification ShouldInvertColor(const Color& color) override {
+ DarkModeClassification ShouldInvertColor(SkColor color) override {
return value_;
}
@@ -39,7 +40,7 @@ class InvertLowBrightnessColorsClassifier : public DarkModeColorClassifier {
DCHECK_LT(brightness_threshold_, 256);
}
- DarkModeClassification ShouldInvertColor(const Color& color) override {
+ DarkModeClassification ShouldInvertColor(SkColor color) override {
if (CalculateColorBrightness(color) < brightness_threshold_)
return DarkModeClassification::kApplyFilter;
return DarkModeClassification::kDoNotApplyFilter;
@@ -57,7 +58,7 @@ class InvertHighBrightnessColorsClassifier : public DarkModeColorClassifier {
DCHECK_LT(brightness_threshold_, 256);
}
- DarkModeClassification ShouldInvertColor(const Color& color) override {
+ DarkModeClassification ShouldInvertColor(SkColor color) override {
if (CalculateColorBrightness(color) > brightness_threshold_)
return DarkModeClassification::kApplyFilter;
return DarkModeClassification::kDoNotApplyFilter;
@@ -74,10 +75,10 @@ class InvertHighBrightnessColorsClassifier : public DarkModeColorClassifier {
//
// We don't use HSL or HSV here because perceived brightness is a function of
// hue as well as lightness/value.
-int DarkModeColorClassifier::CalculateColorBrightness(const Color& color) {
- int weighted_red = color.Red() * 299;
- int weighted_green = color.Green() * 587;
- int weighted_blue = color.Blue() * 114;
+int DarkModeColorClassifier::CalculateColorBrightness(SkColor color) {
+ int weighted_red = SkColorGetR(color) * 299;
+ int weighted_green = SkColorGetG(color) * 587;
+ int weighted_blue = SkColorGetB(color) * 114;
return (weighted_red + weighted_green + weighted_blue) / 1000;
}
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
index 032e16d732c..d1ca6bf43c8 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
#include "third_party/blink/renderer/platform/platform_export.h"
@@ -17,7 +16,7 @@ namespace blink {
class PLATFORM_EXPORT DarkModeColorClassifier {
public:
// Determine perceived brightness of a color.
- static int CalculateColorBrightness(const Color& color);
+ static int CalculateColorBrightness(SkColor color);
static std::unique_ptr<DarkModeColorClassifier> MakeTextColorClassifier(
const DarkModeSettings& settings);
@@ -30,7 +29,7 @@ class PLATFORM_EXPORT DarkModeColorClassifier {
// whether to invert a color. The background is likely to be dark, so a lower
// opacity will usually decrease the effective brightness of both the original
// and the inverted colors.
- virtual DarkModeClassification ShouldInvertColor(const Color& color) = 0;
+ virtual DarkModeClassification ShouldInvertColor(SkColor color) = 0;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier_test.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier_test.cc
index c2251c51b8a..0dc1ce83050 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier_test.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_classifier_test.cc
@@ -4,8 +4,8 @@
#include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
+#include "base/check_op.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -13,11 +13,11 @@
namespace blink {
namespace {
-Color GetColorWithBrightness(int target_brightness) {
+SkColor GetColorWithBrightness(int target_brightness) {
CHECK_GE(target_brightness, 0);
CHECK_LE(target_brightness, 256);
- return Color(target_brightness, target_brightness, target_brightness);
+ return SkColorSetRGB(target_brightness, target_brightness, target_brightness);
}
TEST(DarkModeColorClassifierTest, ApplyFilterToDarkTextOnly) {
@@ -37,10 +37,10 @@ TEST(DarkModeColorClassifierTest, ApplyFilterToDarkTextOnly) {
classifier->ShouldInvertColor(GetColorWithBrightness(
settings.text_brightness_threshold - 5)));
EXPECT_EQ(DarkModeClassification::kApplyFilter,
- classifier->ShouldInvertColor(Color::kBlack));
+ classifier->ShouldInvertColor(SK_ColorBLACK));
EXPECT_EQ(DarkModeClassification::kDoNotApplyFilter,
- classifier->ShouldInvertColor(Color::kWhite));
+ classifier->ShouldInvertColor(SK_ColorWHITE));
EXPECT_EQ(DarkModeClassification::kDoNotApplyFilter,
classifier->ShouldInvertColor(GetColorWithBrightness(
settings.text_brightness_threshold + 5)));
@@ -57,9 +57,9 @@ TEST(DarkModeColorClassifierTest, ApplyFilterToLightBackgroundElementsOnly) {
DarkModeColorClassifier::MakeBackgroundColorClassifier(settings);
EXPECT_EQ(DarkModeClassification::kApplyFilter,
- classifier->ShouldInvertColor(Color::kWhite));
+ classifier->ShouldInvertColor(SK_ColorWHITE));
EXPECT_EQ(DarkModeClassification::kDoNotApplyFilter,
- classifier->ShouldInvertColor(Color::kBlack));
+ classifier->ShouldInvertColor(SK_ColorBLACK));
EXPECT_EQ(DarkModeClassification::kApplyFilter,
classifier->ShouldInvertColor(GetColorWithBrightness(
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc
index ee93de6db48..910fc543a1f 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.cc
@@ -35,8 +35,8 @@ class SkColorFilterWrapper : public DarkModeColorFilter {
new SkColorFilterWrapper(SkHighContrastFilter::Make(config)));
}
- Color InvertColor(const Color& color) const override {
- return Color(filter_->filterColor(color.Rgb()));
+ SkColor InvertColor(SkColor color) const override {
+ return filter_->filterColor(color);
}
sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
@@ -59,20 +59,20 @@ class LabColorFilter : public DarkModeColorFilter {
filter_ = SkHighContrastFilter::Make(config);
}
- Color InvertColor(const Color& color) const override {
- blink::FloatPoint3D rgb = {color.Red() / 255.0f, color.Green() / 255.0f,
- color.Blue() / 255.0f};
+ SkColor InvertColor(SkColor color) const override {
+ blink::FloatPoint3D rgb = {SkColorGetR(color) / 255.0f,
+ SkColorGetG(color) / 255.0f,
+ SkColorGetB(color) / 255.0f};
blink::FloatPoint3D lab = transformer_.sRGBToLab(rgb);
float invertedL = std::min(110.0f - lab.X(), 100.0f);
lab.SetX(invertedL);
rgb = transformer_.LabToSRGB(lab);
- Color inverted_color(Color(static_cast<unsigned int>(rgb.X() * 255 + 0.5),
- static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
- static_cast<unsigned int>(rgb.Z() * 255 + 0.5),
- color.Alpha()));
- AdjustGray(&inverted_color);
- return inverted_color;
+ SkColor inverted_color = SkColorSetARGB(
+ SkColorGetA(color), static_cast<unsigned int>(rgb.X() * 255 + 0.5),
+ static_cast<unsigned int>(rgb.Y() * 255 + 0.5),
+ static_cast<unsigned int>(rgb.Z() * 255 + 0.5));
+ return AdjustGray(inverted_color);
}
sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
@@ -84,17 +84,21 @@ class LabColorFilter : public DarkModeColorFilter {
//
// TODO(gilmanmh): Consider adding a more general way to adjust colors after
// applying the main filter.
- void AdjustGray(Color* color) const {
- DCHECK(color);
- static const int kBrightnessThreshold = 32;
- static const int kAdjustedBrightness = 18;
-
- if (color->Red() == color->Blue() && color->Red() == color->Green() &&
- color->Red() < kBrightnessThreshold &&
- color->Red() > kAdjustedBrightness) {
- color->SetRGB(kAdjustedBrightness, kAdjustedBrightness,
- kAdjustedBrightness);
+ SkColor AdjustGray(SkColor color) const {
+ static const uint8_t kBrightnessThreshold = 32;
+ static const uint8_t kAdjustedBrightness = 18;
+
+ uint8_t r = SkColorGetR(color);
+ uint8_t g = SkColorGetG(color);
+ uint8_t b = SkColorGetB(color);
+
+ if (r == b && r == g && r < kBrightnessThreshold &&
+ r > kAdjustedBrightness) {
+ return SkColorSetRGB(kAdjustedBrightness, kAdjustedBrightness,
+ kAdjustedBrightness);
}
+
+ return color;
}
const LabColorSpace::RGBLABTransformer transformer_;
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h
index ed7c578fdd4..f00e0ec2b81 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h
@@ -7,10 +7,10 @@
#include <memory>
-#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
#include "third_party/blink/renderer/platform/graphics/lab_color_space.h"
#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkRefCnt.h"
class SkColorFilter;
@@ -24,7 +24,7 @@ class PLATFORM_EXPORT DarkModeColorFilter {
const DarkModeSettings& settings);
virtual ~DarkModeColorFilter();
- virtual Color InvertColor(const Color& color) const = 0;
+ virtual SkColor InvertColor(SkColor color) const = 0;
virtual sk_sp<SkColorFilter> ToSkColorFilter() const = 0;
};
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 6d957efd180..6684ac68cfc 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
@@ -11,11 +11,11 @@
#include "base/optional.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_color_classifier.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
-#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/blink/renderer/platform/wtf/hash_functions.h"
+#include "third_party/blink/renderer/platform/wtf/lru_cache.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/effects/SkColorMatrix.h"
@@ -45,35 +45,7 @@ void VerifySettingsAreUnchanged(const DarkModeSettings& a,
#endif // DCHECK_IS_ON()
-bool ShouldApplyToImage(const DarkModeSettings& settings,
- const FloatRect& src_rect,
- const FloatRect& dest_rect,
- Image* image) {
- switch (settings.image_policy) {
- case DarkModeImagePolicy::kFilterSmart: {
- DarkModeImageClassifier* classifier;
- switch (settings.classifier_type) {
- case DarkModeClassifierType::kIcon: {
- DarkModeIconClassifier icon_classifier;
- classifier = &icon_classifier;
- break;
- }
- case DarkModeClassifierType::kGeneric: {
- DarkModeGenericClassifier generic_classifier;
- classifier = &generic_classifier;
- break;
- }
- }
- DarkModeClassification result =
- classifier->Classify(image, src_rect, dest_rect);
- return result == DarkModeClassification::kApplyFilter;
- }
- case DarkModeImagePolicy::kFilterNone:
- return false;
- case DarkModeImagePolicy::kFilterAll:
- return true;
- }
-}
+const size_t kMaxCacheSize = 1024u;
// TODO(gilmanmh): If grayscaling images in dark mode proves popular among
// users, consider experimenting with different grayscale algorithms.
@@ -88,10 +60,40 @@ sk_sp<SkColorFilter> MakeGrayscaleFilter(float grayscale_percent) {
} // namespace
+// DarkModeInvertedColorCache - Implements cache for inverted colors.
+class DarkModeInvertedColorCache {
+ public:
+ DarkModeInvertedColorCache() : cache_(kMaxCacheSize) {}
+ ~DarkModeInvertedColorCache() = default;
+
+ SkColor GetInvertedColor(DarkModeColorFilter* filter, SkColor color) {
+ WTF::IntegralWithAllKeys<SkColor> key(color);
+ SkColor* cached_value = cache_.Get(key);
+ if (cached_value)
+ return *cached_value;
+
+ SkColor inverted_color = filter->InvertColor(color);
+ cache_.Put(key, std::move(inverted_color));
+ return inverted_color;
+ }
+
+ void Clear() { cache_.Clear(); }
+
+ size_t size() { return cache_.size(); }
+
+ private:
+ WTF::LruCache<WTF::IntegralWithAllKeys<SkColor>, SkColor> cache_;
+};
+
DarkModeFilter::DarkModeFilter()
: text_classifier_(nullptr),
+ background_classifier_(nullptr),
+ bitmap_image_classifier_(nullptr),
+ svg_image_classifier_(nullptr),
+ gradient_generated_image_classifier_(nullptr),
color_filter_(nullptr),
- image_filter_(nullptr) {
+ image_filter_(nullptr),
+ inverted_color_cache_(new DarkModeInvertedColorCache()) {
DarkModeSettings default_settings;
default_settings.mode = DarkModeInversionAlgorithm::kOff;
UpdateSettings(default_settings);
@@ -111,6 +113,8 @@ void DarkModeFilter::UpdateSettings(const DarkModeSettings& new_settings) {
return;
}
+ inverted_color_cache_->Clear();
+
settings_ = new_settings;
color_filter_ = DarkModeColorFilter::FromSettings(settings_);
if (!color_filter_) {
@@ -127,28 +131,42 @@ void DarkModeFilter::UpdateSettings(const DarkModeSettings& new_settings) {
DarkModeColorClassifier::MakeTextColorClassifier(settings_);
background_classifier_ =
DarkModeColorClassifier::MakeBackgroundColorClassifier(settings_);
+ bitmap_image_classifier_ =
+ DarkModeImageClassifier::MakeBitmapImageClassifier();
+ svg_image_classifier_ = DarkModeImageClassifier::MakeSVGImageClassifier();
+ gradient_generated_image_classifier_ =
+ DarkModeImageClassifier::MakeGradientGeneratedImageClassifier();
}
-Color DarkModeFilter::InvertColorIfNeeded(const Color& color,
- ElementRole role) {
+SkColor DarkModeFilter::InvertColorIfNeeded(SkColor color, ElementRole role) {
if (!IsDarkModeActive())
return color;
if (role_override_.has_value())
role = role_override_.value();
- if (ShouldApplyToColor(color, role))
- return color_filter_->InvertColor(color);
+ if (ShouldApplyToColor(color, role)) {
+ return inverted_color_cache_->GetInvertedColor(color_filter_.get(), color);
+ }
+
return color;
}
-void DarkModeFilter::ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
- const FloatRect& dest_rect,
- Image* image,
- cc::PaintFlags* flags) {
+void DarkModeFilter::ApplyToImageFlagsIfNeeded(const SkRect& src,
+ const SkRect& dst,
+ const PaintImage& paint_image,
+ cc::PaintFlags* flags,
+ ElementRole element_role) {
+ // The construction of |paint_image| is expensive, so ensure
+ // IsDarkModeActive() is checked prior to calling this function.
+ // See: https://crbug.com/1094781.
+ DCHECK(IsDarkModeActive());
+
if (!image_filter_ ||
- !ShouldApplyToImage(settings(), src_rect, dest_rect, image))
+ !ShouldApplyToImage(settings(), src, dst, paint_image, element_role)) {
return;
+ }
+
flags->setColorFilter(image_filter_);
}
@@ -165,10 +183,8 @@ base::Optional<cc::PaintFlags> DarkModeFilter::ApplyToFlagsIfNeeded(
if (flags.HasShader()) {
dark_mode_flags.setColorFilter(color_filter_->ToSkColorFilter());
} else if (ShouldApplyToColor(flags.getColor(), role)) {
- Color inverted_color = color_filter_->InvertColor(flags.getColor());
- dark_mode_flags.setColor(
- SkColorSetARGB(inverted_color.Alpha(), inverted_color.Red(),
- inverted_color.Green(), inverted_color.Blue()));
+ dark_mode_flags.setColor(inverted_color_cache_->GetInvertedColor(
+ color_filter_.get(), flags.getColor()));
}
return base::make_optional<cc::PaintFlags>(std::move(dark_mode_flags));
@@ -182,7 +198,7 @@ bool DarkModeFilter::IsDarkModeActive() const {
// already done so. This allows the caller to exit earlier if it needs to
// perform some other logic in between confirming dark mode is active and
// checking the color classifiers.
-bool DarkModeFilter::ShouldApplyToColor(const Color& color, ElementRole role) {
+bool DarkModeFilter::ShouldApplyToColor(SkColor color, ElementRole role) {
switch (role) {
case ElementRole::kText:
DCHECK(text_classifier_);
@@ -209,10 +225,50 @@ bool DarkModeFilter::ShouldApplyToColor(const Color& color, ElementRole role) {
// 2) Non-inline SVG images are already classified at this point and have
// a filter applied if necessary.
return false;
+ default:
+ return false;
}
NOTREACHED();
}
+size_t DarkModeFilter::GetInvertedColorCacheSizeForTesting() {
+ return inverted_color_cache_->size();
+}
+
+bool DarkModeFilter::ShouldApplyToImage(const DarkModeSettings& settings,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintImage& paint_image,
+ ElementRole role) {
+ switch (settings.image_policy) {
+ case DarkModeImagePolicy::kFilterSmart: {
+ DarkModeImageClassifier* classifier;
+
+ switch (role) {
+ case ElementRole::kBitmapImage:
+ classifier = bitmap_image_classifier_.get();
+ break;
+ case ElementRole::kSVGImage:
+ classifier = svg_image_classifier_.get();
+ break;
+ case ElementRole::kGradientGeneratedImage:
+ classifier = gradient_generated_image_classifier_.get();
+ break;
+ default:
+ return false;
+ }
+
+ DarkModeClassification result =
+ classifier->Classify(paint_image, src, dst);
+ return result == DarkModeClassification::kApplyFilter;
+ }
+ case DarkModeImagePolicy::kFilterNone:
+ return false;
+ case DarkModeImagePolicy::kFilterAll:
+ return true;
+ }
+}
+
ScopedDarkModeElementRoleOverride::ScopedDarkModeElementRoleOverride(
GraphicsContext* graphics_context,
DarkModeFilter::ElementRole role)
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 56a926f5f0d..be6628843fb 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
@@ -8,10 +8,9 @@
#include <memory>
#include "cc/paint/paint_flags.h"
-#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/skia/include/core/SkRefCnt.h"
@@ -19,9 +18,12 @@ class SkColorFilter;
namespace blink {
+class GraphicsContext;
class DarkModeColorClassifier;
+class DarkModeImageClassifier;
class DarkModeColorFilter;
class ScopedDarkModeElementRoleOverride;
+class DarkModeInvertedColorCache;
class PLATFORM_EXPORT DarkModeFilter {
public:
@@ -38,32 +40,54 @@ 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, kListSymbol, kBackground, kSVG };
- Color InvertColorIfNeeded(const Color& color, ElementRole element_role);
+ enum class ElementRole {
+ kText,
+ kListSymbol,
+ kBackground,
+ kSVG,
+ kUnhandledImage,
+ kBitmapImage,
+ kSVGImage,
+ kGradientGeneratedImage
+ };
+
+ SkColor InvertColorIfNeeded(SkColor color, ElementRole element_role);
base::Optional<cc::PaintFlags> ApplyToFlagsIfNeeded(
const cc::PaintFlags& flags,
ElementRole element_role);
// |image| and |flags| must not be null.
- void ApplyToImageFlagsIfNeeded(const FloatRect& src_rect,
- const FloatRect& dest_rect,
- Image* image,
- cc::PaintFlags* flags);
+ void ApplyToImageFlagsIfNeeded(const SkRect& src,
+ const SkRect& dst,
+ const PaintImage& paint_image,
+ cc::PaintFlags* flags,
+ ElementRole element_role);
SkColorFilter* GetImageFilterForTesting() { return image_filter_.get(); }
+ size_t GetInvertedColorCacheSizeForTesting();
private:
friend class ScopedDarkModeElementRoleOverride;
DarkModeSettings settings_;
- bool ShouldApplyToColor(const Color& color, ElementRole role);
+ bool ShouldApplyToColor(SkColor color, ElementRole role);
+ bool ShouldApplyToImage(const DarkModeSettings& settings,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintImage& paint_image,
+ ElementRole role);
std::unique_ptr<DarkModeColorClassifier> text_classifier_;
std::unique_ptr<DarkModeColorClassifier> background_classifier_;
+ std::unique_ptr<DarkModeImageClassifier> bitmap_image_classifier_;
+ std::unique_ptr<DarkModeImageClassifier> svg_image_classifier_;
+ std::unique_ptr<DarkModeImageClassifier> gradient_generated_image_classifier_;
+
std::unique_ptr<DarkModeColorFilter> color_filter_;
sk_sp<SkColorFilter> image_filter_;
base::Optional<ElementRole> role_override_;
+ std::unique_ptr<DarkModeInvertedColorCache> inverted_color_cache_;
};
// Temporarily override the element role for the scope of this object's
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc
index 132473342e8..da272d2a7da 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter_test.cc
@@ -7,7 +7,6 @@
#include "base/optional.h"
#include "cc/paint/paint_flags.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
#include "third_party/skia/include/core/SkColor.h"
@@ -21,12 +20,12 @@ TEST(DarkModeFilterTest, DoNotApplyFilterWhenDarkModeIsOff) {
settings.mode = DarkModeInversionAlgorithm::kOff;
filter.UpdateSettings(settings);
- EXPECT_EQ(Color::kWhite,
+ EXPECT_EQ(SK_ColorWHITE,
filter.InvertColorIfNeeded(
- Color::kWhite, DarkModeFilter::ElementRole::kBackground));
- EXPECT_EQ(Color::kBlack,
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(SK_ColorBLACK,
filter.InvertColorIfNeeded(
- Color::kBlack, DarkModeFilter::ElementRole::kBackground));
+ SK_ColorBLACK, DarkModeFilter::ElementRole::kBackground));
EXPECT_EQ(base::nullopt,
filter.ApplyToFlagsIfNeeded(
@@ -42,18 +41,18 @@ TEST(DarkModeFilterTest, ApplyDarkModeToColorsAndFlags) {
settings.mode = DarkModeInversionAlgorithm::kSimpleInvertForTesting;
filter.UpdateSettings(settings);
- EXPECT_EQ(Color::kBlack,
+ EXPECT_EQ(SK_ColorBLACK,
filter.InvertColorIfNeeded(
- Color::kWhite, DarkModeFilter::ElementRole::kBackground));
- EXPECT_EQ(Color::kWhite,
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(SK_ColorWHITE,
filter.InvertColorIfNeeded(
- Color::kBlack, DarkModeFilter::ElementRole::kBackground));
+ SK_ColorBLACK, DarkModeFilter::ElementRole::kBackground));
- EXPECT_EQ(Color::kWhite,
- filter.InvertColorIfNeeded(Color::kWhite,
+ EXPECT_EQ(SK_ColorWHITE,
+ filter.InvertColorIfNeeded(SK_ColorWHITE,
DarkModeFilter::ElementRole::kSVG));
- EXPECT_EQ(Color::kBlack,
- filter.InvertColorIfNeeded(Color::kBlack,
+ EXPECT_EQ(SK_ColorBLACK,
+ filter.InvertColorIfNeeded(SK_ColorBLACK,
DarkModeFilter::ElementRole::kSVG));
cc::PaintFlags flags;
@@ -66,5 +65,54 @@ TEST(DarkModeFilterTest, ApplyDarkModeToColorsAndFlags) {
EXPECT_NE(nullptr, filter.GetImageFilterForTesting());
}
+TEST(DarkModeFilterTest, InvertedColorCacheSize) {
+ DarkModeFilter filter;
+ DarkModeSettings settings;
+
+ settings.mode = DarkModeInversionAlgorithm::kSimpleInvertForTesting;
+ filter.UpdateSettings(settings);
+ EXPECT_EQ(0u, filter.GetInvertedColorCacheSizeForTesting());
+ EXPECT_EQ(SK_ColorBLACK,
+ filter.InvertColorIfNeeded(
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(1u, filter.GetInvertedColorCacheSizeForTesting());
+ // Should get cached value.
+ EXPECT_EQ(SK_ColorBLACK,
+ filter.InvertColorIfNeeded(
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(1u, filter.GetInvertedColorCacheSizeForTesting());
+
+ // On changing DarkModeSettings, cache should be reset.
+ settings.mode = DarkModeInversionAlgorithm::kInvertLightness;
+ filter.UpdateSettings(settings);
+ EXPECT_EQ(0u, filter.GetInvertedColorCacheSizeForTesting());
+}
+
+TEST(DarkModeFilterTest, InvertedColorCacheZeroMaxKeys) {
+ DarkModeFilter filter;
+ DarkModeSettings settings;
+ settings.mode = DarkModeInversionAlgorithm::kSimpleInvertForTesting;
+ filter.UpdateSettings(settings);
+
+ EXPECT_EQ(0u, filter.GetInvertedColorCacheSizeForTesting());
+ EXPECT_EQ(SK_ColorBLACK,
+ filter.InvertColorIfNeeded(
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(1u, filter.GetInvertedColorCacheSizeForTesting());
+ EXPECT_EQ(SK_ColorTRANSPARENT,
+ filter.InvertColorIfNeeded(
+ SK_ColorTRANSPARENT, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(2u, filter.GetInvertedColorCacheSizeForTesting());
+
+ // Results returned from cache.
+ EXPECT_EQ(SK_ColorBLACK,
+ filter.InvertColorIfNeeded(
+ SK_ColorWHITE, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(SK_ColorTRANSPARENT,
+ filter.InvertColorIfNeeded(
+ SK_ColorTRANSPARENT, DarkModeFilter::ElementRole::kBackground));
+ EXPECT_EQ(2u, filter.GetInvertedColorCacheSizeForTesting());
+}
+
} // namespace
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.cc
deleted file mode 100644
index f14ba3721b8..00000000000
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.cc
+++ /dev/null
@@ -1,73 +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/graphics/dark_mode_generic_classifier.h"
-
-#include "third_party/blink/renderer/platform/graphics/darkmode/darkmode_classifier.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
-
-namespace blink {
-namespace {
-
-// Decision tree lower and upper thresholds for grayscale and color images.
-const float kLowColorCountThreshold[2] = {0.8125, 0.015137};
-const float kHighColorCountThreshold[2] = {1, 0.025635};
-
-DarkModeClassification ClassifyUsingDecisionTree(
- const DarkModeImageClassifier::Features& features) {
- float low_color_count_threshold =
- kLowColorCountThreshold[features.is_colorful];
- float high_color_count_threshold =
- kHighColorCountThreshold[features.is_colorful];
-
- // Very few colors means it's not a photo, apply the filter.
- if (features.color_buckets_ratio < low_color_count_threshold)
- return DarkModeClassification::kApplyFilter;
-
- // Too many colors means it's probably photorealistic, do not apply it.
- if (features.color_buckets_ratio > high_color_count_threshold)
- return DarkModeClassification::kDoNotApplyFilter;
-
- // In-between, decision tree cannot give a precise result.
- return DarkModeClassification::kNotClassified;
-}
-
-// The neural network expects these features to be in a specific order within
-// the vector. Do not change the order here without also changing the neural
-// network code!
-Vector<float> ToVector(const DarkModeImageClassifier::Features& features) {
- return {features.is_colorful, features.color_buckets_ratio,
- features.transparency_ratio, features.background_ratio,
- features.is_svg};
-}
-
-} // namespace
-
-DarkModeGenericClassifier::DarkModeGenericClassifier() {}
-
-DarkModeClassification DarkModeGenericClassifier::ClassifyWithFeatures(
- const Features& features) {
- DarkModeClassification result = ClassifyUsingDecisionTree(features);
-
- // If decision tree cannot decide, we use a neural network to decide whether
- // to filter or not based on all the features.
- if (result == DarkModeClassification::kNotClassified) {
- darkmode_tfnative_model::FixedAllocations nn_temp;
- float nn_out;
- auto feature_vector = ToVector(features);
- darkmode_tfnative_model::Inference(&feature_vector[0], &nn_out, &nn_temp);
- result = nn_out > 0 ? DarkModeClassification::kApplyFilter
- : DarkModeClassification::kDoNotApplyFilter;
- }
-
- return result;
-}
-
-DarkModeClassification
-DarkModeGenericClassifier::ClassifyUsingDecisionTreeForTesting(
- const Features& features) {
- return ClassifyUsingDecisionTree(features);
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h
deleted file mode 100644
index d231a266c04..00000000000
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h
+++ /dev/null
@@ -1,31 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_GENERIC_CLASSIFIER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_GENERIC_CLASSIFIER_H_
-
-#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
-#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class PLATFORM_EXPORT DarkModeGenericClassifier
- : public DarkModeImageClassifier {
- DISALLOW_NEW();
-
- public:
- DarkModeGenericClassifier();
- ~DarkModeGenericClassifier() = default;
-
- DarkModeClassification ClassifyWithFeatures(
- const Features& features) override;
-
- DarkModeClassification ClassifyUsingDecisionTreeForTesting(
- const Features& features);
-};
-
-} // namespace blink
-
-#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_GENERIC_CLASSIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.cc
deleted file mode 100644
index 59476956715..00000000000
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.cc
+++ /dev/null
@@ -1,16 +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/graphics/dark_mode_icon_classifier.h"
-
-namespace blink {
-
-DarkModeIconClassifier::DarkModeIconClassifier() {}
-
-DarkModeClassification DarkModeIconClassifier::ClassifyWithFeatures(
- const Features& features) {
- return DarkModeClassification::kDoNotApplyFilter;
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.h
deleted file mode 100644
index 2666e93b076..00000000000
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.h
+++ /dev/null
@@ -1,27 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_ICON_CLASSIFIER_H_
-#define 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_types.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class PLATFORM_EXPORT DarkModeIconClassifier : public DarkModeImageClassifier {
- DISALLOW_NEW();
-
- public:
- DarkModeIconClassifier();
- ~DarkModeIconClassifier() = default;
-
- DarkModeClassification ClassifyWithFeatures(
- const Features& features) override;
-};
-
-} // namespace blink
-
-#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_ICON_CLASSIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
index 906b4e781a0..092acd4a1fe 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
@@ -4,8 +4,11 @@
#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
+#include <map>
+
+#include "base/memory/singleton.h"
#include "base/optional.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/darkmode/darkmode_classifier.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
@@ -14,6 +17,10 @@
namespace blink {
namespace {
+// Decision tree lower and upper thresholds for grayscale and color images.
+const float kLowColorCountThreshold[2] = {0.8125, 0.015137};
+const float kHighColorCountThreshold[2] = {1, 0.025635};
+
bool IsColorGray(const SkColor& color) {
return abs(static_cast<int>(SkColorGetR(color)) -
static_cast<int>(SkColorGetG(color))) +
@@ -26,61 +33,180 @@ bool IsColorTransparent(const SkColor& color) {
return (SkColorGetA(color) < 128);
}
-const int kPixelsToSample = 1000;
-const int kBlocksCount1D = 10;
+const int kMaxSampledPixels = 1000;
+const int kMaxBlocks = 10;
const float kMinOpaquePixelPercentageForForeground = 0.2;
+const int kMinImageSizeForClassification1D = 24;
+const int kMaxImageSizeForClassification1D = 100;
+
+class DarkModeBitmapImageClassifier : public DarkModeImageClassifier {
+ DarkModeClassification DoInitialClassification(const SkRect& dst) override {
+ if (dst.width() < kMinImageSizeForClassification1D ||
+ dst.height() < kMinImageSizeForClassification1D)
+ return DarkModeClassification::kApplyFilter;
+
+ if (dst.width() > kMaxImageSizeForClassification1D ||
+ dst.height() > kMaxImageSizeForClassification1D) {
+ return DarkModeClassification::kDoNotApplyFilter;
+ }
+
+ return DarkModeClassification::kNotClassified;
+ }
+};
+
+class DarkModeSVGImageClassifier : public DarkModeImageClassifier {
+ DarkModeClassification DoInitialClassification(const SkRect& dst) override {
+ return DarkModeClassification::kNotClassified;
+ }
+};
+
+class DarkModeGradientGeneratedImageClassifier
+ : public DarkModeImageClassifier {
+ DarkModeClassification DoInitialClassification(const SkRect& dst) override {
+ return DarkModeClassification::kApplyFilter;
+ }
+};
+
+// DarkModeImageClassificationCache - Implements classification caches for
+// different paint image ids. The classification result for the given |src|
+// rect is added to cache identified by |image_id| and result for the same
+// can be retrieved. Using Remove(), the cache identified by |image_id| can
+// be deleted.
+class DarkModeImageClassificationCache {
+ public:
+ static DarkModeImageClassificationCache* GetInstance() {
+ return base::Singleton<DarkModeImageClassificationCache>::get();
+ }
+
+ DarkModeClassification Get(PaintImage::Id image_id, const SkRect& src) {
+ auto map = cache_.find(image_id);
+ if (map == cache_.end())
+ return DarkModeClassification::kNotClassified;
+
+ Key key = std::pair<float, float>(src.x(), src.y());
+ auto result = map->second.find(key);
+
+ if (result == map->second.end())
+ return DarkModeClassification::kNotClassified;
+
+ return result->second;
+ }
+
+ void Add(PaintImage::Id image_id,
+ const SkRect& src,
+ const DarkModeClassification result) {
+ DCHECK(Get(image_id, src) == DarkModeClassification::kNotClassified);
+ auto map = cache_.find(image_id);
+ if (map == cache_.end())
+ map = cache_.emplace(image_id, ClassificationMap()).first;
+
+ // TODO(prashant.n): Check weather full |src| should be used or not for
+ // key, considering the scenario of same origin and different sizes in the
+ // given sprite. Here only location in the image is considered as of now.
+ Key key = std::pair<float, float>(src.x(), src.y());
+ map->second.emplace(key, result);
+ }
+
+ size_t GetSize(PaintImage::Id image_id) {
+ auto map = cache_.find(image_id);
+ if (map == cache_.end())
+ return 0;
+
+ return map->second.size();
+ }
+
+ void Remove(PaintImage::Id image_id) { cache_.erase(image_id); }
+
+ private:
+ typedef std::pair<float, float> Key;
+ typedef std::map<Key, DarkModeClassification> ClassificationMap;
+
+ std::map<PaintImage::Id, ClassificationMap> cache_;
+
+ DarkModeImageClassificationCache() = default;
+ ~DarkModeImageClassificationCache() = default;
+ friend struct base::DefaultSingletonTraits<DarkModeImageClassificationCache>;
+
+ DISALLOW_COPY_AND_ASSIGN(DarkModeImageClassificationCache);
+};
+
} // namespace
-DarkModeImageClassifier::DarkModeImageClassifier()
- : pixels_to_sample_(kPixelsToSample),
- blocks_count_horizontal_(kBlocksCount1D),
- blocks_count_vertical_(kBlocksCount1D) {}
+DarkModeImageClassifier::DarkModeImageClassifier() = default;
+
+DarkModeImageClassifier::~DarkModeImageClassifier() = default;
+
+std::unique_ptr<DarkModeImageClassifier>
+DarkModeImageClassifier::MakeBitmapImageClassifier() {
+ return std::make_unique<DarkModeBitmapImageClassifier>();
+}
+
+std::unique_ptr<DarkModeImageClassifier>
+DarkModeImageClassifier::MakeSVGImageClassifier() {
+ return std::make_unique<DarkModeSVGImageClassifier>();
+}
+
+std::unique_ptr<DarkModeImageClassifier>
+DarkModeImageClassifier::MakeGradientGeneratedImageClassifier() {
+ return std::make_unique<DarkModeGradientGeneratedImageClassifier>();
+}
DarkModeClassification DarkModeImageClassifier::Classify(
- Image* image,
- const FloatRect& src_rect,
- const FloatRect& dest_rect) {
- DarkModeClassification result = image->GetDarkModeClassification(src_rect);
+ const PaintImage& paint_image,
+ const SkRect& src,
+ const SkRect& dst) {
+ DarkModeImageClassificationCache* cache =
+ DarkModeImageClassificationCache::GetInstance();
+ PaintImage::Id image_id = paint_image.stable_id();
+ DarkModeClassification result = cache->Get(image_id, src);
if (result != DarkModeClassification::kNotClassified)
return result;
- result = image->CheckTypeSpecificConditionsForDarkMode(dest_rect, this);
+ result = DoInitialClassification(dst);
if (result != DarkModeClassification::kNotClassified) {
- image->AddDarkModeClassification(src_rect, result);
+ cache->Add(image_id, src, result);
return result;
}
- auto features_or_null = GetFeatures(image, src_rect);
+ auto features_or_null = GetFeatures(paint_image, src);
if (!features_or_null) {
// Do not cache this classification.
return DarkModeClassification::kDoNotApplyFilter;
}
result = ClassifyWithFeatures(features_or_null.value());
- image->AddDarkModeClassification(src_rect, result);
+ cache->Add(image_id, src, result);
return result;
}
-base::Optional<DarkModeImageClassifier::Features>
-DarkModeImageClassifier::GetFeatures(Image* image, const FloatRect& src_rect) {
- SkBitmap bitmap;
- if (!image->GetBitmap(src_rect, &bitmap))
- return base::nullopt;
+bool DarkModeImageClassifier::GetBitmap(const PaintImage& paint_image,
+ const SkRect& src,
+ SkBitmap* bitmap) {
+ if (!src.width() || !src.height())
+ return false;
- if (pixels_to_sample_ > src_rect.Width() * src_rect.Height())
- pixels_to_sample_ = src_rect.Width() * src_rect.Height();
+ SkRect dst = {0, 0, src.width(), src.height()};
- if (blocks_count_horizontal_ > src_rect.Width())
- blocks_count_horizontal_ = floor(src_rect.Width());
+ if (!bitmap || !bitmap->tryAllocPixels(SkImageInfo::MakeN32(
+ static_cast<int>(src.width()),
+ static_cast<int>(src.height()), kPremul_SkAlphaType)))
+ return false;
- if (blocks_count_vertical_ > src_rect.Height())
- blocks_count_vertical_ = floor(src_rect.Height());
+ SkCanvas canvas(*bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+ canvas.drawImageRect(paint_image.GetSkImage(), src, dst, nullptr);
+ return true;
+}
+base::Optional<DarkModeImageClassifier::Features>
+DarkModeImageClassifier::GetFeatures(const PaintImage& paint_image,
+ const SkRect& src) {
float transparency_ratio;
float background_ratio;
- Vector<SkColor> sampled_pixels;
- GetSamples(bitmap, &sampled_pixels, &transparency_ratio, &background_ratio);
+ std::vector<SkColor> sampled_pixels;
+ GetSamples(paint_image, src, &sampled_pixels, &transparency_ratio,
+ &background_ratio);
// TODO(https://crbug.com/945434): Investigate why an incorrect resource is
// loaded and how we can fetch the correct resource. This condition will
// prevent going further with the rest of the classification logic.
@@ -93,45 +219,63 @@ DarkModeImageClassifier::GetFeatures(Image* image, const FloatRect& src_rect) {
// Extracts sample pixels from the image. The image is separated into uniformly
// distributed blocks through its width and height, each block is sampled, and
// checked to see if it seems to be background or foreground.
-void DarkModeImageClassifier::GetSamples(const SkBitmap& bitmap,
- Vector<SkColor>* sampled_pixels,
+void DarkModeImageClassifier::GetSamples(const PaintImage& paint_image,
+ const SkRect& src,
+ std::vector<SkColor>* sampled_pixels,
float* transparency_ratio,
float* background_ratio) {
- int pixels_per_block =
- pixels_to_sample_ / (blocks_count_horizontal_ * blocks_count_vertical_);
+ SkBitmap bitmap;
+ if (!GetBitmap(paint_image, src, &bitmap))
+ return;
+
+ int num_sampled_pixels = kMaxSampledPixels;
+ int num_blocks_x = kMaxBlocks;
+ int num_blocks_y = kMaxBlocks;
+
+ if (num_sampled_pixels > src.width() * src.height())
+ num_sampled_pixels = src.width() * src.height();
+
+ if (num_blocks_x > src.width())
+ num_blocks_x = floor(src.width());
+
+ if (num_blocks_y > src.height())
+ num_blocks_y = floor(src.height());
+
+ int pixels_per_block = num_sampled_pixels / (num_blocks_x * num_blocks_y);
int transparent_pixels = 0;
int opaque_pixels = 0;
int blocks_count = 0;
- Vector<int> horizontal_grid(blocks_count_horizontal_ + 1);
- Vector<int> vertical_grid(blocks_count_vertical_ + 1);
+ std::vector<int> horizontal_grid(num_blocks_x + 1);
+ std::vector<int> vertical_grid(num_blocks_y + 1);
- for (int block = 0; block <= blocks_count_horizontal_; block++) {
- horizontal_grid[block] = static_cast<int>(round(
- block * bitmap.width() / static_cast<float>(blocks_count_horizontal_)));
+ for (int block = 0; block <= num_blocks_x; block++) {
+ horizontal_grid[block] = static_cast<int>(
+ round(block * bitmap.width() / static_cast<float>(num_blocks_x)));
}
- for (int block = 0; block <= blocks_count_vertical_; block++) {
- vertical_grid[block] = static_cast<int>(round(
- block * bitmap.height() / static_cast<float>(blocks_count_vertical_)));
+ for (int block = 0; block <= num_blocks_y; block++) {
+ vertical_grid[block] = static_cast<int>(
+ round(block * bitmap.height() / static_cast<float>(num_blocks_y)));
}
sampled_pixels->clear();
- Vector<IntRect> foreground_blocks;
+ std::vector<gfx::Rect> foreground_blocks;
- for (int y = 0; y < blocks_count_vertical_; y++) {
- for (int x = 0; x < blocks_count_horizontal_; x++) {
- IntRect block(horizontal_grid[x], vertical_grid[y],
- horizontal_grid[x + 1] - horizontal_grid[x],
- vertical_grid[y + 1] - vertical_grid[y]);
+ for (int y = 0; y < num_blocks_y; y++) {
+ for (int x = 0; x < num_blocks_x; x++) {
+ gfx::Rect block(horizontal_grid[x], vertical_grid[y],
+ horizontal_grid[x + 1] - horizontal_grid[x],
+ vertical_grid[y + 1] - vertical_grid[y]);
- Vector<SkColor> block_samples;
+ std::vector<SkColor> block_samples;
int block_transparent_pixels;
GetBlockSamples(bitmap, block, pixels_per_block, &block_samples,
&block_transparent_pixels);
opaque_pixels += static_cast<int>(block_samples.size());
transparent_pixels += block_transparent_pixels;
- sampled_pixels->AppendRange(block_samples.begin(), block_samples.end());
+ sampled_pixels->insert(sampled_pixels->end(), block_samples.begin(),
+ block_samples.end());
if (opaque_pixels >
kMinOpaquePixelPercentageForForeground * pixels_per_block) {
foreground_blocks.push_back(block);
@@ -149,17 +293,18 @@ void DarkModeImageClassifier::GetSamples(const SkBitmap& bitmap,
// Selects samples at regular intervals from a block of the image.
// Returns the opaque sampled pixels, and the number of transparent
// sampled pixels.
-void DarkModeImageClassifier::GetBlockSamples(const SkBitmap& bitmap,
- const IntRect& block,
- const int required_samples_count,
- Vector<SkColor>* sampled_pixels,
- int* transparent_pixels_count) {
+void DarkModeImageClassifier::GetBlockSamples(
+ const SkBitmap& bitmap,
+ const gfx::Rect& block,
+ const int required_samples_count,
+ std::vector<SkColor>* sampled_pixels,
+ int* transparent_pixels_count) {
*transparent_pixels_count = 0;
- int x1 = block.X();
- int y1 = block.Y();
- int x2 = block.MaxX();
- int y2 = block.MaxY();
+ int x1 = block.x();
+ int y1 = block.y();
+ int x2 = block.right();
+ int y2 = block.bottom();
DCHECK(x1 < bitmap.width());
DCHECK(y1 < bitmap.height());
DCHECK(x2 <= bitmap.width());
@@ -184,7 +329,7 @@ void DarkModeImageClassifier::GetBlockSamples(const SkBitmap& bitmap,
}
DarkModeImageClassifier::Features DarkModeImageClassifier::ComputeFeatures(
- const Vector<SkColor>& sampled_pixels,
+ const std::vector<SkColor>& sampled_pixels,
const float transparency_ratio,
const float background_ratio) {
int samples_count = static_cast<int>(sampled_pixels.size());
@@ -205,13 +350,12 @@ DarkModeImageClassifier::Features DarkModeImageClassifier::ComputeFeatures(
ComputeColorBucketsRatio(sampled_pixels, color_mode);
features.transparency_ratio = transparency_ratio;
features.background_ratio = background_ratio;
- features.is_svg = image_type_ == ImageType::kSvg;
return features;
}
float DarkModeImageClassifier::ComputeColorBucketsRatio(
- const Vector<SkColor>& sampled_pixels,
+ const std::vector<SkColor>& sampled_pixels,
const ColorMode color_mode) {
HashSet<unsigned, WTF::AlreadyHashed,
WTF::UnsignedWithZeroKeyHashTraits<unsigned>>
@@ -243,10 +387,70 @@ float DarkModeImageClassifier::ComputeColorBucketsRatio(
max_buckets[color_mode == ColorMode::kColor];
}
-void DarkModeImageClassifier::ResetDataMembersToDefaults() {
- pixels_to_sample_ = kPixelsToSample;
- blocks_count_horizontal_ = kBlocksCount1D;
- blocks_count_vertical_ = kBlocksCount1D;
+DarkModeClassification DarkModeImageClassifier::ClassifyWithFeatures(
+ const Features& features) {
+ DarkModeClassification result = ClassifyUsingDecisionTree(features);
+
+ // If decision tree cannot decide, we use a neural network to decide whether
+ // to filter or not based on all the features.
+ if (result == DarkModeClassification::kNotClassified) {
+ darkmode_tfnative_model::FixedAllocations nn_temp;
+ float nn_out;
+
+ // The neural network expects these features to be in a specific order
+ // within float array. Do not change the order here without also changing
+ // the neural network code!
+ float feature_list[]{features.is_colorful, features.color_buckets_ratio,
+ features.transparency_ratio,
+ features.background_ratio};
+
+ darkmode_tfnative_model::Inference(feature_list, &nn_out, &nn_temp);
+ result = nn_out > 0 ? DarkModeClassification::kApplyFilter
+ : DarkModeClassification::kDoNotApplyFilter;
+ }
+
+ return result;
+}
+
+DarkModeClassification DarkModeImageClassifier::ClassifyUsingDecisionTree(
+ const DarkModeImageClassifier::Features& features) {
+ float low_color_count_threshold =
+ kLowColorCountThreshold[features.is_colorful];
+ float high_color_count_threshold =
+ kHighColorCountThreshold[features.is_colorful];
+
+ // Very few colors means it's not a photo, apply the filter.
+ if (features.color_buckets_ratio < low_color_count_threshold)
+ return DarkModeClassification::kApplyFilter;
+
+ // Too many colors means it's probably photorealistic, do not apply it.
+ if (features.color_buckets_ratio > high_color_count_threshold)
+ return DarkModeClassification::kDoNotApplyFilter;
+
+ // In-between, decision tree cannot give a precise result.
+ return DarkModeClassification::kNotClassified;
+}
+
+// static
+void DarkModeImageClassifier::RemoveCache(PaintImage::Id image_id) {
+ DarkModeImageClassificationCache::GetInstance()->Remove(image_id);
+}
+
+DarkModeClassification DarkModeImageClassifier::GetCacheValue(
+ PaintImage::Id image_id,
+ const SkRect& src) {
+ return DarkModeImageClassificationCache::GetInstance()->Get(image_id, src);
+}
+
+void DarkModeImageClassifier::AddCacheValue(PaintImage::Id image_id,
+ const SkRect& src,
+ DarkModeClassification result) {
+ return DarkModeImageClassificationCache::GetInstance()->Add(image_id, src,
+ result);
+}
+
+size_t DarkModeImageClassifier::GetCacheSize(PaintImage::Id image_id) {
+ return DarkModeImageClassificationCache::GetInstance()->GetSize(image_id);
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
index a54f25937c3..73045d72e5b 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
@@ -5,34 +5,37 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_IMAGE_CLASSIFIER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_IMAGE_CLASSIFIER_H_
+#include <vector>
+
+#include "base/gtest_prod_util.h"
#include "base/optional.h"
-#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/graphics/graphics_types.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/geometry/rect.h"
namespace blink {
-class Image;
+FORWARD_DECLARE_TEST(DarkModeImageClassifierTest, FeaturesAndClassification);
+FORWARD_DECLARE_TEST(DarkModeImageClassifierTest, Caching);
+// This class is not threadsafe as the cache used for storing classification
+// results is not threadsafe. So it can be used only in blink main thread.
class PLATFORM_EXPORT DarkModeImageClassifier {
- DISALLOW_NEW();
-
public:
- DarkModeImageClassifier();
- ~DarkModeImageClassifier() = default;
+ virtual ~DarkModeImageClassifier();
- DarkModeClassification Classify(Image* image,
- const FloatRect& src_rect,
- const FloatRect& dest_rect);
+ static std::unique_ptr<DarkModeImageClassifier> MakeBitmapImageClassifier();
+ static std::unique_ptr<DarkModeImageClassifier> MakeSVGImageClassifier();
+ static std::unique_ptr<DarkModeImageClassifier>
+ MakeGradientGeneratedImageClassifier();
struct Features {
// True if the image is in color, false if it is grayscale.
bool is_colorful;
- // Whether the image was originally an SVG.
- bool is_svg;
-
// Ratio of the number of bucketed colors used in the image to all
// possibilities. Color buckets are represented with 4 bits per color
// channel.
@@ -40,61 +43,54 @@ class PLATFORM_EXPORT DarkModeImageClassifier {
// How much of the image is transparent or considered part of the
// background.
- float background_ratio;
float transparency_ratio;
+ float background_ratio;
};
- // Computes the features for a given image.
- base::Optional<Features> GetFeatures(Image* image, const FloatRect& src_rect);
-
- virtual DarkModeClassification ClassifyWithFeatures(
- const Features& features) {
- return DarkModeClassification::kDoNotApplyFilter;
- }
-
- enum class ImageType { kBitmap = 0, kSvg = 1 };
+ // Performance warning: |paint_image| will be synchronously decoded if this
+ // function is called in blink main thread.
+ DarkModeClassification Classify(const PaintImage& paint_image,
+ const SkRect& src,
+ const SkRect& dst);
- void SetImageType(ImageType image_type) { image_type_ = image_type; }
+ // Removes cache identified by given |image_id|.
+ static void RemoveCache(PaintImage::Id image_id);
- // Functions for testing.
-
- void SetHorizontalBlocksCount(int horizontal_blocks) {
- blocks_count_horizontal_ = horizontal_blocks;
- }
-
- void SetVerticalBlocksCount(int vertical_blocks) {
- blocks_count_vertical_ = vertical_blocks;
- }
-
- int HorizontalBlocksCount() { return blocks_count_horizontal_; }
-
- int VerticalBlocksCount() { return blocks_count_vertical_; }
-
- void ResetDataMembersToDefaults();
+ protected:
+ DarkModeImageClassifier();
- // End of Functions for testing.
+ virtual DarkModeClassification DoInitialClassification(const SkRect& dst) = 0;
private:
+ DarkModeClassification ClassifyWithFeatures(const Features& features);
+ DarkModeClassification ClassifyUsingDecisionTree(const Features& features);
+ bool GetBitmap(const PaintImage& paint_image,
+ const SkRect& src,
+ SkBitmap* bitmap);
+ base::Optional<Features> GetFeatures(const PaintImage& paint_image,
+ const SkRect& src);
+
enum class ColorMode { kColor = 0, kGrayscale = 1 };
- // Given a SkBitmap, extracts a sample set of pixels (|sampled_pixels|),
- // |transparency_ratio|, and |background_ratio|.
- void GetSamples(const SkBitmap& bitmap,
- Vector<SkColor>* sampled_pixels,
+ // Extracts a sample set of pixels (|sampled_pixels|), |transparency_ratio|,
+ // and |background_ratio|.
+ void GetSamples(const PaintImage& paint_image,
+ const SkRect& src,
+ std::vector<SkColor>* sampled_pixels,
float* transparency_ratio,
float* background_ratio);
// Gets the |required_samples_count| for a specific |block| of the given
// SkBitmap, and returns |sampled_pixels| and |transparent_pixels_count|.
void GetBlockSamples(const SkBitmap& bitmap,
- const IntRect& block,
+ const gfx::Rect& block,
const int required_samples_count,
- Vector<SkColor>* sampled_pixels,
+ std::vector<SkColor>* sampled_pixels,
int* transparent_pixels_count);
// Given |sampled_pixels|, |transparency_ratio|, and |background_ratio| for an
// image, computes and returns the features required for classification.
- Features ComputeFeatures(const Vector<SkColor>& sampled_pixels,
+ Features ComputeFeatures(const std::vector<SkColor>& sampled_pixels,
const float transparency_ratio,
const float background_ratio);
@@ -102,18 +98,22 @@ class PLATFORM_EXPORT DarkModeImageClassifier {
// buckets count to all possible color buckets. If image is in color, a color
// bucket is a 4 bit per channel representation of each RGB color, and if it
// is grayscale, each bucket is a 4 bit representation of luminance.
- float ComputeColorBucketsRatio(const Vector<SkColor>& sampled_pixels,
+ float ComputeColorBucketsRatio(const std::vector<SkColor>& sampled_pixels,
const ColorMode color_mode);
- int pixels_to_sample_;
- // Holds the number of blocks in the horizontal direction when the image is
- // divided into a grid of blocks.
- int blocks_count_horizontal_;
- // Holds the number of blocks in the vertical direction when the image is
- // divided into a grid of blocks.
- int blocks_count_vertical_;
-
- ImageType image_type_;
+ // Gets cached value from the given |image_id| cache.
+ DarkModeClassification GetCacheValue(PaintImage::Id image_id,
+ const SkRect& src);
+ // Adds cache value |result| to the given |image_id| cache.
+ void AddCacheValue(PaintImage::Id image_id,
+ const SkRect& src,
+ DarkModeClassification result);
+ // Returns the cache size for the given |image_id|.
+ size_t GetCacheSize(PaintImage::Id image_id);
+
+ FRIEND_TEST_ALL_PREFIXES(DarkModeImageClassifierTest,
+ FeaturesAndClassification);
+ FRIEND_TEST_ALL_PREFIXES(DarkModeImageClassifierTest, Caching);
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
index 9a38e0dd1ad..1a2ee135ece 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
@@ -6,7 +6,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h"
#include "third_party/blink/renderer/platform/graphics/image.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
@@ -20,40 +19,13 @@ const float kEpsilon = 0.00001;
} // namespace
-class FakeImageForCacheTest : public Image {
+class DarkModeImageClassifierTest : public testing::Test {
public:
- static scoped_refptr<FakeImageForCacheTest> Create() {
- return base::AdoptRef(new FakeImageForCacheTest());
- }
-
- int GetMapSize() { return dark_mode_classifications_.size(); }
-
- DarkModeClassification GetClassification(const FloatRect& src_rect) {
- return GetDarkModeClassification(src_rect);
+ DarkModeImageClassifierTest() {
+ dark_mode_image_classifier_ =
+ DarkModeImageClassifier::MakeBitmapImageClassifier();
}
- void AddClassification(
- const FloatRect& src_rect,
- const DarkModeClassification dark_mode_classification) {
- AddDarkModeClassification(src_rect, dark_mode_classification);
- }
-
- // Pure virtual functions that have to be overridden.
- bool CurrentFrameKnownToBeOpaque() override { return false; }
- IntSize Size() const override { return IntSize(0, 0); }
- void DestroyDecodedData() override {}
- PaintImage PaintImageForCurrentFrame() override { return PaintImage(); }
- void Draw(cc::PaintCanvas*,
- const cc::PaintFlags&,
- const FloatRect& dst_rect,
- const FloatRect& src_rect,
- RespectImageOrientationEnum,
- ImageClampingMode,
- ImageDecodingMode) override {}
-};
-
-class DarkModeImageClassifierTest : public testing::Test {
- public:
// Loads the image from |file_name|.
scoped_refptr<BitmapImage> GetImage(const String& file_name) {
SCOPED_TRACE(file_name);
@@ -66,42 +38,19 @@ class DarkModeImageClassifierTest : public testing::Test {
return image;
}
- // Computes features into |features|.
- void GetFeatures(scoped_refptr<BitmapImage> image,
- DarkModeImageClassifier::Features* features) {
- CHECK(features);
- dark_mode_image_classifier_.SetImageType(
- DarkModeImageClassifier::ImageType::kBitmap);
- auto features_or_null = dark_mode_image_classifier_.GetFeatures(
- image.get(), FloatRect(0, 0, image->width(), image->height()));
- CHECK(features_or_null.has_value());
- (*features) = features_or_null.value();
- }
-
- // Returns the classification result.
- bool GetClassification(const DarkModeImageClassifier::Features features) {
- DarkModeClassification result =
- dark_mode_generic_classifier_.ClassifyWithFeatures(features);
- return result == DarkModeClassification::kApplyFilter;
- }
-
DarkModeImageClassifier* image_classifier() {
- return &dark_mode_image_classifier_;
- }
-
- DarkModeGenericClassifier* generic_classifier() {
- return &dark_mode_generic_classifier_;
+ return dark_mode_image_classifier_.get();
}
protected:
ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
platform_;
- DarkModeImageClassifier dark_mode_image_classifier_;
- DarkModeGenericClassifier dark_mode_generic_classifier_;
+ std::unique_ptr<DarkModeImageClassifier> dark_mode_image_classifier_;
};
TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
DarkModeImageClassifier::Features features;
+ scoped_refptr<BitmapImage> image;
// Test Case 1:
// Grayscale
@@ -111,13 +60,16 @@ TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
// The data members of DarkModeImageClassifier have to be reset for every
// image as the same classifier object is used for all the tests.
- image_classifier()->ResetDataMembersToDefaults();
- GetFeatures(GetImage("/images/resources/grid-large.png"), &features);
- EXPECT_TRUE(GetClassification(features));
- EXPECT_EQ(generic_classifier()->ClassifyUsingDecisionTreeForTesting(features),
+ image = GetImage("/images/resources/grid-large.png");
+ features = image_classifier()
+ ->GetFeatures(image->PaintImageForCurrentFrame(),
+ SkRect::MakeWH(image->width(), image->height()))
+ .value();
+ EXPECT_EQ(image_classifier()->ClassifyWithFeatures(features),
+ DarkModeClassification::kApplyFilter);
+ EXPECT_EQ(image_classifier()->ClassifyUsingDecisionTree(features),
DarkModeClassification::kApplyFilter);
EXPECT_FALSE(features.is_colorful);
- EXPECT_FALSE(features.is_svg);
EXPECT_NEAR(0.1875f, features.color_buckets_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.transparency_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.background_ratio, kEpsilon);
@@ -127,13 +79,16 @@ TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
// Color Buckets Ratio: Medium
// Decision Tree: Can't Decide
// Neural Network: Apply
- image_classifier()->ResetDataMembersToDefaults();
- GetFeatures(GetImage("/images/resources/apng08-ref.png"), &features);
- EXPECT_FALSE(GetClassification(features));
- EXPECT_EQ(generic_classifier()->ClassifyUsingDecisionTreeForTesting(features),
+ image = GetImage("/images/resources/apng08-ref.png");
+ features = image_classifier()
+ ->GetFeatures(image->PaintImageForCurrentFrame(),
+ SkRect::MakeWH(image->width(), image->height()))
+ .value();
+ EXPECT_EQ(image_classifier()->ClassifyWithFeatures(features),
+ DarkModeClassification::kDoNotApplyFilter);
+ EXPECT_EQ(image_classifier()->ClassifyUsingDecisionTree(features),
DarkModeClassification::kNotClassified);
EXPECT_FALSE(features.is_colorful);
- EXPECT_FALSE(features.is_svg);
EXPECT_NEAR(0.8125f, features.color_buckets_ratio, kEpsilon);
EXPECT_NEAR(0.446667f, features.transparency_ratio, kEpsilon);
EXPECT_NEAR(0.03f, features.background_ratio, kEpsilon);
@@ -143,13 +98,16 @@ TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
// Color Buckets Ratio: Low
// Decision Tree: Apply
// Neural Network: NA.
- image_classifier()->ResetDataMembersToDefaults();
- GetFeatures(GetImage("/images/resources/twitter_favicon.ico"), &features);
- EXPECT_TRUE(GetClassification(features));
- EXPECT_EQ(generic_classifier()->ClassifyUsingDecisionTreeForTesting(features),
+ image = GetImage("/images/resources/twitter_favicon.ico");
+ features = image_classifier()
+ ->GetFeatures(image->PaintImageForCurrentFrame(),
+ SkRect::MakeWH(image->width(), image->height()))
+ .value();
+ EXPECT_EQ(image_classifier()->ClassifyWithFeatures(features),
+ DarkModeClassification::kApplyFilter);
+ EXPECT_EQ(image_classifier()->ClassifyUsingDecisionTree(features),
DarkModeClassification::kApplyFilter);
EXPECT_TRUE(features.is_colorful);
- EXPECT_FALSE(features.is_svg);
EXPECT_NEAR(0.0002441f, features.color_buckets_ratio, kEpsilon);
EXPECT_NEAR(0.542092f, features.transparency_ratio, kEpsilon);
EXPECT_NEAR(0.1500000f, features.background_ratio, kEpsilon);
@@ -159,14 +117,16 @@ TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
// Color Buckets Ratio: High
// Decision Tree: Do Not Apply
// Neural Network: NA.
- image_classifier()->ResetDataMembersToDefaults();
- GetFeatures(GetImage("/images/resources/blue-wheel-srgb-color-profile.png"),
- &features);
- EXPECT_FALSE(GetClassification(features));
- EXPECT_EQ(generic_classifier()->ClassifyUsingDecisionTreeForTesting(features),
+ image = GetImage("/images/resources/blue-wheel-srgb-color-profile.png");
+ features = image_classifier()
+ ->GetFeatures(image->PaintImageForCurrentFrame(),
+ SkRect::MakeWH(image->width(), image->height()))
+ .value();
+ EXPECT_EQ(image_classifier()->ClassifyWithFeatures(features),
+ DarkModeClassification::kDoNotApplyFilter);
+ EXPECT_EQ(image_classifier()->ClassifyUsingDecisionTree(features),
DarkModeClassification::kDoNotApplyFilter);
EXPECT_TRUE(features.is_colorful);
- EXPECT_FALSE(features.is_svg);
EXPECT_NEAR(0.032959f, features.color_buckets_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.transparency_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.background_ratio, kEpsilon);
@@ -176,81 +136,57 @@ TEST_F(DarkModeImageClassifierTest, FeaturesAndClassification) {
// Color Buckets Ratio: Medium
// Decision Tree: Apply
// Neural Network: NA.
- image_classifier()->ResetDataMembersToDefaults();
- GetFeatures(GetImage("/images/resources/ycbcr-444-float.jpg"), &features);
- EXPECT_TRUE(GetClassification(features));
- EXPECT_EQ(generic_classifier()->ClassifyUsingDecisionTreeForTesting(features),
+ image = GetImage("/images/resources/ycbcr-444-float.jpg");
+ features = image_classifier()
+ ->GetFeatures(image->PaintImageForCurrentFrame(),
+ SkRect::MakeWH(image->width(), image->height()))
+ .value();
+ EXPECT_EQ(image_classifier()->ClassifyWithFeatures(features),
+ DarkModeClassification::kApplyFilter);
+ EXPECT_EQ(image_classifier()->ClassifyUsingDecisionTree(features),
DarkModeClassification::kApplyFilter);
EXPECT_TRUE(features.is_colorful);
- EXPECT_FALSE(features.is_svg);
EXPECT_NEAR(0.0151367f, features.color_buckets_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.transparency_ratio, kEpsilon);
EXPECT_NEAR(0.0f, features.background_ratio, kEpsilon);
}
TEST_F(DarkModeImageClassifierTest, Caching) {
- scoped_refptr<FakeImageForCacheTest> image = FakeImageForCacheTest::Create();
- FloatRect src_rect1(0, 0, 50, 50);
- FloatRect src_rect2(5, 20, 100, 100);
- FloatRect src_rect3(6, -9, 50, 50);
+ PaintImage::Id image_id = PaintImage::GetNextId();
+ SkRect src1 = SkRect::MakeXYWH(0, 0, 50, 50);
+ SkRect src2 = SkRect::MakeXYWH(5, 20, 100, 100);
+ SkRect src3 = SkRect::MakeXYWH(6, -9, 50, 50);
- EXPECT_EQ(image->GetClassification(src_rect1),
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src1),
DarkModeClassification::kNotClassified);
- image->AddClassification(src_rect1, DarkModeClassification::kApplyFilter);
- EXPECT_EQ(image->GetClassification(src_rect1),
+ image_classifier()->AddCacheValue(image_id, src1,
+ DarkModeClassification::kApplyFilter);
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src1),
DarkModeClassification::kApplyFilter);
- EXPECT_EQ(image->GetClassification(src_rect2),
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src2),
DarkModeClassification::kNotClassified);
- image->AddClassification(src_rect2,
- DarkModeClassification::kDoNotApplyFilter);
- EXPECT_EQ(image->GetClassification(src_rect2),
+ image_classifier()->AddCacheValue(image_id, src2,
+ DarkModeClassification::kDoNotApplyFilter);
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src2),
DarkModeClassification::kDoNotApplyFilter);
- EXPECT_EQ(image->GetClassification(src_rect3),
+ EXPECT_EQ(image_classifier()->GetCacheSize(image_id), 2u);
+ DarkModeImageClassifier::RemoveCache(image_id);
+ EXPECT_EQ(image_classifier()->GetCacheSize(image_id), 0u);
+
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src1),
+ DarkModeClassification::kNotClassified);
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src2),
DarkModeClassification::kNotClassified);
- image->AddClassification(src_rect3, DarkModeClassification::kApplyFilter);
- EXPECT_EQ(image->GetClassification(src_rect3),
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src3),
+ DarkModeClassification::kNotClassified);
+ image_classifier()->AddCacheValue(image_id, src3,
+ DarkModeClassification::kApplyFilter);
+ EXPECT_EQ(image_classifier()->GetCacheValue(image_id, src3),
DarkModeClassification::kApplyFilter);
- EXPECT_EQ(image->GetMapSize(), 3);
-}
-
-TEST_F(DarkModeImageClassifierTest, BlocksCount) {
- scoped_refptr<BitmapImage> image =
- GetImage("/images/resources/grid-large.png");
- DarkModeImageClassifier::Features features;
- image_classifier()->ResetDataMembersToDefaults();
-
- // When the horizontal and vertical blocks counts are lesser than the
- // image dimensions, they should remain unaltered.
- image_classifier()->SetHorizontalBlocksCount((int)(image->width() - 1));
- image_classifier()->SetVerticalBlocksCount((int)(image->height() - 1));
- GetFeatures(image, &features);
- EXPECT_EQ(image_classifier()->HorizontalBlocksCount(),
- (int)(image->width() - 1));
- EXPECT_EQ(image_classifier()->VerticalBlocksCount(),
- (int)(image->height() - 1));
-
- // When the horizontal and vertical blocks counts are lesser than the
- // image dimensions, they should remain unaltered.
- image_classifier()->SetHorizontalBlocksCount((int)(image->width()));
- image_classifier()->SetVerticalBlocksCount((int)(image->height()));
- GetFeatures(image, &features);
- EXPECT_EQ(image_classifier()->HorizontalBlocksCount(),
- (int)(image->width()));
- EXPECT_EQ(image_classifier()->VerticalBlocksCount(),
- (int)(image->height()));
-
- // When the horizontal and vertical blocks counts are greater than the
- // image dimensions, they should be reduced.
- image_classifier()->SetHorizontalBlocksCount((int)(image->width() + 1));
- image_classifier()->SetVerticalBlocksCount((int)(image->height() + 1));
- GetFeatures(image, &features);
- EXPECT_EQ(image_classifier()->HorizontalBlocksCount(),
- floor(image->width()));
- EXPECT_EQ(image_classifier()->VerticalBlocksCount(),
- floor(image->height()));
+ EXPECT_EQ(image_classifier()->GetCacheSize(image_id), 1u);
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_settings.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
index 50dad6f6644..f21bd52bc6b 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
@@ -35,11 +35,6 @@ enum class DarkModePagePolicy {
kFilterByBackground,
};
-enum class DarkModeClassifierType {
- kIcon,
- kGeneric,
-};
-
// New variables added to this struct should also be added to
// BuildDarkModeSettings() in
// //src/third_party/blink/renderer/core/accessibility/apply_dark_mode.h
@@ -49,7 +44,6 @@ struct DarkModeSettings {
float image_grayscale_percent = 0.0; // Valid range from 0.0 to 1.0
float contrast = 0.0; // Valid range from -1.0 to 1.0
DarkModeImagePolicy image_policy = DarkModeImagePolicy::kFilterNone;
- DarkModeClassifierType classifier_type = DarkModeClassifierType::kGeneric;
// Text colors with brightness below this threshold will be inverted, and
// above it will be left as in the original, non-dark-mode page. Set to 256
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 a8cc839fa98..484c24b7756 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
@@ -112,7 +112,7 @@ TEST(DeferredImageDecoderTestWoPlatform, fragmentedSignature) {
// Truncated signature (only 1 byte). Decoder instantiation should fail.
scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create<size_t>(data, 1u);
- EXPECT_FALSE(ImageDecoder::HasSufficientDataToSniffImageType(*buffer));
+ EXPECT_FALSE(ImageDecoder::HasSufficientDataToSniffMimeType(*buffer));
EXPECT_EQ(nullptr, DeferredImageDecoder::Create(
buffer, false, ImageDecoder::kAlphaPremultiplied,
ColorBehavior::Ignore()));
@@ -120,7 +120,7 @@ TEST(DeferredImageDecoderTestWoPlatform, fragmentedSignature) {
// Append the rest of the data. We should be able to sniff the signature
// now, even if segmented.
buffer->Append<size_t>(data + 1, contiguous.size() - 1);
- EXPECT_TRUE(ImageDecoder::HasSufficientDataToSniffImageType(*buffer));
+ EXPECT_TRUE(ImageDecoder::HasSufficientDataToSniffMimeType(*buffer));
std::unique_ptr<DeferredImageDecoder> decoder =
DeferredImageDecoder::Create(buffer, false,
ImageDecoder::kAlphaPremultiplied,
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 c1ae00d3ba2..809c889adf5 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(Visitor* visitor) {
+void Filter::Trace(Visitor* visitor) const {
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 efd30aa6721..7873b0de347 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(Visitor*);
+ void Trace(Visitor*) const;
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 1b8dd7a802e..93c09b3e0ad 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(Visitor* visitor) {
+void FilterEffect::Trace(Visitor* visitor) const {
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 1f3e99d19fe..15c66cbfcdd 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(Visitor*);
+ virtual void Trace(Visitor*) const;
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 666cc1d5925..eb5033227ae 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc
@@ -50,7 +50,7 @@ void GeneratedImage::DrawPattern(
FloatRect tile_rect = src_rect;
tile_rect.Expand(repeat_spacing);
- SkMatrix pattern_matrix = SkMatrix::MakeTrans(phase.X(), phase.Y());
+ SkMatrix pattern_matrix = SkMatrix::Translate(phase.X(), phase.Y());
pattern_matrix.preScale(scale.Width(), scale.Height());
pattern_matrix.preTranslate(tile_rect.X(), tile_rect.Y());
@@ -70,7 +70,8 @@ sk_sp<PaintShader> GeneratedImage::CreateShader(
const SkMatrix* pattern_matrix,
const FloatRect& src_rect,
RespectImageOrientationEnum respect_orientation) {
- auto paint_controller = std::make_unique<PaintController>();
+ auto paint_controller =
+ std::make_unique<PaintController>(PaintController::kTransient);
GraphicsContext context(*paint_controller);
context.BeginRecording(tile_rect);
DrawTile(context, src_rect, respect_orientation);
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 9017db5231a..e0aeaba9eba 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
@@ -76,6 +76,14 @@ bool g_should_fail_drawing_buffer_creation_for_testing = false;
} // namespace
+// Increase cache to avoid reallocation on fuchsia, see
+// https://crbug.com/1087941.
+#if defined(OS_FUCHSIA)
+const size_t DrawingBuffer::kDefaultColorBufferCacheLimit = 2;
+#else
+const size_t DrawingBuffer::kDefaultColorBufferCacheLimit = 1;
+#endif
+
// Function defined in third_party/blink/public/web/blink.h.
void ForceNextDrawingBufferCreationToFailForTest() {
g_should_fail_drawing_buffer_creation_for_testing = true;
@@ -95,6 +103,7 @@ scoped_refptr<DrawingBuffer> DrawingBuffer::Create(
PreserveDrawingBuffer preserve,
WebGLVersion webgl_version,
ChromiumImageUsage chromium_image_usage,
+ SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
gl::GpuPreference gpu_preference) {
if (g_should_fail_drawing_buffer_creation_for_testing) {
@@ -147,7 +156,7 @@ scoped_refptr<DrawingBuffer> DrawingBuffer::Create(
std::move(extensions_util), client, discard_framebuffer_supported,
want_alpha_channel, premultiplied_alpha, preserve, webgl_version,
want_depth_buffer, want_stencil_buffer, chromium_image_usage,
- color_params, gpu_preference));
+ filter_quality, color_params, gpu_preference));
if (!drawing_buffer->Initialize(size, multisample_supported)) {
drawing_buffer->BeginDestruction();
return scoped_refptr<DrawingBuffer>();
@@ -169,6 +178,7 @@ DrawingBuffer::DrawingBuffer(
bool want_depth,
bool want_stencil,
ChromiumImageUsage chromium_image_usage,
+ SkFilterQuality filter_quality,
const CanvasColorParams& color_params,
gl::GpuPreference gpu_preference)
: client_(client),
@@ -189,6 +199,7 @@ DrawingBuffer::DrawingBuffer(
sampler_color_space_(color_params.GetSamplerGfxColorSpace()),
use_half_float_storage_(color_params.PixelFormat() ==
CanvasPixelFormat::kF16),
+ filter_quality_(filter_quality),
chromium_image_usage_(chromium_image_usage),
opengl_flip_y_extension_(
ContextProvider()->GetCapabilities().mesa_framebuffer_flip_y),
@@ -338,7 +349,11 @@ bool DrawingBuffer::PrepareTransferableResourceInternal(
// 4. Here.
return false;
}
- DCHECK(!is_hidden_);
+
+ // There used to be a DCHECK(!is_hidden_) here, but in some tab
+ // switching scenarios, it seems that this can racily be called for
+ // backgrounded tabs.
+
if (!contents_changed_)
return false;
@@ -550,7 +565,7 @@ void DrawingBuffer::MailboxReleasedGpu(scoped_refptr<ColorBuffer> color_buffer,
// Creation of image backed mailboxes is very expensive, so be less
// aggressive about pruning them. Pruning is done in FIFO order.
- size_t cache_limit = 1;
+ size_t cache_limit = kDefaultColorBufferCacheLimit;
if (ShouldUseChromiumImage())
cache_limit = 4;
while (recycled_color_buffer_queue_.size() >= cache_limit)
@@ -1382,6 +1397,7 @@ void DrawingBuffer::RestoreAllState() {
client_->DrawingBufferClientRestoreMaskAndClearValues();
client_->DrawingBufferClientRestorePixelPackParameters();
client_->DrawingBufferClientRestoreTexture2DBinding();
+ client_->DrawingBufferClientRestoreTextureCubeMapBinding();
client_->DrawingBufferClientRestoreRenderbufferBinding();
client_->DrawingBufferClientRestoreFramebufferBinding();
client_->DrawingBufferClientRestorePixelUnpackBufferBinding();
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 480209908b8..cfbd4502db0 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
@@ -98,6 +98,8 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
virtual void DrawingBufferClientRestorePixelPackParameters() = 0;
// Restores the GL_TEXTURE_2D binding for the active texture unit only.
virtual void DrawingBufferClientRestoreTexture2DBinding() = 0;
+ // Restores the GL_TEXTURE_CUBE_MAP binding for the active texture unit.
+ virtual void DrawingBufferClientRestoreTextureCubeMapBinding() = 0;
virtual void DrawingBufferClientRestoreRenderbufferBinding() = 0;
virtual void DrawingBufferClientRestoreFramebufferBinding() = 0;
virtual void DrawingBufferClientRestorePixelUnpackBufferBinding() = 0;
@@ -136,6 +138,7 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
PreserveDrawingBuffer,
WebGLVersion,
ChromiumImageUsage,
+ SkFilterQuality,
const CanvasColorParams&,
gl::GpuPreference);
@@ -281,6 +284,8 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
scoped_refptr<CanvasResource> AsCanvasResource(
base::WeakPtr<CanvasResourceProvider> resource_provider);
+ static const size_t kDefaultColorBufferCacheLimit;
+
protected: // For unittests
DrawingBuffer(std::unique_ptr<WebGraphicsContext3DProvider>,
bool using_gpu_compositing,
@@ -295,6 +300,7 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient,
bool wants_depth,
bool wants_stencil,
ChromiumImageUsage,
+ SkFilterQuality,
const CanvasColorParams&,
gl::GpuPreference gpu_preference);
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 537c1af0001..8509c15a852 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
@@ -246,58 +246,64 @@ TEST_F(DrawingBufferTest, VerifySharedImagesReleasedAfterReleaseCallback) {
drawing_buffer_->BeginDestruction();
}
-TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) {
- 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;
+TEST_F(DrawingBufferTest, VerifyCachedRecycledResourcesAreKept) {
+ const size_t kNumResources = DrawingBuffer::kDefaultColorBufferCacheLimit + 1;
+ std::vector<viz::TransferableResource> resources(kNumResources);
+ std::vector<std::unique_ptr<viz::SingleReleaseCallback>> release_callbacks(
+ kNumResources);
// Produce resources.
- EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
- EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1,
- &release_callback1));
- EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
- EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2,
- &release_callback2));
- EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
- EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3,
- &release_callback3));
+ for (size_t i = 0; i < kNumResources; ++i) {
+ drawing_buffer_->MarkContentsChanged();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &resources[i], &release_callbacks[i]));
+ }
- // Release resources by specific order; 1, 3, 2.
- EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
- release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
- EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
- release_callback3->Run(gpu::SyncToken(), false /* lostResource */);
- EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
- release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ // Release resources.
+ for (auto& release_callback : release_callbacks) {
+ drawing_buffer_->MarkContentsChanged();
+ release_callback->Run(gpu::SyncToken(), false /* lostResource */);
+ }
- // The first recycled resource must be 2. 1 and 3 were deleted by FIFO order
- // because DrawingBuffer never keeps more than one resource.
- viz::TransferableResource recycled_resource1;
- std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback1;
- EXPECT_FALSE(drawing_buffer_->MarkContentsChanged());
- EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
- nullptr, &recycled_resource1, &recycled_release_callback1));
- EXPECT_EQ(resource2.mailbox_holder.mailbox,
- recycled_resource1.mailbox_holder.mailbox);
+ std::vector<std::unique_ptr<viz::SingleReleaseCallback>>
+ recycled_release_callbacks(DrawingBuffer::kDefaultColorBufferCacheLimit);
+
+ // The first recycled resource must be from the cache
+ for (size_t i = 0; i < DrawingBuffer::kDefaultColorBufferCacheLimit; ++i) {
+ viz::TransferableResource recycled_resource;
+ std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback;
+ drawing_buffer_->MarkContentsChanged();
+ EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
+ nullptr, &recycled_resource, &recycled_release_callbacks[i]));
+
+ bool recycled = false;
+ for (auto& resource : resources) {
+ if (recycled_resource.mailbox_holder.mailbox ==
+ resource.mailbox_holder.mailbox) {
+ recycled = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(recycled);
+ }
- // The second recycled resource must be a new resource.
- viz::TransferableResource recycled_resource2;
- std::unique_ptr<viz::SingleReleaseCallback> recycled_release_callback2;
- EXPECT_TRUE(drawing_buffer_->MarkContentsChanged());
+ // The next recycled resource must be a new resource.
+ viz::TransferableResource next_recycled_resource;
+ std::unique_ptr<viz::SingleReleaseCallback> next_recycled_release_callback;
+ drawing_buffer_->MarkContentsChanged();
EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(
- nullptr, &recycled_resource2, &recycled_release_callback2));
- EXPECT_NE(resource1.mailbox_holder.mailbox,
- recycled_resource2.mailbox_holder.mailbox);
- EXPECT_NE(resource2.mailbox_holder.mailbox,
- recycled_resource2.mailbox_holder.mailbox);
- EXPECT_NE(resource3.mailbox_holder.mailbox,
- recycled_resource2.mailbox_holder.mailbox);
-
- recycled_release_callback1->Run(gpu::SyncToken(), false /* lostResource */);
- recycled_release_callback2->Run(gpu::SyncToken(), false /* lostResource */);
+ nullptr, &next_recycled_resource, &next_recycled_release_callback));
+ for (auto& resource : resources) {
+ EXPECT_NE(resource.mailbox_holder.mailbox,
+ next_recycled_resource.mailbox_holder.mailbox);
+ }
+ recycled_release_callbacks.push_back(
+ std::move(next_recycled_release_callback));
+
+ // Cleanup
+ for (auto& release_cb : recycled_release_callbacks) {
+ release_cb->Run(gpu::SyncToken(), false /* lostResource */);
+ }
drawing_buffer_->BeginDestruction();
}
@@ -679,7 +685,8 @@ TEST(DrawingBufferDepthStencilTest, packedDepthStencilSupported) {
IntSize(10, 10), premultiplied_alpha, want_alpha_channel,
want_depth_buffer, want_stencil_buffer, want_antialiasing, preserve,
DrawingBuffer::kWebGL1, DrawingBuffer::kAllowChromiumImage,
- CanvasColorParams(), gl::GpuPreference::kHighPerformance);
+ kLow_SkFilterQuality, CanvasColorParams(),
+ gl::GpuPreference::kHighPerformance);
// When we request a depth or a stencil buffer, we will get both.
EXPECT_EQ(cases[i].request_depth || cases[i].request_stencil,
@@ -749,7 +756,8 @@ TEST_F(DrawingBufferTest,
nullptr, gpu_compositing, false /* using_swap_chain */, nullptr,
too_big_size, false, false, false, false, false, DrawingBuffer::kDiscard,
DrawingBuffer::kWebGL1, DrawingBuffer::kAllowChromiumImage,
- CanvasColorParams(), gl::GpuPreference::kHighPerformance);
+ kLow_SkFilterQuality, CanvasColorParams(),
+ gl::GpuPreference::kHighPerformance);
EXPECT_EQ(too_big_drawing_buffer, nullptr);
drawing_buffer_->BeginDestruction();
}
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 c71278a7b75..f0f8dd37990 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
@@ -92,6 +92,8 @@ class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
void BindTexture(GLenum target, GLuint texture) override {
if (target == GL_TEXTURE_2D)
state_.active_texture2d_binding = texture;
+ if (target == GL_TEXTURE_CUBE_MAP)
+ state_.active_texturecubemap_binding = texture;
bound_textures_.insert(target, texture);
}
@@ -318,6 +320,10 @@ class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
void DrawingBufferClientRestoreTexture2DBinding() override {
state_.active_texture2d_binding = saved_state_.active_texture2d_binding;
}
+ void DrawingBufferClientRestoreTextureCubeMapBinding() override {
+ state_.active_texturecubemap_binding =
+ saved_state_.active_texturecubemap_binding;
+ }
void DrawingBufferClientRestoreRenderbufferBinding() override {
state_.renderbuffer_binding = saved_state_.renderbuffer_binding;
}
@@ -367,6 +373,8 @@ class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
EXPECT_EQ(state_.pack_alignment, saved_state_.pack_alignment);
EXPECT_EQ(state_.active_texture2d_binding,
saved_state_.active_texture2d_binding);
+ EXPECT_EQ(state_.active_texturecubemap_binding,
+ saved_state_.active_texturecubemap_binding);
EXPECT_EQ(state_.renderbuffer_binding, saved_state_.renderbuffer_binding);
EXPECT_EQ(state_.draw_framebuffer_binding,
saved_state_.draw_framebuffer_binding);
@@ -408,6 +416,8 @@ class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub,
// The bound 2D texture for the active texture unit.
GLuint active_texture2d_binding = 0;
+ // The bound cube map texture for the active texture unit.
+ GLuint active_texturecubemap_binding = 0;
GLuint renderbuffer_binding = 0;
GLuint draw_framebuffer_binding = 0;
GLuint read_framebuffer_binding = 0;
@@ -471,6 +481,7 @@ class DrawingBufferForTests : public DrawingBuffer {
false /* wantDepth */,
false /* wantStencil */,
DrawingBuffer::kAllowChromiumImage /* ChromiumImageUsage */,
+ kLow_SkFilterQuality,
CanvasColorParams(),
gl::GpuPreference::kHighPerformance),
live_(nullptr) {}
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 e8a21596af5..c6afa795ccc 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
@@ -40,7 +40,7 @@ scoped_refptr<StaticBitmapImage> MakeAccelerated(
auto provider = CanvasResourceProvider::CreateSharedImageProvider(
source->Size(), context_provider_wrapper, kLow_SkFilterQuality,
CanvasColorParams(paint_image.GetSkImage()->imageInfo()),
- source->IsOriginTopLeft(), CanvasResourceProvider::RasterMode::kGPU,
+ source->IsOriginTopLeft(), RasterMode::kGPU,
gpu::SHARED_IMAGE_USAGE_DISPLAY);
if (!provider || !provider->IsAccelerated())
return nullptr;
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 acfd1b59240..9ca6afb7aa6 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
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 82b5c4a5fd9..468f7b56a0b 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
@@ -168,8 +168,8 @@ TEST_F(SharedGpuContextTest, Canvas2DLayerBridgeAutoRecovery) {
IntSize size(10, 10);
CanvasColorParams color_params;
std::unique_ptr<Canvas2DLayerBridge> bridge =
- std::make_unique<Canvas2DLayerBridge>(
- size, Canvas2DLayerBridge::kEnableAcceleration, color_params);
+ std::make_unique<Canvas2DLayerBridge>(size, RasterMode::kGPU,
+ color_params);
EXPECT_TRUE(bridge->IsAccelerated());
EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring());
}
@@ -195,7 +195,7 @@ TEST_F(BadSharedGpuContextTest, AccelerateImageBufferSurfaceCreationFails) {
CanvasResourceProvider::CreateSharedImageProvider(
size, SharedGpuContext::ContextProviderWrapper(),
kLow_SkFilterQuality, CanvasColorParams(),
- true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU,
+ true /*is_origin_top_left*/, RasterMode::kGPU,
0u /*shared_image_usage_flags*/);
EXPECT_FALSE(resource_provider);
}
@@ -222,7 +222,7 @@ TEST_F(SharedGpuContextTestViz, AccelerateImageBufferSurfaceAutoRecovery) {
CanvasResourceProvider::CreateSharedImageProvider(
size, SharedGpuContext::ContextProviderWrapper(),
kLow_SkFilterQuality, CanvasColorParams(),
- true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU,
+ true /*is_origin_top_left*/, RasterMode::kGPU,
0u /*shared_image_usage_flags*/);
EXPECT_TRUE(resource_provider && resource_provider->IsValid());
EXPECT_TRUE(resource_provider->IsAccelerated());
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 011f6331bfc..25f68839ec3 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h"
+#include <limits>
#include <memory>
#include "base/compiler_specific.h"
@@ -43,6 +44,29 @@ int32_t ClampMin(int32_t value) {
return value < kMinInt32Value ? kMinInt32Value : value;
}
+template <class T>
+T ClampImpl(const float& v, const T& lo, const T& hi) {
+ return (v < lo) ? lo : ((hi < v) ? hi : static_cast<T>(v));
+}
+
+template <class T>
+T ClampFloat(float value) {
+ if (std::numeric_limits<T>::is_signed) {
+ // Generate an equal number of positive and negative values. Two's
+ // complement has one more negative number than positive number.
+ return ClampImpl<T>(value, std::numeric_limits<T>::min() + 1,
+ std::numeric_limits<T>::max());
+ } else {
+ return ClampImpl<T>(value, std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ }
+}
+
+template <class T>
+T ClampAndScaleFloat(float value) {
+ return ClampFloat<T>(value * std::numeric_limits<T>::max());
+}
+
// Return kDataFormatNumFormats if format/type combination is invalid.
WebGLImageConversion::DataFormat GetDataFormat(GLenum destination_format,
GLenum destination_type) {
@@ -262,7 +286,7 @@ WebGLImageConversion::DataFormat GetDataFormat(GLenum destination_format,
}
// The following Float to Half-Float conversion code is from the implementation
-// of ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf, "Fast Half
+// of http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf , "Fast Half
// Float Conversions" by Jeroen van der Zijp, November 2008 (Revised September
// 2010). Specially, the basetable[512] and shifttable[512] are generated as
// follows:
@@ -309,7 +333,7 @@ void generatetables(){
}
*/
-uint16_t g_base_table[512] = {
+const uint16_t g_base_table[512] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -358,7 +382,7 @@ uint16_t g_base_table[512] = {
64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
64512, 64512, 64512, 64512, 64512, 64512};
-unsigned char g_shift_table[512] = {
+const unsigned char g_shift_table[512] = {
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
@@ -394,6 +418,421 @@ uint16_t ConvertFloatToHalfFloat(float f) {
((temp & 0x007fffff) >> g_shift_table[signexp]);
}
+// The mantissatable[2048], offsettable[64] and exponenttable[64] are
+// generated as follows:
+/*
+unsigned int mantissatable[2048];
+unsigned short offsettable[64];
+unsigned int exponenttable[64];
+
+unsigned int convertmantissa(unsigned int i) {
+ unsigned int m=i<<13; // Zero pad mantissa bits
+ unsigned int e=0; // Zero exponent
+ while(!(m&0x00800000)){ // While not normalized
+ e-=0x00800000; // Decrement exponent (1<<23)
+ m<<=1; // Shift mantissa
+ }
+ m&=~0x00800000; // Clear leading 1 bit
+ e+=0x38800000; // Adjust bias ((127-14)<<23)
+ return m | e; // Return combined number
+}
+
+void generatef16tof32tables() {
+ int i;
+ mantissatable[0] = 0;
+ for (i = 1; i <= 1023; ++i)
+ mantissatable[i] = convertmantissa(i);
+ for (i = 1024; i <= 2047; ++i)
+ mantissatable[i] = 0x38000000 + ((i-1024)<<13);
+
+ exponenttable[0] = 0;
+ exponenttable[32]= 0x80000000;
+ for (int i = 1; i <= 30; ++i)
+ exponenttable[i] = i<<23;
+ for (int i = 33; i <= 62; ++i)
+ exponenttable[i] = 0x80000000 + ((i-32)<<23);
+ exponenttable[31]= 0x47800000;
+ exponenttable[63]= 0xC7800000;
+
+ for (i = 0; i <= 63; ++i)
+ offsettable[i] = 1024;
+ offsettable[0] = 0;
+ offsettable[32] = 0;
+}
+*/
+
+const uint32_t g_mantissa_table[2048] = {
+ 0x0, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000,
+ 0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,
+ 0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000,
+ 0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000,
+ 0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000,
+ 0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000,
+ 0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000,
+ 0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000,
+ 0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000,
+ 0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000,
+ 0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000,
+ 0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000,
+ 0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000,
+ 0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000,
+ 0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000,
+ 0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000,
+ 0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000,
+ 0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000,
+ 0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000,
+ 0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000,
+ 0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000,
+ 0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000,
+ 0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000,
+ 0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000,
+ 0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000,
+ 0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000,
+ 0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000,
+ 0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000,
+ 0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000,
+ 0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000,
+ 0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000,
+ 0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000,
+ 0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000,
+ 0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000,
+ 0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000,
+ 0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000,
+ 0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000,
+ 0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000,
+ 0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000,
+ 0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000,
+ 0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000,
+ 0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000,
+ 0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000,
+ 0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000,
+ 0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000,
+ 0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000,
+ 0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000,
+ 0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000,
+ 0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000,
+ 0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000,
+ 0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000,
+ 0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000,
+ 0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000,
+ 0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000,
+ 0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000,
+ 0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000,
+ 0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000,
+ 0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000,
+ 0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000,
+ 0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000,
+ 0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000,
+ 0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000,
+ 0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000,
+ 0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000,
+ 0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000,
+ 0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000,
+ 0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000,
+ 0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000,
+ 0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000,
+ 0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000,
+ 0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000,
+ 0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000,
+ 0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000,
+ 0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000,
+ 0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000,
+ 0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000,
+ 0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000,
+ 0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000,
+ 0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000,
+ 0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000,
+ 0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000,
+ 0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000,
+ 0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000,
+ 0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000,
+ 0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000,
+ 0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000,
+ 0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000,
+ 0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000,
+ 0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000,
+ 0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000,
+ 0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000,
+ 0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000,
+ 0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000,
+ 0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000,
+ 0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000,
+ 0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000,
+ 0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000,
+ 0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000,
+ 0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000,
+ 0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000,
+ 0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000,
+ 0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000,
+ 0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000,
+ 0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000,
+ 0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000,
+ 0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000,
+ 0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000,
+ 0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000,
+ 0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000,
+ 0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000,
+ 0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000,
+ 0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000,
+ 0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000,
+ 0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000,
+ 0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000,
+ 0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000,
+ 0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000,
+ 0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000,
+ 0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000,
+ 0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000,
+ 0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000,
+ 0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000,
+ 0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000,
+ 0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000,
+ 0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000,
+ 0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000,
+ 0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000,
+ 0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000,
+ 0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000,
+ 0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000,
+ 0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000,
+ 0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000,
+ 0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000,
+ 0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000,
+ 0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000,
+ 0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000,
+ 0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000,
+ 0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000,
+ 0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000,
+ 0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000,
+ 0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000,
+ 0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000,
+ 0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000,
+ 0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000,
+ 0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000,
+ 0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000,
+ 0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000,
+ 0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000,
+ 0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000,
+ 0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000,
+ 0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000,
+ 0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000,
+ 0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000,
+ 0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000,
+ 0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000,
+ 0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000,
+ 0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000,
+ 0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000,
+ 0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000,
+ 0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000,
+ 0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000,
+ 0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000,
+ 0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000,
+ 0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000,
+ 0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000,
+ 0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000,
+ 0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000,
+ 0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000,
+ 0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000,
+ 0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000,
+ 0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000,
+ 0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000,
+ 0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000,
+ 0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000,
+ 0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000,
+ 0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000,
+ 0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000,
+ 0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000,
+ 0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000,
+ 0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000,
+ 0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000,
+ 0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000,
+ 0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000,
+ 0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000,
+ 0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000,
+ 0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000,
+ 0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000,
+ 0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000,
+ 0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000,
+ 0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000,
+ 0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000,
+ 0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000,
+ 0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000,
+ 0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000,
+ 0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000,
+ 0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000,
+ 0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000,
+ 0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000,
+ 0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000,
+ 0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000,
+ 0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000,
+ 0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000,
+ 0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000,
+ 0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000,
+ 0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000,
+ 0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000,
+ 0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000,
+ 0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000,
+ 0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000,
+ 0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000,
+ 0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000,
+ 0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000,
+ 0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000,
+ 0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000,
+ 0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000,
+ 0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000,
+ 0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000,
+ 0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000,
+ 0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000,
+ 0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000,
+ 0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000,
+ 0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000,
+ 0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000,
+ 0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000,
+ 0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000,
+ 0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000,
+ 0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000,
+ 0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000,
+ 0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000,
+ 0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000,
+ 0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000,
+ 0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000,
+ 0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000,
+ 0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000,
+ 0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000,
+ 0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000,
+ 0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000,
+ 0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000,
+ 0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000,
+ 0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000,
+ 0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000,
+ 0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000,
+ 0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000,
+ 0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000,
+ 0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000,
+ 0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000,
+ 0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000,
+ 0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000,
+ 0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000,
+ 0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000,
+ 0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000,
+ 0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000,
+ 0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000,
+ 0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000,
+ 0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000,
+ 0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000,
+ 0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000,
+ 0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000,
+ 0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000,
+ 0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000,
+ 0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000,
+ 0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000,
+ 0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000,
+ 0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000,
+ 0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000,
+ 0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000,
+ 0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000,
+ 0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000,
+ 0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000,
+ 0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000,
+ 0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000,
+ 0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000,
+ 0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000,
+ 0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000,
+ 0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000,
+ 0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000,
+ 0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000,
+ 0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000,
+ 0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000,
+ 0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000,
+ 0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000,
+ 0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000,
+ 0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000,
+ 0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000,
+ 0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000,
+ 0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000,
+ 0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000,
+ 0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000,
+ 0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000,
+ 0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000,
+ 0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000,
+ 0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000,
+ 0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000,
+ 0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000,
+ 0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000,
+ 0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000,
+ 0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000,
+ 0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000,
+ 0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000,
+ 0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000,
+ 0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000,
+ 0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000,
+ 0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000,
+ 0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000,
+ 0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000,
+ 0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000,
+ 0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000,
+ 0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000,
+ 0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000,
+ 0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000,
+ 0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000,
+ 0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000,
+ 0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000,
+ 0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000,
+ 0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000,
+ 0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000,
+ 0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000,
+ 0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000,
+ 0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000,
+ 0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000,
+ 0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000,
+ 0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000,
+ 0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000,
+ 0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000,
+ 0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000,
+ 0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000,
+ 0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000,
+ 0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000,
+ 0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000,
+ 0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000,
+ 0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000,
+ 0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000,
+ 0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000,
+ 0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000,
+ 0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000,
+ 0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000,
+ 0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000,
+ 0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000,
+ 0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000,
+ 0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000,
+ 0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000,
+ 0x387fc000, 0x387fe000};
+
+const uint16_t g_offset_table[64] = {
+ 0, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,
+ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024};
+
+const uint32_t g_exponent_table[64] = {
+ 0x0, 0x800000, 0x1000000, 0x1800000, 0x2000000, 0x2800000,
+ 0x3000000, 0x3800000, 0x4000000, 0x4800000, 0x5000000, 0x5800000,
+ 0x6000000, 0x6800000, 0x7000000, 0x7800000, 0x8000000, 0x8800000,
+ 0x9000000, 0x9800000, 0xa000000, 0xa800000, 0xb000000, 0xb800000,
+ 0xc000000, 0xc800000, 0xd000000, 0xd800000, 0xe000000, 0xe800000,
+ 0xf000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000,
+ 0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000,
+ 0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000,
+ 0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000,
+ 0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000,
+ 0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000};
+
+float ConvertHalfFloatToFloat(uint16_t half) {
+ uint32_t temp =
+ g_mantissa_table[g_offset_table[half >> 10] + (half & 0x3ff)] +
+ g_exponent_table[half >> 10];
+ return *(reinterpret_cast<float*>(&temp));
+}
+
/* BEGIN CODE SHARED WITH MOZILLA FIREFOX */
// The following packing and unpacking routines are expressed in terms of
@@ -659,10 +1098,70 @@ void Unpack<WebGLImageConversion::kDataFormatRGBA2_10_10_10, uint32_t, float>(
}
}
+// Used for non-trivial conversions of RGBA16F data.
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA16F, uint16_t, float>(
+ const uint16_t* source,
+ float* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ConvertHalfFloatToFloat(source[0]);
+ destination[1] = ConvertHalfFloatToFloat(source[1]);
+ destination[2] = ConvertHalfFloatToFloat(source[2]);
+ destination[3] = ConvertHalfFloatToFloat(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+// Used for the trivial conversion of RGBA16F data to RGBA8.
+template <>
+void Unpack<WebGLImageConversion::kDataFormatRGBA16F, uint16_t, uint8_t>(
+ const uint16_t* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] =
+ ClampAndScaleFloat<uint8_t>(ConvertHalfFloatToFloat(source[0]));
+ destination[1] =
+ ClampAndScaleFloat<uint8_t>(ConvertHalfFloatToFloat(source[1]));
+ destination[2] =
+ ClampAndScaleFloat<uint8_t>(ConvertHalfFloatToFloat(source[2]));
+ destination[3] =
+ ClampAndScaleFloat<uint8_t>(ConvertHalfFloatToFloat(source[3]));
+ source += 4;
+ destination += 4;
+ }
+}
+
//----------------------------------------------------------------------
// Pixel packing routines.
//
+// All of the formats below refer to the format of the texture being
+// uploaded. Only the formats that accept DOM sources (images, videos,
+// ImageBitmap, etc.) need to:
+//
+// (a) support conversions from "other" formats than the destination
+// format, since the other cases are simply handling Y-flips or alpha
+// premultiplication of data supplied via ArrayBufferView
+//
+// (b) support the kAlphaDoUnmultiply operation, which is needed because
+// there are some DOM-related data sources (like 2D canvas) which are
+// stored in premultiplied form. Note that the alpha-only formats
+// inherently don't need to support the kAlphaDoUnmultiply operation.
+//
+// The formats that accept DOM-related inputs are in the table for
+// texImage2D taking TexImageSource in the WebGL 2.0 specification, plus
+// all of the formats in the WebGL 1.0 specification, including legacy
+// formats like luminance, alpha and luminance-alpha formats (which are
+// renamed in the DataFormat enum to things like "red-alpha"). Extensions
+// like EXT_texture_norm16 add to the supported formats
+//
+// Currently, those texture formats to which DOM-related inputs can be
+// uploaded have to support two basic input formats coming from the rest of
+// the browser: uint8_t, for RGBA8, and float, for RGBA16F.
+
template <int format, int alphaOp, typename SourceType, typename DstType>
void Pack(const SourceType*, DstType*, unsigned) {
NOTREACHED();
@@ -683,6 +1182,20 @@ void Pack<WebGLImageConversion::kDataFormatA8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatR8,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -698,6 +1211,20 @@ void Pack<WebGLImageConversion::kDataFormatR8,
template <>
void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0]);
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint8_t>(const uint8_t* source,
@@ -713,7 +1240,21 @@ void Pack<WebGLImageConversion::kDataFormatR8,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t source_r = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatR8,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -738,6 +1279,22 @@ void Pack<WebGLImageConversion::kDataFormatR8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatR8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ uint8_t source_r = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ destination[0] = source_r;
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRA8,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -754,6 +1311,21 @@ void Pack<WebGLImageConversion::kDataFormatRA8,
template <>
void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint8_t>(const uint8_t* source,
@@ -770,7 +1342,21 @@ void Pack<WebGLImageConversion::kDataFormatRA8,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRA8,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -796,6 +1382,23 @@ void Pack<WebGLImageConversion::kDataFormatRA8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ uint8_t source_r = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ destination[0] = source_r;
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGB8,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -813,6 +1416,22 @@ void Pack<WebGLImageConversion::kDataFormatRGB8,
template <>
void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1]);
+ destination[2] = ClampAndScaleFloat<uint8_t>(source[2]);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint8_t>(const uint8_t* source,
@@ -834,7 +1453,22 @@ void Pack<WebGLImageConversion::kDataFormatRGB8,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ destination[2] = ClampAndScaleFloat<uint8_t>(source[2] * source[3]);
+ source += 4;
+ destination += 3;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGB8,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -859,6 +1493,23 @@ void Pack<WebGLImageConversion::kDataFormatRGB8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGB8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ destination[2] = ClampAndScaleFloat<uint8_t>(source[2] * scale_factor);
+ source += 4;
+ destination += 3;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGBA8,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
@@ -882,7 +1533,23 @@ void Pack<WebGLImageConversion::kDataFormatRGBA8,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ destination[2] = ClampAndScaleFloat<uint8_t>(source[2] * source[3]);
+ destination[3] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA8,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -914,6 +1581,24 @@ void Pack<WebGLImageConversion::kDataFormatRGBA8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ destination[2] = ClampAndScaleFloat<uint8_t>(source[2] * scale_factor);
+ destination[3] = ClampAndScaleFloat<uint8_t>(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGBA4444,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -938,6 +1623,25 @@ void Pack<WebGLImageConversion::kDataFormatRGBA4444,
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2]);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF0) << 8) | ((g & 0xF0) << 4) | (b & 0xF0) | (a >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint16_t>(const uint8_t* source,
@@ -958,7 +1662,25 @@ void Pack<WebGLImageConversion::kDataFormatRGBA4444,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * source[3]);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF0) << 8) | ((g & 0xF0) << 4) | (b & 0xF0) | (a >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA4444,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -982,6 +1704,26 @@ void Pack<WebGLImageConversion::kDataFormatRGBA4444,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA4444,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * scale_factor);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF0) << 8) | ((g & 0xF0) << 4) | (b & 0xF0) | (a >> 4));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGBA5551,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -1006,6 +1748,25 @@ void Pack<WebGLImageConversion::kDataFormatRGBA5551,
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2]);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF8) << 8) | ((g & 0xF8) << 3) | ((b & 0xF8) >> 2) | (a >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint16_t>(const uint8_t* source,
@@ -1026,7 +1787,25 @@ void Pack<WebGLImageConversion::kDataFormatRGBA5551,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * source[3]);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF8) << 8) | ((g & 0xF8) << 3) | ((b & 0xF8) >> 2) | (a >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA5551,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -1050,6 +1829,26 @@ void Pack<WebGLImageConversion::kDataFormatRGBA5551,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA5551,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * scale_factor);
+ uint8_t a = ClampAndScaleFloat<uint8_t>(source[3]);
+ *destination =
+ (((r & 0xF8) << 8) | ((g & 0xF8) << 3) | ((b & 0xF8) >> 2) | (a >> 7));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGB565,
WebGLImageConversion::kAlphaDoNothing,
uint8_t,
@@ -1074,6 +1873,23 @@ void Pack<WebGLImageConversion::kDataFormatRGB565,
template <>
void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2]);
+ *destination = (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint16_t>(const uint8_t* source,
@@ -1094,7 +1910,23 @@ void Pack<WebGLImageConversion::kDataFormatRGB565,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * source[3]);
+ *destination = (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGB565,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -1118,6 +1950,24 @@ void Pack<WebGLImageConversion::kDataFormatRGB565,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGB565,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ uint8_t r = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ uint8_t g = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ uint8_t b = ClampAndScaleFloat<uint8_t>(source[2] * scale_factor);
+ *destination = (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3));
+ source += 4;
+ destination += 1;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGB32F,
WebGLImageConversion::kAlphaDoNothing,
float,
@@ -1516,6 +2366,9 @@ void Pack<WebGLImageConversion::kDataFormatA16F,
}
}
+// Can not be targeted by DOM uploads, so does not need to support float
+// input data.
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA8_S,
WebGLImageConversion::kAlphaDoPremultiply,
@@ -1559,6 +2412,26 @@ void Pack<WebGLImageConversion::kDataFormatRGBA16,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRGBA16,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint16_t>(const float* source,
+ uint16_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint16_t>(source[0] * source[3]);
+ destination[1] = ClampAndScaleFloat<uint16_t>(source[1] * source[3]);
+ destination[2] = ClampAndScaleFloat<uint16_t>(source[2] * source[3]);
+ destination[3] = ClampAndScaleFloat<uint16_t>(source[3]);
+ source += 4;
+ destination += 4;
+ }
+}
+
+// Can not be targeted by DOM uploads, so does not need to support float
+// input data.
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRGBA16_S,
WebGLImageConversion::kAlphaDoPremultiply,
int16_t,
@@ -1579,6 +2452,9 @@ void Pack<WebGLImageConversion::kDataFormatRGBA16_S,
}
}
+// Can not be targeted by DOM uploads, so does not need to support float
+// input data.
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA32,
WebGLImageConversion::kAlphaDoPremultiply,
@@ -1600,6 +2476,9 @@ void Pack<WebGLImageConversion::kDataFormatRGBA32,
}
}
+// Can not be targeted by DOM uploads, so does not need to support float
+// input data.
+
template <>
void Pack<WebGLImageConversion::kDataFormatRGBA32_S,
WebGLImageConversion::kAlphaDoPremultiply,
@@ -1693,6 +2572,21 @@ void Pack<WebGLImageConversion::kDataFormatRG8,
template <>
void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoNothing,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1]);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
WebGLImageConversion::kAlphaDoPremultiply,
uint8_t,
uint8_t>(const uint8_t* source,
@@ -1709,7 +2603,21 @@ void Pack<WebGLImageConversion::kDataFormatRG8,
}
}
-// FIXME: this routine is lossy and must be removed.
+template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoPremultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * source[3]);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * source[3]);
+ source += 4;
+ destination += 2;
+ }
+}
+
template <>
void Pack<WebGLImageConversion::kDataFormatRG8,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -1730,6 +2638,22 @@ void Pack<WebGLImageConversion::kDataFormatRG8,
}
template <>
+void Pack<WebGLImageConversion::kDataFormatRG8,
+ WebGLImageConversion::kAlphaDoUnmultiply,
+ float,
+ uint8_t>(const float* source,
+ uint8_t* destination,
+ unsigned pixels_per_row) {
+ for (unsigned i = 0; i < pixels_per_row; ++i) {
+ float scale_factor = source[3] ? 1.0f / source[3] : 1.0f;
+ destination[0] = ClampAndScaleFloat<uint8_t>(source[0] * scale_factor);
+ destination[1] = ClampAndScaleFloat<uint8_t>(source[1] * scale_factor);
+ source += 4;
+ destination += 2;
+ }
+}
+
+template <>
void Pack<WebGLImageConversion::kDataFormatRG16F,
WebGLImageConversion::kAlphaDoNothing,
float,
@@ -1760,7 +2684,6 @@ void Pack<WebGLImageConversion::kDataFormatRG16F,
}
}
-// FIXME: this routine is lossy and must be removed.
template <>
void Pack<WebGLImageConversion::kDataFormatRG16F,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -1808,7 +2731,6 @@ void Pack<WebGLImageConversion::kDataFormatRG32F,
}
}
-// FIXME: this routine is lossy and must be removed.
template <>
void Pack<WebGLImageConversion::kDataFormatRG32F,
WebGLImageConversion::kAlphaDoUnmultiply,
@@ -2360,6 +3282,8 @@ void FormatConverter::Convert(WebGLImageConversion::DataFormat src_format,
FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA32F)
FORMATCONVERTER_CASE_SRCFORMAT(
WebGLImageConversion::kDataFormatRGBA2_10_10_10)
+ // Only used by ImageBitmap, when colorspace conversion is needed.
+ FORMATCONVERTER_CASE_SRCFORMAT(WebGLImageConversion::kDataFormatRGBA16F)
default:
NOTREACHED();
}
@@ -2462,6 +3386,8 @@ void FormatConverter::Convert() {
NOTREACHED();
return;
}
+ // Note that ImageBitmaps with SrcFormat==kDataFormatRGBA16F return
+ // false for IsFloatFormat since the input data is uint16_t.
if (!IsFloatFormat<DstFormat>::value && IsFloatFormat<SrcFormat>::value) {
NOTREACHED();
return;
@@ -2470,7 +3396,7 @@ void FormatConverter::Convert() {
// Only textures uploaded from DOM elements or ImageData can allow DstFormat
// != SrcFormat.
const bool src_format_comes_from_dom_element_or_image_data =
- WebGLImageConversion::SrcFormatComeFromDOMElementOrImageData(SrcFormat);
+ WebGLImageConversion::SrcFormatComesFromDOMElementOrImageData(SrcFormat);
if (!src_format_comes_from_dom_element_or_image_data &&
SrcFormat != DstFormat) {
NOTREACHED();
diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
index 2c8f9617f3d..6ac0093ee61 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.h
@@ -194,12 +194,14 @@ class PLATFORM_EXPORT WebGLImageConversion final {
unsigned* padding_in_bytes,
unsigned* skip_size_in_bytes);
- // Check if the format is one of the formats from the ImageData or DOM
- // elements. The format from ImageData is always RGBA8. The formats from DOM
- // elements vary with Graphics ports, but can only be RGBA8 or BGRA8.
- static ALWAYS_INLINE bool SrcFormatComeFromDOMElementOrImageData(
+ // Check if the format is one of the formats from ImageData DOM elements, or
+ // ImageBitmap. The format from ImageData is always RGBA8. The formats from
+ // DOM elements vary with Graphics ports, but can only be RGBA8 or BGRA8.
+ // ImageBitmap can use RGBA16F when colorspace conversion is performed.
+ static ALWAYS_INLINE bool SrcFormatComesFromDOMElementOrImageData(
DataFormat src_format) {
- return src_format == kDataFormatBGRA8 || src_format == kDataFormatRGBA8;
+ return src_format == kDataFormatBGRA8 || src_format == kDataFormatRGBA8 ||
+ src_format == kDataFormatRGBA16F;
}
// The input can be either format or internalformat.
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
index e6a4a67905d..a767c2e5b32 100644
--- 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
@@ -21,13 +21,42 @@ uint64_t AlignWebGPUBytesPerRow(uint64_t bytesPerRow) {
<< kDawnBytesPerRowAlignmentBits;
}
+SkColorType DawnColorTypeToSkColorType(WGPUTextureFormat dawn_format) {
+ switch (dawn_format) {
+ case WGPUTextureFormat_RGBA8Unorm:
+ // According to WebGPU spec, format with -srgb suffix will do color
+ // space conversion when reading and writing in shader. In this uploading
+ // path, we should keep the conversion happening in canvas color space and
+ // leave the srgb color space conversion to the GPU.
+ case WGPUTextureFormat_RGBA8UnormSrgb:
+ return SkColorType::kRGBA_8888_SkColorType;
+ case WGPUTextureFormat_BGRA8Unorm:
+ case WGPUTextureFormat_BGRA8UnormSrgb:
+ return SkColorType::kBGRA_8888_SkColorType;
+ case WGPUTextureFormat_RGB10A2Unorm:
+ return SkColorType::kRGBA_1010102_SkColorType;
+ case WGPUTextureFormat_RGBA16Float:
+ return SkColorType::kRGBA_F16_SkColorType;
+ case WGPUTextureFormat_RGBA32Float:
+ return SkColorType::kRGBA_F32_SkColorType;
+ case WGPUTextureFormat_RG8Unorm:
+ return SkColorType::kR8G8_unorm_SkColorType;
+ case WGPUTextureFormat_RG16Float:
+ return SkColorType::kR16G16_float_SkColorType;
+ default:
+ return SkColorType::kUnknown_SkColorType;
+ }
+}
+
} // anonymous namespace
WebGPUImageUploadSizeInfo ComputeImageBitmapWebGPUUploadSizeInfo(
const IntRect& rect,
- const CanvasColorParams& color_params) {
+ const WGPUTextureFormat& destination_format) {
WebGPUImageUploadSizeInfo info;
- uint64_t bytes_per_pixel = color_params.BytesPerPixel();
+
+ uint64_t bytes_per_pixel = DawnTextureFormatBytesPerPixel(destination_format);
+ DCHECK_NE(bytes_per_pixel, 0u);
uint64_t bytes_per_row =
AlignWebGPUBytesPerRow(rect.Width() * bytes_per_pixel);
@@ -42,31 +71,33 @@ WebGPUImageUploadSizeInfo ComputeImageBitmapWebGPUUploadSizeInfo(
return info;
}
-bool CopyBytesFromImageBitmapForWebGPU(scoped_refptr<StaticBitmapImage> image,
- base::span<uint8_t> dst,
- const IntRect& rect,
- const CanvasColorParams& color_params) {
+bool CopyBytesFromImageBitmapForWebGPU(
+ scoped_refptr<StaticBitmapImage> image,
+ base::span<uint8_t> dst,
+ const IntRect& rect,
+ const CanvasColorParams& color_params,
+ const WGPUTextureFormat destination_format) {
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);
+ ComputeImageBitmapWebGPUUploadSizeInfo(rect, destination_format);
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;
+ SkColorType sk_color_type = DawnColorTypeToSkColorType(destination_format);
+ if (sk_color_type == kUnknown_SkColorType) {
+ return false;
+ }
// Read pixel request dst info.
- // TODO(shaobo.yan@intel.com): Use Skia to do transform and color conversion.
+ // Keep premulalpha config and color space from imageBitmap and using dest
+ // texture color type. This can help do conversions in ReadPixels.
SkImageInfo info = SkImageInfo::Make(
- rect.Width(), rect.Height(), color_type, kUnpremul_SkAlphaType,
+ rect.Width(), rect.Height(), sk_color_type,
+ image->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType,
color_params.GetSkColorSpaceForSkSurfaces());
sk_sp<SkImage> sk_image = image->PaintImageForCurrentFrame().GetSkImage();
@@ -84,6 +115,27 @@ bool CopyBytesFromImageBitmapForWebGPU(scoped_refptr<StaticBitmapImage> image,
return true;
}
+uint64_t DawnTextureFormatBytesPerPixel(const WGPUTextureFormat color_type) {
+ switch (color_type) {
+ case WGPUTextureFormat_RG8Unorm:
+ return 2;
+ case WGPUTextureFormat_RGBA8Unorm:
+ case WGPUTextureFormat_RGBA8UnormSrgb:
+ case WGPUTextureFormat_BGRA8Unorm:
+ case WGPUTextureFormat_BGRA8UnormSrgb:
+ case WGPUTextureFormat_RGB10A2Unorm:
+ case WGPUTextureFormat_RG16Float:
+ return 4;
+ case WGPUTextureFormat_RGBA16Float:
+ return 8;
+ case WGPUTextureFormat_RGBA32Float:
+ return 16;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
DawnTextureFromImageBitmap::DawnTextureFromImageBitmap(
scoped_refptr<DawnControlClientHolder> dawn_control_client,
uint64_t device_client_id)
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
index 57cce174508..836ef713646 100644
--- 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
@@ -5,6 +5,8 @@
#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 <dawn/webgpu.h>
+
#include "base/containers/span.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
@@ -23,13 +25,18 @@ class IntRect;
class StaticBitmapImage;
WebGPUImageUploadSizeInfo PLATFORM_EXPORT
-ComputeImageBitmapWebGPUUploadSizeInfo(const IntRect& rect,
- const CanvasColorParams& color_params);
+ComputeImageBitmapWebGPUUploadSizeInfo(
+ const IntRect& rect,
+ const WGPUTextureFormat& destination_format);
bool PLATFORM_EXPORT
CopyBytesFromImageBitmapForWebGPU(scoped_refptr<StaticBitmapImage> image,
base::span<uint8_t> dst,
const IntRect& rect,
- const CanvasColorParams& color_params);
+ const CanvasColorParams& color_params,
+ const WGPUTextureFormat destination_format);
+
+uint64_t PLATFORM_EXPORT
+DawnTextureFormatBytesPerPixel(const WGPUTextureFormat color_type);
class PLATFORM_EXPORT DawnTextureFromImageBitmap
: public RefCounted<DawnTextureFromImageBitmap> {
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
index 411431ff306..c6ab596d479 100644
--- 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
@@ -24,8 +24,6 @@ using testing::Return;
namespace blink {
-static constexpr uint64_t kMaxArrayLength = 40000;
-
namespace {
gpu::SyncToken GenTestSyncToken(GLbyte id) {
gpu::SyncToken token;
@@ -77,58 +75,244 @@ class MockWebGPUInterface : public gpu::webgpu::WebGPUInterfaceStub {
GLuint texture_id,
GLuint texture_generation));
};
+
+// The six reference pixels are: red, green, blue, white, black.
+static const uint8_t rgba8[] = {
+ 0xFF, 0x00, 0x00, 0xFF, // Red
+ 0x00, 0xFF, 0x00, 0xFF, // Green
+ 0x00, 0x00, 0xFF, 0xFF, // Blue
+ 0x00, 0x00, 0x00, 0xFF, // White
+ 0xFF, 0xFF, 0xFF, 0xFF, // Opaque Black
+ 0xFF, 0xFF, 0xFF, 0x00, // Transparent Black
+};
+
+static const uint8_t bgra8[] = {
+ 0x00, 0x00, 0xFF, 0xFF, // Red
+ 0x00, 0xFF, 0x00, 0xFF, // Green
+ 0xFF, 0x00, 0x00, 0xFF, // Blue
+ 0x00, 0x00, 0x00, 0xFF, // White
+ 0xFF, 0xFF, 0xFF, 0xFF, // Opaque Black
+ 0xFF, 0xFF, 0xFF, 0x00, // Transparent Black
+};
+
+static const uint8_t rgb10a2[] = {
+ 0xFF, 0x03, 0x00, 0xC0, // Red
+ 0x00, 0xFC, 0x0F, 0xC0, // Green
+ 0x00, 0x00, 0xF0, 0xFF, // Blue
+ 0x00, 0x00, 0x00, 0xC0, // White
+ 0xFF, 0xFF, 0xFF, 0xFF, // Opaque Black
+ 0xFF, 0xFF, 0xFF, 0x3F, // Transparent Black
+};
+
+static const uint16_t f16[] = {
+ 0x3C00, 0x0000, 0x0000, 0x3C00, // Red
+ 0x0000, 0x3C00, 0x0000, 0x3C00, // Green
+ 0x0000, 0x0000, 0x3C00, 0x3C00, // Blue
+ 0x0000, 0x0000, 0x0000, 0x3C00, // White
+ 0x3C00, 0x3C00, 0x3C00, 0x3C00, // Opaque Black
+ 0x3C00, 0x3C00, 0x3C00, 0x0000, // Transparent Black
+};
+
+static const float f32[] = {
+ 1.0f, 0.0f, 0.0f, 1.0f, // Red
+ 0.0f, 1.0f, 0.0f, 1.0f, // Green
+ 0.0f, 0.0f, 1.0f, 1.0f, // Blue
+ 0.0f, 0.0f, 0.0f, 1.0f, // White
+ 1.0f, 1.0f, 1.0f, 1.0f, // Opaque Black
+ 1.0f, 1.0f, 1.0f, 0.0f, // Transparent Black
+};
+
+static const uint8_t rg8[] = {
+ 0xFF, 0x00, // Red
+ 0x00, 0xFF, // Green
+ 0x00, 0x00, // No Blue
+ 0x00, 0x00, // White
+ 0xFF, 0xFF, // Opaque Black
+ 0xFF, 0xFF, // Transparent Black
+};
+
+static const uint16_t rg16f[] = {
+ 0x3C00, 0x0000, // Red
+ 0x0000, 0x3C00, // Green
+ 0x0000, 0x0000, // No Blue
+ 0x0000, 0x0000, // White
+ 0x3C00, 0x3C00, // Opaque Black
+ 0x3C00, 0x3C00, // Transparent Black
+};
+
+base::span<const uint8_t> GetDstContent(WGPUTextureFormat format) {
+ switch (format) {
+ case WGPUTextureFormat_RG8Unorm:
+ return base::span<const uint8_t>(rg8, sizeof(rg8));
+ case WGPUTextureFormat_RGBA8Unorm:
+ // We need to ensure no color space conversion happens
+ // during imageBitmap uploading.
+ case WGPUTextureFormat_RGBA8UnormSrgb:
+ return base::span<const uint8_t>(rgba8, sizeof(rgba8));
+ case WGPUTextureFormat_BGRA8Unorm:
+ case WGPUTextureFormat_BGRA8UnormSrgb:
+ return base::span<const uint8_t>(bgra8, sizeof(bgra8));
+ case WGPUTextureFormat_RGB10A2Unorm:
+ return base::span<const uint8_t>(rgb10a2, sizeof(rgb10a2));
+ case WGPUTextureFormat_RG16Float:
+ return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(rg16f),
+ sizeof(rg16f));
+ case WGPUTextureFormat_RGBA16Float:
+ return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f16),
+ sizeof(f16));
+ case WGPUTextureFormat_RGBA32Float:
+ return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f32),
+ sizeof(f32));
+ default:
+ NOTREACHED();
+ return {};
+ }
+}
+
+base::span<const uint8_t> GetSrcPixelContent(SkColorType format) {
+ switch (format) {
+ case SkColorType::kRGBA_8888_SkColorType:
+ return base::span<const uint8_t>(rgba8, sizeof(rgba8));
+ case SkColorType::kBGRA_8888_SkColorType:
+ return base::span<const uint8_t>(bgra8, sizeof(bgra8));
+ case SkColorType::kRGBA_F16_SkColorType:
+ return base::span<const uint8_t>(reinterpret_cast<const uint8_t*>(f16),
+ sizeof(f16));
+ default:
+ NOTREACHED();
+ return {};
+ }
+}
+
+CanvasPixelFormat SkColorTypeToCanvasPixelFormat(SkColorType format) {
+ switch (format) {
+ case SkColorType::kRGBA_8888_SkColorType:
+ return CanvasPixelFormat::kRGBA8;
+ case SkColorType::kBGRA_8888_SkColorType:
+ return CanvasPixelFormat::kBGRA8;
+ case SkColorType::kRGBA_F16_SkColorType:
+ return CanvasPixelFormat::kF16;
+ default:
+ NOTREACHED();
+ return CanvasPixelFormat::kRGBA8;
+ }
+}
} // anonymous namespace
class WebGPUImageBitmapHandlerTest : public testing::Test {
protected:
void SetUp() override {}
- void VerifyCopyBytesForWebGPU(uint64_t width,
- uint64_t height,
- SkImageInfo info,
- CanvasColorParams param,
- IntRect copyRect) {
+ void VerifyCopyBytesForCanvasColorParams(uint64_t width,
+ uint64_t height,
+ SkImageInfo info,
+ CanvasColorParams param,
+ IntRect copy_rect,
+ WGPUTextureFormat color_type) {
const uint64_t content_length = width * height * param.BytesPerPixel();
- std::array<uint8_t, kMaxArrayLength> contents = {0};
+ std::vector<uint8_t> contents(content_length, 0);
// Initialize contents.
for (size_t i = 0; i < content_length; ++i) {
contents[i] = i % std::numeric_limits<uint8_t>::max();
}
+ VerifyCopyBytes(width, height, info, param, copy_rect, color_type,
+ base::span<uint8_t>(contents.data(), content_length),
+ base::span<uint8_t>(contents.data(), content_length));
+ }
+
+ void VerifyCopyBytes(uint64_t width,
+ uint64_t height,
+ SkImageInfo info,
+ CanvasColorParams param,
+ IntRect copy_rect,
+ WGPUTextureFormat color_type,
+ base::span<const uint8_t> contents,
+ base::span<const uint8_t> expected_value) {
+ uint64_t bytes_per_pixel = DawnTextureFormatBytesPerPixel(color_type);
+ ASSERT_EQ(contents.size(), width * height * param.BytesPerPixel());
sk_sp<SkData> image_pixels =
- SkData::MakeWithCopy(contents.data(), content_length);
+ SkData::MakeWithCopy(contents.data(), contents.size());
scoped_refptr<StaticBitmapImage> image =
StaticBitmapImage::Create(std::move(image_pixels), info);
WebGPUImageUploadSizeInfo wgpu_info =
- ComputeImageBitmapWebGPUUploadSizeInfo(copyRect, param);
+ ComputeImageBitmapWebGPUUploadSizeInfo(copy_rect, color_type);
const uint64_t result_length = wgpu_info.size_in_bytes;
- std::array<uint8_t, kMaxArrayLength> results = {0};
+ std::vector<uint8_t> results(result_length, 0);
bool success = CopyBytesFromImageBitmapForWebGPU(
- image, base::span<uint8_t>(results.data(), result_length), copyRect,
- param);
+ image, base::span<uint8_t>(results.data(), result_length), copy_rect,
+ param, color_type);
ASSERT_EQ(success, true);
// Compare content and results
uint32_t bytes_per_row = wgpu_info.wgpu_bytes_per_row;
uint32_t content_row_index =
- (copyRect.Y() * width + copyRect.X()) * param.BytesPerPixel();
+ (copy_rect.Y() * width + copy_rect.X()) * bytes_per_pixel;
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();
+ for (int i = 0; i < copy_rect.Height(); ++i) {
+ EXPECT_EQ(0, memcmp(&expected_value[content_row_index],
+ &results[result_row_index],
+ copy_rect.Width() * bytes_per_pixel));
+ content_row_index += width * bytes_per_pixel;
result_row_index += bytes_per_row;
}
}
};
+TEST_F(WebGPUImageBitmapHandlerTest, VerifyColorConvert) {
+ // All supported CanvasPixelFormat mapping to SkColorType
+ const SkColorType srcSkColorFormat[] = {
+ SkColorType::kRGBA_8888_SkColorType,
+ SkColorType::kBGRA_8888_SkColorType,
+ SkColorType::kRGBA_F16_SkColorType,
+ };
+
+ // Joint of SkColorType and WebGPU texture format
+ const WGPUTextureFormat kDstWebGPUTextureFormat[] = {
+ WGPUTextureFormat_RG16Float, WGPUTextureFormat_RGBA16Float,
+ WGPUTextureFormat_RGBA32Float,
+
+ WGPUTextureFormat_RGB10A2Unorm, WGPUTextureFormat_RG8Unorm,
+ WGPUTextureFormat_RGBA8Unorm, WGPUTextureFormat_BGRA8Unorm,
+ WGPUTextureFormat_RGBA8UnormSrgb, WGPUTextureFormat_BGRA8UnormSrgb,
+ };
+
+ const CanvasColorSpace kColorSpaces[] = {
+ CanvasColorSpace::kSRGB,
+ CanvasColorSpace::kRec2020,
+ CanvasColorSpace::kP3,
+ };
+
+ uint64_t kImageWidth = 3;
+ uint64_t kImageHeight = 2;
+
+ IntRect image_data_rect(0, 0, kImageWidth, kImageHeight);
+
+ for (SkColorType src_color_type : srcSkColorFormat) {
+ for (WGPUTextureFormat dst_color_type : kDstWebGPUTextureFormat) {
+ for (CanvasColorSpace color_space : kColorSpaces) {
+ CanvasColorParams color_param(
+ color_space, SkColorTypeToCanvasPixelFormat(src_color_type),
+ OpacityMode::kNonOpaque);
+ SkImageInfo info =
+ SkImageInfo::Make(kImageWidth, kImageHeight, src_color_type,
+ SkAlphaType::kUnpremul_SkAlphaType,
+ color_param.GetSkColorSpaceForSkSurfaces());
+ VerifyCopyBytes(kImageWidth, kImageHeight, info, color_param,
+ image_data_rect, dst_color_type,
+ GetSrcPixelContent(src_color_type),
+ GetDstContent(dst_color_type));
+ }
+ }
+ }
+}
+
// Test calculate size
TEST_F(WebGPUImageBitmapHandlerTest, VerifyGetWGPUResourceInfo) {
- uint64_t imageWidth = 63;
- uint64_t imageHeight = 1;
+ uint64_t kImageWidth = 63;
+ uint64_t kImageHeight = 1;
CanvasColorParams param(CanvasColorSpace::kSRGB, CanvasPixelFormat::kRGBA8,
OpacityMode::kNonOpaque);
@@ -136,43 +320,62 @@ TEST_F(WebGPUImageBitmapHandlerTest, VerifyGetWGPUResourceInfo) {
uint32_t expected_bytes_per_row = 256;
uint64_t expected_size = 256;
- IntRect test_rect(0, 0, imageWidth, imageHeight);
- WebGPUImageUploadSizeInfo info =
- ComputeImageBitmapWebGPUUploadSizeInfo(test_rect, param);
+ IntRect test_rect(0, 0, kImageWidth, kImageHeight);
+ WebGPUImageUploadSizeInfo info = ComputeImageBitmapWebGPUUploadSizeInfo(
+ test_rect, WGPUTextureFormat_RGBA8Unorm);
ASSERT_EQ(expected_size, info.size_in_bytes);
ASSERT_EQ(expected_bytes_per_row, info.wgpu_bytes_per_row);
}
// Copy full image bitmap test
TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromImageBitmapForWebGPU) {
- uint64_t imageWidth = 4;
- uint64_t imageHeight = 2;
+ uint64_t kImageWidth = 4;
+ uint64_t kImageHeight = 2;
SkImageInfo info = SkImageInfo::Make(
- imageWidth, imageHeight, SkColorType::kRGBA_8888_SkColorType,
+ kImageWidth, kImageHeight, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
- IntRect image_data_rect(0, 0, imageWidth, imageHeight);
+ IntRect image_data_rect(0, 0, kImageWidth, kImageHeight);
CanvasColorParams color_params(CanvasColorSpace::kSRGB,
CanvasPixelFormat::kRGBA8,
OpacityMode::kNonOpaque);
- VerifyCopyBytesForWebGPU(imageWidth, imageHeight, info, color_params,
- image_data_rect);
+ VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
+ color_params, image_data_rect,
+ WGPUTextureFormat_RGBA8Unorm);
}
// Copy sub image bitmap test
TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromSubImageBitmap) {
- uint64_t imageWidth = 63;
- uint64_t imageHeight = 4;
+ uint64_t kImageWidth = 63;
+ uint64_t kImageHeight = 4;
SkImageInfo info = SkImageInfo::Make(
- imageWidth, imageHeight, SkColorType::kRGBA_8888_SkColorType,
+ kImageWidth, kImageHeight, 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);
+ VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
+ color_params, image_data_rect,
+ WGPUTextureFormat_RGBA8Unorm);
+}
+
+// Copy image bitmap with premultiply alpha
+TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesWithPremultiplyAlpha) {
+ uint64_t kImageWidth = 2;
+ uint64_t kImageHeight = 1;
+ SkImageInfo info = SkImageInfo::Make(
+ kImageWidth, kImageHeight, SkColorType::kRGBA_8888_SkColorType,
+ SkAlphaType::kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
+
+ IntRect image_data_rect(0, 0, 2, 1);
+ CanvasColorParams color_params(
+ CanvasColorSpace::kSRGB, CanvasPixelFormat::kRGBA8, OpacityMode::kOpaque);
+
+ VerifyCopyBytesForCanvasColorParams(kImageWidth, kImageHeight, info,
+ color_params, image_data_rect,
+ WGPUTextureFormat_RGBA8Unorm);
}
class DawnTextureFromImageBitmapTest : public testing::Test {
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 3480e5c9fc3..20dd28f6009 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
@@ -249,7 +249,7 @@ base::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
return base::TimeTicks::Now() - start;
}
-void XRFrameTransport::Trace(Visitor* visitor) {
+void XRFrameTransport::Trace(Visitor* visitor) const {
visitor->Trace(submit_frame_client_receiver_);
}
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 2ca8d645834..ea42ba83b40 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
@@ -62,7 +62,7 @@ class PLATFORM_EXPORT XRFrameTransport final
gpu::gles2::GLES2Interface*,
int16_t vr_frame_id);
- virtual void Trace(Visitor*);
+ virtual void Trace(Visitor*) const;
private:
void WaitForPreviousTransfer();
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 072a19c233b..589bb5c3838 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
@@ -43,13 +43,9 @@ class PLATFORM_EXPORT GradientGeneratedImage final : public GeneratedImage {
~GradientGeneratedImage() override = default;
- bool ApplyShader(PaintFlags&, const SkMatrix&) override;
+ bool IsGradientGeneratedImage() const override { return true; }
- DarkModeClassification CheckTypeSpecificConditionsForDarkMode(
- const FloatRect& dest_rect,
- DarkModeImageClassifier* classifier) override {
- return DarkModeClassification::kApplyFilter;
- }
+ bool ApplyShader(PaintFlags&, const SkMatrix&) override;
protected:
void Draw(cc::PaintCanvas*,
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 75ad3a100d4..04558034211 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -62,6 +62,25 @@
namespace blink {
+namespace {
+DarkModeFilter::ElementRole GetElementRoleForImage(Image* image) {
+ DCHECK(image);
+
+ if (image->IsBitmapImage())
+ return DarkModeFilter::ElementRole::kBitmapImage;
+
+ if (image->IsSVGImage() || image->IsSVGImageForContainer())
+ return DarkModeFilter::ElementRole::kSVGImage;
+
+ if (image->IsGradientGeneratedImage())
+ return DarkModeFilter::ElementRole::kGradientGeneratedImage;
+
+ // TODO(prashant.n): Check if remaining image types need to be treated
+ // separately.
+ return DarkModeFilter::ElementRole::kUnhandledImage;
+}
+} // namespace
+
// Helper class that copies |flags| only when dark mode is enabled.
//
// TODO(gilmanmh): Investigate removing const from |flags| in the calling
@@ -355,9 +374,8 @@ void GraphicsContext::DrawFocusRingPath(const SkPath& path,
float border_radius) {
DrawPlatformFocusRing(
path, canvas_,
- dark_mode_filter_
- .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground)
- .Rgb(),
+ dark_mode_filter_.InvertColorIfNeeded(
+ color.Rgb(), DarkModeFilter::ElementRole::kBackground),
width, border_radius);
}
@@ -367,9 +385,8 @@ void GraphicsContext::DrawFocusRingRect(const SkRect& rect,
float border_radius) {
DrawPlatformFocusRing(
rect, canvas_,
- dark_mode_filter_
- .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground)
- .Rgb(),
+ dark_mode_filter_.InvertColorIfNeeded(
+ color.Rgb(), DarkModeFilter::ElementRole::kBackground),
width, border_radius);
}
@@ -422,7 +439,16 @@ void GraphicsContext::DrawFocusRing(const Vector<IntRect>& rects,
int offset,
float border_radius,
float min_border_width,
- const Color& color) {
+ const Color& color,
+ WebColorScheme color_scheme) {
+#if defined(OS_MACOSX)
+ const Color& inner_color = color_scheme == WebColorScheme::kDark
+ ? SkColorSetRGB(0x99, 0xC8, 0xFF)
+ : color;
+#else
+ const Color& inner_color =
+ color_scheme == WebColorScheme::kDark ? SK_ColorWHITE : color;
+#endif
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;
@@ -433,15 +459,18 @@ void GraphicsContext::DrawFocusRing(const Vector<IntRect>& rects,
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
+ const Color& outer_color = color_scheme == WebColorScheme::kDark
+ ? SkColorSetRGB(0x10, 0x10, 0x10)
+ : SK_ColorWHITE;
+ // The outer 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);
+ border_radius, outer_color);
DrawFocusRingInternal(rects, first_border_width, offset, border_radius,
- color);
+ inner_color);
} else {
- DrawFocusRingInternal(rects, width, offset, border_radius, color);
+ DrawFocusRingInternal(rects, width, offset, border_radius, inner_color);
}
}
@@ -468,14 +497,14 @@ void GraphicsContext::DrawInnerShadow(const FloatRoundedRect& rect,
float shadow_blur,
float shadow_spread,
Edges clipped_edges) {
- Color shadow_color = dark_mode_filter_.InvertColorIfNeeded(
- orig_shadow_color, DarkModeFilter::ElementRole::kBackground);
+ SkColor shadow_color = dark_mode_filter_.InvertColorIfNeeded(
+ orig_shadow_color.Rgb(), DarkModeFilter::ElementRole::kBackground);
FloatRect hole_rect(rect.Rect());
hole_rect.Inflate(-shadow_spread);
if (hole_rect.IsEmpty()) {
- FillRoundedRect(rect, shadow_color);
+ FillRoundedRect(rect, Color(shadow_color));
return;
}
@@ -496,8 +525,8 @@ void GraphicsContext::DrawInnerShadow(const FloatRoundedRect& rect,
hole_rect.SetHeight(hole_rect.Height() -
std::min(shadow_offset.Height(), 0.0f) + shadow_blur);
- Color fill_color(shadow_color.Red(), shadow_color.Green(),
- shadow_color.Blue(), 255);
+ Color fill_color(SkColorGetR(shadow_color), SkColorGetG(shadow_color),
+ SkColorGetB(shadow_color), 255);
FloatRect outer_rect = AreaCastingShadowInHole(rect.Rect(), shadow_blur,
shadow_spread, shadow_offset);
@@ -858,8 +887,11 @@ void GraphicsContext::DrawImage(
image_flags.setFilterQuality(ComputeFilterQuality(image, dest, src));
// Do not classify the image if the element has any CSS filters.
- if (!has_filter_property)
- dark_mode_filter_.ApplyToImageFlagsIfNeeded(src, dest, image, &image_flags);
+ if (!has_filter_property && dark_mode_filter_.IsDarkModeActive()) {
+ dark_mode_filter_.ApplyToImageFlagsIfNeeded(
+ src, dest, image->PaintImageForCurrentFrame(), &image_flags,
+ GetElementRoleForImage(image));
+ }
image->Draw(canvas_, image_flags, dest, src, should_respect_image_orientation,
Image::kClampImageToSourceRect, decode_mode);
@@ -896,8 +928,11 @@ void GraphicsContext::DrawImageRRect(
image_flags.setFilterQuality(
ComputeFilterQuality(image, dest.Rect(), src_rect));
- dark_mode_filter_.ApplyToImageFlagsIfNeeded(src_rect, dest.Rect(), image,
- &image_flags);
+ if (dark_mode_filter_.IsDarkModeActive()) {
+ dark_mode_filter_.ApplyToImageFlagsIfNeeded(
+ src_rect, dest.Rect(), image->PaintImageForCurrentFrame(), &image_flags,
+ GetElementRoleForImage(image));
+ }
bool use_shader = (visible_src == src_rect) &&
(respect_orientation == kDoNotRespectImageOrientation ||
@@ -1102,10 +1137,8 @@ void GraphicsContext::FillDRRect(const FloatRoundedRect& outer,
canvas_->drawDRRect(outer, inner, ImmutableState()->FillFlags());
} else {
PaintFlags flags(ImmutableState()->FillFlags());
- flags.setColor(dark_mode_filter_
- .InvertColorIfNeeded(
- color, DarkModeFilter::ElementRole::kBackground)
- .Rgb());
+ flags.setColor(dark_mode_filter_.InvertColorIfNeeded(
+ color.Rgb(), DarkModeFilter::ElementRole::kBackground));
canvas_->drawDRRect(outer, inner, flags);
}
@@ -1118,10 +1151,8 @@ void GraphicsContext::FillDRRect(const FloatRoundedRect& outer,
stroke_r_rect.inset(stroke_width / 2, stroke_width / 2);
PaintFlags stroke_flags(ImmutableState()->FillFlags());
- stroke_flags.setColor(
- dark_mode_filter_
- .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground)
- .Rgb());
+ stroke_flags.setColor(dark_mode_filter_.InvertColorIfNeeded(
+ color.Rgb(), DarkModeFilter::ElementRole::kBackground));
stroke_flags.setStyle(PaintFlags::kStroke_Style);
stroke_flags.setStrokeWidth(stroke_width);
@@ -1284,10 +1315,8 @@ void GraphicsContext::FillRectWithRoundedHole(
const FloatRoundedRect& rounded_hole_rect,
const Color& color) {
PaintFlags flags(ImmutableState()->FillFlags());
- flags.setColor(
- dark_mode_filter_
- .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground)
- .Rgb());
+ flags.setColor(dark_mode_filter_.InvertColorIfNeeded(
+ color.Rgb(), DarkModeFilter::ElementRole::kBackground));
canvas_->drawDRRect(SkRRect::MakeRect(rect), rounded_hole_rect, flags);
}
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 af2ca36c980..2f25292b419 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -31,6 +31,7 @@
#include <memory>
#include "base/macros.h"
+#include "third_party/blink/public/platform/web_color_scheme.h"
#include "third_party/blink/renderer/platform/fonts/font.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h"
#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
@@ -364,7 +365,8 @@ class PLATFORM_EXPORT GraphicsContext {
int offset,
float border_radius,
float min_border_width,
- const Color&);
+ const Color&,
+ WebColorScheme color_scheme);
void DrawFocusRing(const Path&, float width, int offset, const Color&);
enum Edge {
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 3be2155abd6..4981a1c0c4d 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -72,10 +72,8 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client)
contents_visible_(true),
hit_testable_(false),
needs_check_raster_invalidation_(false),
- painted_(false),
painting_phase_(kGraphicsLayerPaintAllWithOverflowClip),
parent_(nullptr),
- mask_layer_(nullptr),
raster_invalidation_function_(
base::BindRepeating(&GraphicsLayer::SetNeedsDisplayInRect,
base::Unretained(this))) {
@@ -295,9 +293,6 @@ void GraphicsLayer::PaintRecursivelyInternal(
repainted_layers.push_back(this);
}
- if (MaskLayer())
- MaskLayer()->PaintRecursivelyInternal(repainted_layers);
-
for (auto* child : Children())
child->PaintRecursivelyInternal(repainted_layers);
}
@@ -567,11 +562,8 @@ void GraphicsLayer::SetContentsOpaque(bool opaque) {
contents_layer_->SetContentsOpaque(opaque);
}
-void GraphicsLayer::SetMaskLayer(GraphicsLayer* mask_layer) {
- if (mask_layer == mask_layer_)
- return;
-
- mask_layer_ = mask_layer;
+void GraphicsLayer::SetContentsOpaqueForText(bool opaque) {
+ CcLayer()->SetContentsOpaqueForText(opaque);
}
void GraphicsLayer::SetHitTestable(bool should_hit_test) {
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 47601313d87..3c8cbd1e42b 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -112,9 +112,6 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient,
void RemoveAllChildren();
void RemoveFromParent();
- GraphicsLayer* MaskLayer() const { return mask_layer_; }
- void SetMaskLayer(GraphicsLayer*);
-
// The offset is the origin of the layoutObject minus the origin of the
// graphics layer (so either zero or negative).
IntSize OffsetFromLayoutObject() const { return offset_from_layout_object_; }
@@ -124,8 +121,6 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient,
const gfx::Size& Size() const;
void SetSize(const gfx::Size&);
- void SetRenderingContext(int id);
-
bool DrawsContent() const { return draws_content_; }
void SetDrawsContent(bool);
@@ -151,12 +146,11 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient,
// Opaque means that we know the layer contents have no alpha.
bool ContentsOpaque() const;
void SetContentsOpaque(bool);
+ void SetContentsOpaqueForText(bool);
void SetHitTestable(bool);
bool GetHitTestable() const { return hit_testable_; }
- void SetFilterQuality(SkFilterQuality);
-
// Some GraphicsLayers paint only the foreground or the background content
GraphicsLayerPaintingPhase PaintingPhase() const { return painting_phase_; }
void SetPaintingPhase(GraphicsLayerPaintingPhase);
@@ -292,20 +286,14 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient,
bool hit_testable_ : 1;
bool needs_check_raster_invalidation_ : 1;
- bool painted_ : 1;
-
GraphicsLayerPaintingPhase painting_phase_;
Vector<GraphicsLayer*> children_;
GraphicsLayer* parent_;
- // Reference to mask layer. We don't own this.
- GraphicsLayer* mask_layer_;
-
IntRect contents_rect_;
scoped_refptr<cc::PictureLayer> layer_;
- IntSize image_size_;
scoped_refptr<cc::Layer> contents_layer_;
SquashingDisallowedReasons squashing_disallowed_reasons_ =
@@ -328,8 +316,6 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient,
DOMNodeId owner_node_id_ = kInvalidDOMNodeId;
CompositingReasons compositing_reasons_ = CompositingReason::kNone;
- FRIEND_TEST_ALL_PREFIXES(CompositingLayerPropertyUpdaterTest, MaskLayerState);
-
DISALLOW_COPY_AND_ASSIGN(GraphicsLayer);
};
diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h
index 237d6c5ac94..03d1fb9807e 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -116,12 +116,16 @@ enum OpacityMode {
kOpaque,
};
-enum AccelerationHint {
- kPreferAcceleration,
- // The PreferAccelerationAfterVisibilityChange hint suggests we should switch
- // back to acceleration in the context of the canvas becoming visible again.
- kPreferAccelerationAfterVisibilityChange,
- kPreferNoAcceleration,
+// 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,
+};
+
+enum class RasterModeHint {
+ kPreferGPU,
+ kPreferCPU,
};
enum MailboxSyncMode {
diff --git a/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc b/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc
new file mode 100644
index 00000000000..cfa64d421ba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.cc
@@ -0,0 +1,112 @@
+// 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/identifiability_paint_op_digest.h"
+
+#include "gpu/command_buffer/client/raster_interface.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_metrics.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_participation.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+
+namespace blink {
+
+// Storage for serialized PaintOp state.
+Vector<char>& SerializationBuffer() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Vector<char>>,
+ serialization_buffer, ());
+ return *serialization_buffer;
+}
+
+IdentifiabilityPaintOpDigest::IdentifiabilityPaintOpDigest(IntSize size)
+ : size_(size),
+ paint_cache_(cc::ClientPaintCache::kNoCachingBudget),
+ nodraw_canvas_(size_.Width(), size_.Height()),
+ serialize_options_(&image_provider_,
+ /*transfer_cache=*/nullptr,
+ &paint_cache_,
+ &nodraw_canvas_,
+ /*strike_server=*/nullptr,
+ /*color_space=*/nullptr,
+ /*can_use_lcd_text=*/false,
+ /*content_supports_distance_field_text=*/false,
+ /*max_texture_size=*/0,
+ /*original_ctm=*/SkMatrix::I()) {
+ constexpr size_t kInitialSize = 16 * 1024;
+ if (IsUserInIdentifiabilityStudy() &&
+ SerializationBuffer().size() < kInitialSize)
+ SerializationBuffer().resize(kInitialSize);
+}
+
+IdentifiabilityPaintOpDigest::~IdentifiabilityPaintOpDigest() = default;
+
+constexpr size_t IdentifiabilityPaintOpDigest::kInfiniteOps;
+
+void IdentifiabilityPaintOpDigest::MaybeUpdateDigest(
+ const sk_sp<const cc::PaintRecord>& paint_record,
+ const size_t num_ops_to_visit) {
+ // To minimize performance impact, don't exceed kMaxDigestOps during the
+ // lifetime of this IdentifiabilityPaintOpDigest object.
+ constexpr int kMaxDigestOps = 1 << 20;
+ if (!IsUserInIdentifiabilityStudy() || total_ops_digested_ > kMaxDigestOps)
+ return;
+
+ // Determine how many PaintOps we'll need to digest after the initial digests
+ // that are skipped.
+ const size_t num_ops_to_digest = num_ops_to_visit - prefix_skip_count_;
+
+ // The number of PaintOps digested in this MaybeUpdateDigest() call.
+ size_t cur_ops_digested = 0;
+ for (const auto* op : cc::PaintRecord::Iterator(paint_record.get())) {
+ // Skip initial PaintOps that don't correspond to context operations.
+ if (prefix_skip_count_ > 0) {
+ prefix_skip_count_--;
+ continue;
+ }
+ // Update the digest for at most |num_ops_to_digest| operations in this
+ // MaybeUpdateDigest() invocation.
+ if (num_ops_to_visit != kInfiniteOps &&
+ cur_ops_digested >= num_ops_to_digest)
+ break;
+
+ // To capture font fallback identifiability, we capture text draw operations
+ // at the 2D context layer.
+ if (op->GetType() == cc::PaintOpType::DrawTextBlob)
+ continue;
+
+ // DrawRecord PaintOps contain nested PaintOps.
+ if (op->GetType() == cc::PaintOpType::DrawRecord) {
+ const auto* draw_record_op = static_cast<const cc::DrawRecordOp*>(op);
+ MaybeUpdateDigest(draw_record_op->record, kInfiniteOps);
+ continue;
+ }
+
+ size_t serialized_size;
+ while ((serialized_size = op->Serialize(SerializationBuffer().data(),
+ SerializationBuffer().size(),
+ serialize_options_)) == 0) {
+ constexpr size_t kMaxBufferSize =
+ gpu::raster::RasterInterface::kDefaultMaxOpSizeHint << 2;
+ if (SerializationBuffer().size() >= kMaxBufferSize)
+ return;
+ SerializationBuffer().Grow(SerializationBuffer().size() << 1);
+ }
+ digest_ ^= IdentifiabilityDigestOfBytes(base::as_bytes(
+ base::make_span(SerializationBuffer().data(), serialized_size)));
+ total_ops_digested_++;
+ cur_ops_digested++;
+ }
+ DCHECK_EQ(prefix_skip_count_, 0u);
+}
+
+cc::ImageProvider::ScopedResult
+IdentifiabilityPaintOpDigest::IdentifiabilityImageProvider::GetRasterContent(
+ const cc::DrawImage& draw_image) {
+ // TODO(crbug.com/973801): Compute digests on images.
+ return ScopedResult();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h b/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h
new file mode 100644
index 00000000000..4b0cdbcf9ac
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h
@@ -0,0 +1,90 @@
+// 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_IDENTIFIABILITY_PAINT_OP_DIGEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IDENTIFIABILITY_PAINT_OP_DIGEST_H_
+
+#include <memory>
+
+#include "cc/paint/draw_image.h"
+#include "cc/paint/image_provider.h"
+#include "cc/paint/paint_cache.h"
+#include "third_party/blink/renderer/platform/geometry/int_size.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+
+namespace blink {
+
+// Manages digest calculation of operations performed on an HTML canvas using
+// the serialization of PaintOps generated by those operations.
+//
+// TODO(crbug.com/973801): Report results poisoning when dropping PaintOps.
+class IdentifiabilityPaintOpDigest {
+ public:
+ // Constructs based on the size of the CanvasResourceProvider.
+ explicit IdentifiabilityPaintOpDigest(IntSize size);
+ ~IdentifiabilityPaintOpDigest();
+
+ // When passed as |num_ops_to_visit| to MaybeUpdateDigest(), every
+ // non-skipped PaintOp in the buffer contributes to digest calculation.
+ static constexpr size_t kInfiniteOps = -1;
+
+ // Maybe update the digest, if the user is participating in the study, and we
+ // haven't exceeded the operation count.
+ //
+ // Only processes |num_ops_to_visit| PaintOps, which includes the first
+ // |prefix_skip_count| PaintOps that are skipped. The prefix and suffixes that
+ // aren't processed are internal rendering details, and don't correspond to
+ // operations performed on the canvas context.
+ void MaybeUpdateDigest(const sk_sp<const cc::PaintRecord>& paint_record,
+ size_t num_ops_to_visit);
+
+ // Sets the number of operations to skip in the next PaintRecord passed to
+ // MaybeUpdateDigest().
+ void SetPrefixSkipCount(size_t prefix_skip_count) {
+ prefix_skip_count_ = prefix_skip_count;
+ }
+
+ // The digest that was calculated, based on the PaintOps observed.
+ uint64_t digest() const { return digest_; }
+
+ private:
+ class IdentifiabilityImageProvider : public cc::ImageProvider {
+ public:
+ ScopedResult GetRasterContent(const cc::DrawImage& draw_image) override;
+ };
+
+ // The current identifiability digest -- potentially updated every
+ // MaybeUpdateDigest() call.
+ uint64_t digest_ = 0;
+
+ // The number of PaintOps that have contributed to the current digest -- used
+ // to stop updating the digest after a threshold number of operations to avoid
+ // hurting performance.
+ int total_ops_digested_ = 0;
+
+ // How many PaintOps to skip, as set by SetPrefixSkipCount().
+ size_t prefix_skip_count_ = 0;
+
+ // Resources needed for PaintOp serialization.
+
+ // Size of the corresponding CanvasResourceProvider.
+ IntSize size_;
+
+ // Fake identifiability image provider; can be used to compute image digests.
+ IdentifiabilityImageProvider image_provider_{};
+
+ // Real paint cache with Put() disabled.
+ cc::ClientPaintCache paint_cache_;
+
+ // Fake canvas needed for null checks.
+ SkNoDrawCanvas nodraw_canvas_;
+
+ // Used for PaintOp::Serialize() -- several options are not needed, since we
+ // just need to compute a digest.
+ cc::PaintOp::SerializeOptions serialize_options_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IDENTIFIABILITY_PAINT_OP_DIGEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.cc b/chromium/third_party/blink/renderer/platform/graphics/image.cc
index 444fec9627d..b50cc2347d3 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/image.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/image.cc
@@ -40,6 +40,7 @@
#include "third_party/blink/renderer/platform/geometry/float_size.h"
#include "third_party/blink/renderer/platform/geometry/length.h"
#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
#include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
@@ -64,7 +65,12 @@ Image::Image(ImageObserver* observer, bool is_multipart)
stable_image_id_(PaintImage::GetNextId()),
is_multipart_(is_multipart) {}
-Image::~Image() = default;
+Image::~Image() {
+ // TODO(prashant.n): This logic is needed to purge cache for the same origin
+ // page navigations. Redesign this once dark mode filter module gets moved to
+ // compositor side.
+ DarkModeImageClassifier::RemoveCache(stable_image_id_);
+}
Image* Image::NullImage() {
DCHECK(IsMainThread());
@@ -356,29 +362,6 @@ SkBitmap Image::AsSkBitmapForCurrentFrame(
return bitmap;
}
-bool Image::GetBitmap(const FloatRect& src_rect, SkBitmap* bitmap) {
- if (!src_rect.Width() || !src_rect.Height())
- return false;
-
- SkScalar sx = SkFloatToScalar(src_rect.X());
- SkScalar sy = SkFloatToScalar(src_rect.Y());
- SkScalar sw = SkFloatToScalar(src_rect.Width());
- SkScalar sh = SkFloatToScalar(src_rect.Height());
- SkRect src = {sx, sy, sx + sw, sy + sh};
- SkRect dest = {0, 0, sw, sh};
-
- if (!bitmap || !bitmap->tryAllocPixels(SkImageInfo::MakeN32(
- static_cast<int>(src_rect.Width()),
- static_cast<int>(src_rect.Height()), kPremul_SkAlphaType)))
- return false;
-
- SkCanvas canvas(*bitmap);
- canvas.clear(SK_ColorTRANSPARENT);
- canvas.drawImageRect(PaintImageForCurrentFrame().GetSkImage(), src, dest,
- nullptr);
- return true;
-}
-
FloatRect Image::CorrectSrcRectForImageOrientation(FloatSize image_size,
FloatRect src_rect) const {
ImageOrientation orientation = CurrentFrameOrientation();
@@ -388,27 +371,4 @@ FloatRect Image::CorrectSrcRectForImageOrientation(FloatSize image_size,
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
- // size, only the top left corner coordinates of the src_rect are used to
- // generate the key for caching and retrieving the classification.
- ClassificationKey key(src_rect.X(), src_rect.Y());
- auto result = dark_mode_classifications_.find(key);
- if (result == dark_mode_classifications_.end())
- return DarkModeClassification::kNotClassified;
-
- return result->value;
-}
-
-void Image::AddDarkModeClassification(
- const FloatRect& src_rect,
- DarkModeClassification dark_mode_classification) {
- // Add the classification in the map only if the image is not classified yet.
- DCHECK(GetDarkModeClassification(src_rect) ==
- DarkModeClassification::kNotClassified);
- ClassificationKey key(src_rect.X(), src_rect.Y());
- dark_mode_classifications_.insert(key, dark_mode_classification);
-}
-
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.h b/chromium/third_party/blink/renderer/platform/graphics/image.h
index 40cce084170..bb5e190087f 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/image.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/image.h
@@ -57,7 +57,6 @@ class ImageDecodeCache;
namespace blink {
-class DarkModeImageClassifier;
class FloatRect;
class GraphicsContext;
class Image;
@@ -88,9 +87,11 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> {
InterpolationQuality = kInterpolationNone);
virtual bool IsSVGImage() const { return false; }
+ virtual bool IsSVGImageForContainer() const { return false; }
virtual bool IsBitmapImage() const { return false; }
virtual bool IsStaticBitmapImage() const { return false; }
virtual bool IsPlaceholderImage() const { return false; }
+ virtual bool IsGradientGeneratedImage() const { return false; }
virtual bool CurrentFrameKnownToBeOpaque() = 0;
@@ -259,37 +260,11 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> {
return nullptr;
}
- // This function is implemented by the derived classes which might
- // have certain conditions or default classification decisions which
- // need to be checked before the classification algorithms are applied
- // on the image.
- virtual DarkModeClassification CheckTypeSpecificConditionsForDarkMode(
- const FloatRect& dest_rect,
- DarkModeImageClassifier* classifier) {
- return DarkModeClassification::kDoNotApplyFilter;
- }
-
- // This function returns true if it can create the bitmap of the
- // image using |src_rect| for the location and dimensions of the image.
- // For Bitmap and SVG (and any other type) images the implementation
- // of this function differs when it comes to the implementation of
- // PaintImageForCurrentFrame(). Once the PaintImage is available,
- // the method used to extract the bitmap is the same for any image.
- bool GetBitmap(const FloatRect& src_rect, SkBitmap* bitmap);
-
PaintImage::Id paint_image_id() const { return stable_image_id_; }
// Returns an SkBitmap that is a copy of the image's current frame.
SkBitmap AsSkBitmapForCurrentFrame(RespectImageOrientationEnum);
- DarkModeClassification GetDarkModeClassification(const FloatRect& src_rect);
-
- // Dark mode classification result is cached to be consistent and have
- // higher performance for future paints.
- void AddDarkModeClassification(
- const FloatRect& src_rect,
- const DarkModeClassification dark_mode_classification);
-
protected:
Image(ImageObserver* = nullptr, bool is_multipart = false);
@@ -309,9 +284,6 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> {
// Whether or not size is available yet.
virtual bool IsSizeAvailable() { return true; }
- typedef FloatPoint ClassificationKey;
- HashMap<ClassificationKey, DarkModeClassification> dark_mode_classifications_;
-
private:
bool image_observer_disabled_;
scoped_refptr<SharedBuffer> encoded_image_data_;
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h
index 37f8263c8b7..cc8dc19e87b 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_data_buffer.h
@@ -60,6 +60,7 @@ class PLATFORM_EXPORT ImageDataBuffer {
const IntSize& size() const { return size_; }
int Height() const { return size_.Height(); }
int Width() const { return size_.Width(); }
+ size_t ComputeByteSize() const { return pixmap_.computeByteSize(); }
private:
ImageDataBuffer(const IntSize&,
diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
index 080f1e7a1ef..6a899799638 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/image_decoding_store.cc
@@ -43,6 +43,7 @@ ImageDecodingStore::ImageDecodingStore()
: heap_limit_in_bytes_(kDefaultMaxTotalSizeOfHeapEntries),
heap_memory_usage_in_bytes_(0),
memory_pressure_listener_(
+ FROM_HERE,
base::BindRepeating(&ImageDecodingStore::OnMemoryPressure,
base::Unretained(this))) {}
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 1cf6d753c4d..249d5092a17 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc
index eca493829fd..d999f2b958d 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/interpolation_space.cc
@@ -32,6 +32,7 @@
#include "third_party/blink/renderer/platform/graphics/interpolation_space.h"
+#include "base/notreached.h"
#include "third_party/skia/include/core/SkColorFilter.h"
namespace blink {
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 2559f262412..e677457ae55 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -341,8 +341,6 @@ String StyleName(SkPaint::Style style) {
return "Fill";
case SkPaint::kStroke_Style:
return "Stroke";
- case SkPaint::kStrokeAndFill_Style:
- return "StrokeAndFill";
default:
NOTREACHED();
return "?";
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
index 3659a862b3e..527bbf2aabc 100644
--- 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
@@ -29,13 +29,13 @@ void MemoryManagedPaintCanvas::drawImageRect(
const SkRect& src,
const SkRect& dst,
const cc::PaintFlags* flags,
- PaintCanvas::SrcRectConstraint constraint) {
+ SkCanvas::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)))
+ if (cached_image_ids_.Contains(image.GetContentIdForFrame(0u)))
return;
cached_image_ids_.insert(image.GetContentIdForFrame(0u));
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
index 444d771f899..1a7c0000697 100644
--- 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
@@ -9,6 +9,7 @@
#include "cc/paint/record_paint_canvas.h"
#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
@@ -33,12 +34,13 @@ class PLATFORM_EXPORT MemoryManagedPaintCanvas final
const SkRect& src,
const SkRect& dst,
const cc::PaintFlags* flags,
- SrcRectConstraint constraint) override;
+ SkCanvas::SrcRectConstraint constraint) override;
private:
void UpdateMemoryUsage(const cc::PaintImage& image);
- base::flat_set<int> cached_image_ids_;
+ HashSet<int, DefaultHash<int>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int>>
+ cached_image_ids_;
uint64_t total_stored_image_memory_ = 0;
base::RepeatingClosure set_needs_flush_callback_;
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index acfc8a40fa0..a244e790db7 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -51,6 +51,15 @@ void CullRect::Move(const IntSize& offset) {
rect_.Move(offset);
}
+void CullRect::Move(const FloatSize& offset) {
+ if (IsInfinite())
+ return;
+
+ FloatRect float_rect(rect_);
+ float_rect.Move(offset);
+ rect_ = EnclosingIntRect(float_rect);
+}
+
static void MapRect(const TransformPaintPropertyNode& transform,
IntRect& rect) {
if (transform.IsIdentityOr2DTranslation()) {
@@ -63,15 +72,12 @@ static void MapRect(const TransformPaintPropertyNode& transform,
}
CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
- const TransformPaintPropertyNode& transform,
- bool clip_to_scroll_container) {
+ const TransformPaintPropertyNode& transform) {
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
if (const auto* scroll = transform.ScrollNode()) {
- if (clip_to_scroll_container) {
- rect_.Intersect(scroll->ContainerRect());
- if (rect_.IsEmpty())
- return kNotExpanded;
- }
+ rect_.Intersect(scroll->ContainerRect());
+ if (rect_.IsEmpty())
+ return kNotExpanded;
MapRect(transform, rect_);
@@ -103,8 +109,7 @@ CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
void CullRect::ApplyTransforms(const TransformPaintPropertyNode& source,
const TransformPaintPropertyNode& destination,
- const base::Optional<CullRect>& old_cull_rect,
- bool clip_to_scroll_container) {
+ const base::Optional<CullRect>& old_cull_rect) {
DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
Vector<const TransformPaintPropertyNode*> scroll_translations;
@@ -130,7 +135,7 @@ void CullRect::ApplyTransforms(const TransformPaintPropertyNode& source,
*last_transform, *scroll_translation->Parent(), rect_);
}
last_scroll_translation_result =
- ApplyTransformInternal(*scroll_translation, clip_to_scroll_container);
+ ApplyTransformInternal(*scroll_translation);
last_transform = scroll_translation;
}
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
index 8b4fade8326..234237025e0 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.h
@@ -40,13 +40,13 @@ class PLATFORM_EXPORT CullRect {
void MoveBy(const IntPoint& offset);
void Move(const IntSize& offset);
+ void Move(const FloatSize& offset);
// Applies one transform to the cull rect. Before this function is called,
// the cull rect is in the space of the parent the transform node.
// For CompositeAfterPaint, when the transform is a scroll translation, the
// cull rect is converted in the following steps:
- // 1. it's clipped by the container rect if |clip_to_scroll_container| is
- // true,
+ // 1. it's clipped by the container rect,
// 2. transformed by inverse of the scroll translation,
// 3. expanded by thousands of pixels for composited scrolling.
void ApplyTransform(const TransformPaintPropertyNode& transform) {
@@ -62,8 +62,7 @@ class PLATFORM_EXPORT CullRect {
// will be set to |old_cull_rect| to avoid repaint on each composited scroll.
void ApplyTransforms(const TransformPaintPropertyNode& source,
const TransformPaintPropertyNode& destination,
- const base::Optional<CullRect>& old_cull_rect,
- bool clip_to_scroll_container = true);
+ const base::Optional<CullRect>& old_cull_rect);
const IntRect& Rect() const { return rect_; }
@@ -86,8 +85,7 @@ class PLATFORM_EXPORT CullRect {
kExpandedForPartialScrollingContents,
};
ApplyTransformResult ApplyTransformInternal(
- const TransformPaintPropertyNode&,
- bool clip_to_scroll_container = true);
+ const TransformPaintPropertyNode&);
bool ChangedEnough(const CullRect& old_cull_rect) const;
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 51582a9aeb6..c589ada7464 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
@@ -13,7 +13,8 @@ struct SameSizeAsDisplayItem {
void* pointer;
IntRect rect;
float outset;
- int i;
+ uint32_t i1;
+ uint32_t i2;
};
static_assert(sizeof(DisplayItem) == sizeof(SameSizeAsDisplayItem),
"DisplayItem should stay small");
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 8010d2d84c3..f9c4b364574 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
@@ -166,17 +166,17 @@ class PLATFORM_EXPORT DisplayItem {
: client_(&client),
visual_rect_(client.VisualRect()),
outset_for_raster_effects_(client.VisualRectOutsetForRasterEffects()),
+ fragment_(0),
type_(type),
+ derived_size_(derived_size),
draws_content_(draws_content),
- fragment_(0),
is_cacheable_(client.IsCacheable()),
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 << 7));
+ SECURITY_DCHECK(derived_size == derived_size_);
SECURITY_DCHECK(derived_size >= sizeof(*this));
- derived_size_ = static_cast<unsigned>(derived_size);
}
virtual ~DisplayItem() = default;
@@ -184,16 +184,16 @@ class PLATFORM_EXPORT DisplayItem {
// Ids are for matching new DisplayItems with existing DisplayItems.
struct Id {
DISALLOW_NEW();
- Id(const DisplayItemClient& client, const Type type, unsigned fragment = 0)
+ Id(const DisplayItemClient& client, Type type, wtf_size_t fragment = 0)
: client(client), type(type), fragment(fragment) {}
- Id(const Id& id, unsigned fragment)
+ Id(const Id& id, wtf_size_t fragment)
: client(id.client), type(id.type), fragment(fragment) {}
String ToString() const;
const DisplayItemClient& client;
const Type type;
- const unsigned fragment;
+ const wtf_size_t fragment;
};
Id GetId() const { return Id(*client_, GetType(), fragment_); }
@@ -225,11 +225,8 @@ class PLATFORM_EXPORT DisplayItem {
// The fragment is part of the id, to uniquely identify display items in
// different fragments for the same client and type.
- unsigned Fragment() const { return fragment_; }
- void SetFragment(unsigned fragment) {
- DCHECK(fragment < (1 << 14));
- fragment_ = fragment;
- }
+ wtf_size_t Fragment() const { return fragment_; }
+ void SetFragment(wtf_size_t fragment) { fragment_ = fragment; }
void SetVisualRectForTesting(const IntRect& r) { visual_rect_ = r; }
@@ -297,7 +294,7 @@ class PLATFORM_EXPORT DisplayItem {
#endif
private:
- template <typename T, unsigned alignment>
+ template <typename T, wtf_size_t alignment>
friend class ContiguousContainer;
friend class DisplayItemList;
@@ -312,15 +309,15 @@ class PLATFORM_EXPORT DisplayItem {
const DisplayItemClient* client_;
IntRect visual_rect_;
float outset_for_raster_effects_;
-
- static_assert(kTypeLast < (1 << 7), "DisplayItem::Type should fit in 7 bits");
- unsigned type_ : 7;
- unsigned draws_content_ : 1;
- 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;
+ wtf_size_t fragment_;
+ static_assert(kTypeLast < (1 << 8),
+ "DisplayItem::Type should fit in uint8_t");
+ uint8_t type_;
+ uint8_t derived_size_; // size of the actual derived class
+ bool draws_content_ : 1;
+ bool is_cacheable_ : 1;
+ bool is_tombstone_ : 1;
+ bool 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_client.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h
index 6e108747031..30504157c8c 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
@@ -22,12 +22,14 @@ namespace blink {
class PLATFORM_EXPORT DisplayItemClient {
public:
DisplayItemClient()
- : paint_invalidation_reason_(PaintInvalidationReason::kJustCreated) {
+ : paint_invalidation_reason_(PaintInvalidationReason::kJustCreated),
+ is_in_paint_controller_before_finish_cycle_(false) {
#if DCHECK_IS_ON()
OnCreate();
#endif
}
virtual ~DisplayItemClient() {
+ CHECK(!is_in_paint_controller_before_finish_cycle_);
#if DCHECK_IS_ON()
OnDestroy();
#endif
@@ -105,6 +107,12 @@ class PLATFORM_EXPORT DisplayItemClient {
return paint_invalidation_reason_ == PaintInvalidationReason::kNone;
}
+ // This is used to track early deletion of DisplayItemClient after paint
+ // before PaintController::FinishCycle().
+ void SetIsInPaintControllerBeforeFinishCycle(bool b) const {
+ is_in_paint_controller_before_finish_cycle_ = b;
+ }
+
String ToString() const;
private:
@@ -121,7 +129,8 @@ class PLATFORM_EXPORT DisplayItemClient {
void OnDestroy();
#endif
- mutable PaintInvalidationReason paint_invalidation_reason_;
+ mutable PaintInvalidationReason paint_invalidation_reason_ : 7;
+ mutable bool is_in_paint_controller_before_finish_cycle_ : 1;
DISALLOW_COPY_AND_ASSIGN(DisplayItemClient);
};
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 a6fa2cd2fa4..86f8e25fbb3 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
@@ -50,8 +50,8 @@ class PLATFORM_EXPORT DrawingDisplayItem : public DisplayItem {
private:
bool CalculateKnownToBeOpaque(const PaintRecord*) const;
- sk_sp<const PaintRecord> record_;
mutable base::Optional<bool> known_to_be_opaque_;
+ sk_sp<const PaintRecord> record_;
};
// TODO(dcheng): Move this ctor back inline once the clang plugin is fixed.
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
index 0c82591ea35..8d7bd6107b2 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -18,15 +18,10 @@ const EffectPaintPropertyNode& EffectPaintPropertyNode::Root() {
return *root;
}
-FloatRect EffectPaintPropertyNode::MapRect(const FloatRect& input_rect) const {
+FloatRect EffectPaintPropertyNode::MapRect(const FloatRect& rect) const {
if (state_.filter.IsEmpty())
- return input_rect;
-
- FloatRect rect = input_rect;
- rect.MoveBy(-state_.filters_origin);
- FloatRect result = state_.filter.MapRect(rect);
- result.MoveBy(state_.filters_origin);
- return result;
+ return rect;
+ return state_.filter.MapRect(rect);
}
bool EffectPaintPropertyNode::Changed(
@@ -89,8 +84,6 @@ std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const {
json->SetString("compositorElementId",
state_.compositor_element_id.ToString().c_str());
}
- if (state_.filters_origin != FloatPoint())
- json->SetString("filtersOrigin", state_.filters_origin.ToString());
return json;
}
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 9c039137fd9..d35a984f273 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
@@ -66,8 +66,6 @@ class PLATFORM_EXPORT EffectPaintPropertyNode
bool has_active_opacity_animation = false;
bool has_active_filter_animation = false;
bool has_active_backdrop_filter_animation = false;
- // The offset of the origin of filters in local_transform_space.
- FloatPoint filters_origin;
PaintPropertyChangeType ComputeChange(
const State& other,
@@ -76,8 +74,7 @@ class PLATFORM_EXPORT EffectPaintPropertyNode
output_clip != other.output_clip ||
color_filter != other.color_filter ||
backdrop_filter_bounds != other.backdrop_filter_bounds ||
- blend_mode != other.blend_mode ||
- filters_origin != other.filters_origin) {
+ blend_mode != other.blend_mode) {
return PaintPropertyChangeType::kChangedOnlyValues;
}
bool opacity_changed = opacity != other.opacity;
@@ -202,11 +199,6 @@ class PLATFORM_EXPORT EffectPaintPropertyNode
return state_.filter.HasFilterThatMovesPixels();
}
- FloatPoint FiltersOrigin() const {
- DCHECK(!Parent() || !IsParentAlias());
- return state_.filters_origin;
- }
-
bool HasRealEffects() const {
return Opacity() != 1.0f || GetColorFilter() != kColorFilterNone ||
BlendMode() != SkBlendMode::kSrcOver || !Filter().IsEmpty() ||
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 690644d7dd0..8b76eb1fdd9 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
@@ -54,14 +54,12 @@ ForeignLayerDisplayItem::ForeignLayerDisplayItem(
const DisplayItemClient& client,
Type type,
scoped_refptr<cc::Layer> layer,
- const FloatPoint& offset,
- const LayerAsJSONClient* json_client)
+ const FloatPoint& offset)
: DisplayItem(
*new ForeignLayerDisplayItemClient(client, std::move(layer), offset),
type,
sizeof(*this)),
- offset_(offset),
- json_client_(json_client) {
+ offset_(offset) {
DCHECK(IsForeignLayerType(type));
DCHECK(!IsCacheable());
}
@@ -74,10 +72,6 @@ cc::Layer* ForeignLayerDisplayItem::GetLayer() const {
return static_cast<const ForeignLayerDisplayItemClient&>(Client()).GetLayer();
}
-const LayerAsJSONClient* ForeignLayerDisplayItem::GetLayerAsJSONClient() const {
- return json_client_;
-}
-
bool ForeignLayerDisplayItem::Equals(const DisplayItem& other) const {
return GetType() == other.GetType() &&
GetLayer() ==
@@ -93,13 +87,12 @@ 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 PropertyTreeState* properties) {
+void RecordForeignLayer(GraphicsContext& context,
+ const DisplayItemClient& client,
+ DisplayItem::Type type,
+ scoped_refptr<cc::Layer> layer,
+ const FloatPoint& offset,
+ const PropertyTreeState* properties) {
PaintController& paint_controller = context.GetPaintController();
// This is like ScopedPaintChunkProperties but uses null id because foreign
// layer chunk doesn't need an id nor a client.
@@ -109,21 +102,11 @@ static void RecordForeignLayerInternal(GraphicsContext& context,
paint_controller.UpdateCurrentPaintChunkProperties(nullptr, *properties);
}
paint_controller.CreateAndAppend<ForeignLayerDisplayItem>(
- client, type, std::move(layer), offset, json_client);
+ client, type, std::move(layer), offset);
if (properties) {
paint_controller.UpdateCurrentPaintChunkProperties(nullptr,
*previous_properties);
}
}
-void RecordForeignLayer(GraphicsContext& context,
- const DisplayItemClient& client,
- DisplayItem::Type type,
- scoped_refptr<cc::Layer> layer,
- const FloatPoint& offset,
- const PropertyTreeState* properties) {
- RecordForeignLayerInternal(context, client, type, std::move(layer), offset,
- nullptr, properties);
-}
-
} // 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 7e662a566ef..c7a1dce66bd 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 LayerAsJSONClient;
// Represents foreign content (produced outside Blink) which draws to a layer.
// A client supplies a layer which can be unwrapped and inserted into the full
@@ -26,14 +25,11 @@ class PLATFORM_EXPORT ForeignLayerDisplayItem : public DisplayItem {
ForeignLayerDisplayItem(const DisplayItemClient& client,
Type,
scoped_refptr<cc::Layer>,
- const FloatPoint& offset,
- const LayerAsJSONClient*);
+ const FloatPoint& offset);
~ForeignLayerDisplayItem() override;
cc::Layer* GetLayer() const;
- const LayerAsJSONClient* GetLayerAsJSONClient() const;
-
// DisplayItem
bool Equals(const DisplayItem&) const final;
#if DCHECK_IS_ON()
@@ -44,7 +40,6 @@ class PLATFORM_EXPORT ForeignLayerDisplayItem : public DisplayItem {
private:
FloatPoint offset_;
- const LayerAsJSONClient* json_client_;
};
// When a foreign layer's debug name is a literal string, define a instance of
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 7a55af4fdb5..45c4e3c5068 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
@@ -108,7 +108,7 @@ class PLATFORM_EXPORT GeometryMapper {
SkMatrix ToSkMatrix() const {
if (LIKELY(IsIdentityOr2DTranslation())) {
- return SkMatrix::MakeTrans(Translation2D().Width(),
+ return SkMatrix::Translate(Translation2D().Width(),
Translation2D().Height());
}
return SkMatrix(TransformationMatrix::ToSkMatrix44(Matrix()));
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 9e32de68f0c..3f910019cec 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
@@ -884,17 +884,17 @@ TEST_P(GeometryMapperTest,
CheckMappings();
}
-TEST_P(GeometryMapperTest, ReflectionWithPaintOffset) {
+TEST_P(GeometryMapperTest, Reflection) {
CompositorFilterOperations filters;
filters.AppendReferenceFilter(paint_filter_builder::BuildBoxReflectFilter(
BoxReflection(BoxReflection::kHorizontalReflection, 0), nullptr));
- auto effect = CreateFilterEffect(e0(), filters, FloatPoint(100, 100));
+ auto effect = CreateFilterEffect(e0(), filters);
local_state.SetEffect(*effect);
input_rect = FloatRect(100, 100, 50, 50);
expected_transformed_rect = input_rect;
// Reflection is at (50, 100, 50, 50).
- expected_visual_rect = FloatClipRect(FloatRect(50, 100, 100, 50));
+ expected_visual_rect = FloatClipRect(FloatRect(-150, 100, 300, 50));
expected_visual_rect.ClearIsTight();
CheckMappings();
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 648cba4ab0b..fff9fe8bf80 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
@@ -107,16 +107,8 @@ SkColor PaintArtifact::SafeOpaqueBackgroundColor(
}
void PaintArtifact::FinishCycle() {
- // Until CompositeAfterPaint, PaintController::ClearPropertyTreeChangedStateTo
- // is used for clearing the property tree changed state at the end of paint
- // instead of in FinishCycle. See: LocalFrameView::RunPaintLifecyclePhase.
- bool clear_property_tree_changed =
- RuntimeEnabledFeatures::CompositeAfterPaintEnabled();
- for (auto& chunk : chunks_) {
+ for (auto& chunk : chunks_)
chunk.client_is_just_created = false;
- if (clear_property_tree_changed)
- chunk.properties.ClearChangedToRoot();
- }
}
} // namespace blink
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 d474d4ea698..25762fb17a2 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
@@ -56,8 +56,7 @@ class TestDisplayItemRequiringSeparateChunk : public ForeignLayerDisplayItem {
: ForeignLayerDisplayItem(client,
DisplayItem::kForeignLayerPlugin,
cc::Layer::Create(),
- FloatPoint(),
- nullptr) {}
+ FloatPoint()) {}
};
TEST_F(PaintChunkerTest, Empty) {
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 b2343d16fbb..452115c6525 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
@@ -27,9 +27,12 @@ PaintController::PaintController(Usage usage)
}
PaintController::~PaintController() {
- // New display items should be committed before PaintController is destroyed,
- // except for transient paint controllers.
- DCHECK(usage_ == kTransient || new_display_item_list_.IsEmpty());
+ if (usage_ == kMultiplePaints) {
+ // New display items should have been committed.
+ DCHECK(new_display_item_list_.IsEmpty());
+ // And the committed_ flag should have been cleared by FinishCycle().
+ DCHECK(!committed_);
+ }
}
// For micro benchmarks of record time.
@@ -277,11 +280,14 @@ void PaintController::DidAppendItem(DisplayItem& display_item) {
if (usage_ == kTransient)
return;
+ if (!display_item.IsMovedFromCachedSubsequence())
+ display_item.Client().SetIsInPaintControllerBeforeFinishCycle(true);
+
#if DCHECK_IS_ON()
if (display_item.IsCacheable()) {
- auto index = FindMatchingItemFromIndex(display_item.GetId(),
- new_display_item_indices_by_client_,
- new_display_item_list_);
+ auto index = FindItemFromIdIndexMap(display_item.GetId(),
+ new_display_item_id_index_map_,
+ new_display_item_list_);
if (index != kNotFound) {
ShowDebugData();
NOTREACHED() << "DisplayItem " << display_item.AsDebugString().Utf8()
@@ -289,9 +295,8 @@ void PaintController::DidAppendItem(DisplayItem& display_item) {
<< new_display_item_list_[index].AsDebugString().Utf8()
<< " (index=" << index << ")";
}
- AddToIndicesByClientMap(display_item.Client(),
- new_display_item_list_.size() - 1,
- new_display_item_indices_by_client_);
+ AddToIdIndexMap(display_item.GetId(), new_display_item_list_.size() - 1,
+ new_display_item_id_index_map_);
}
#endif
@@ -328,11 +333,17 @@ DisplayItem& PaintController::MoveItemFromCurrentListToNewList(
}
void PaintController::DidAppendChunk() {
+ if (usage_ == kMultiplePaints &&
+ !new_paint_chunks_.LastChunk().is_moved_from_cached_subsequence) {
+ new_paint_chunks_.LastChunk()
+ .id.client.SetIsInPaintControllerBeforeFinishCycle(true);
+ }
+
#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_);
+ AddToIdIndexMap(new_paint_chunks_.LastChunk().id,
+ new_paint_chunks_.size() - 1,
+ new_paint_chunk_id_index_map_);
}
#endif
}
@@ -386,36 +397,27 @@ bool PaintController::ClientCacheIsValid(
return client.IsValid();
}
-wtf_size_t PaintController::FindMatchingItemFromIndex(
+wtf_size_t PaintController::FindItemFromIdIndexMap(
const DisplayItem::Id& id,
- const IndicesByClientMap& display_item_indices_by_client,
+ const IdIndexMap& display_item_id_index_map,
const DisplayItemList& list) {
- IndicesByClientMap::const_iterator it =
- display_item_indices_by_client.find(&id.client);
- if (it == display_item_indices_by_client.end())
+ auto it = display_item_id_index_map.find(IdAsHashKey(id));
+ if (it == display_item_id_index_map.end())
return kNotFound;
- for (auto index : it->value) {
- const DisplayItem& existing_item = list[index];
- if (existing_item.IsTombstone())
- continue;
- DCHECK(existing_item.Client() == id.client);
- if (id == existing_item.GetId())
- return index;
- }
-
- return kNotFound;
+ wtf_size_t index = it->value;
+ const DisplayItem& existing_item = list[index];
+ if (existing_item.IsTombstone())
+ return kNotFound;
+ DCHECK_EQ(existing_item.GetId(), id);
+ return index;
}
-void PaintController::AddToIndicesByClientMap(const DisplayItemClient& client,
- wtf_size_t index,
- IndicesByClientMap& map) {
- auto it = map.find(&client);
- auto& indices =
- it == map.end()
- ? map.insert(&client, Vector<wtf_size_t>()).stored_value->value
- : it->value;
- indices.push_back(index);
+void PaintController::AddToIdIndexMap(const DisplayItem::Id& id,
+ wtf_size_t index,
+ IdIndexMap& map) {
+ DCHECK(!map.Contains(IdAsHashKey(id)));
+ map.insert(IdAsHashKey(id), index);
}
wtf_size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
@@ -440,8 +442,8 @@ wtf_size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
}
wtf_size_t found_index =
- FindMatchingItemFromIndex(id, out_of_order_item_indices_,
- current_paint_artifact_->GetDisplayItemList());
+ FindItemFromIdIndexMap(id, out_of_order_item_id_index_map_,
+ current_paint_artifact_->GetDisplayItemList());
if (found_index != kNotFound) {
#if DCHECK_IS_ON()
++num_out_of_order_matches_;
@@ -470,7 +472,7 @@ wtf_size_t PaintController::FindOutOfOrderCachedItemForward(
#if DCHECK_IS_ON()
++num_indexed_items_;
#endif
- AddToIndicesByClientMap(item.Client(), i, out_of_order_item_indices_);
+ AddToIdIndexMap(item.GetId(), i, out_of_order_item_id_index_map_);
next_item_to_index_ = i + 1;
}
}
@@ -560,8 +562,8 @@ void PaintController::CommitNewDisplayItems() {
num_cached_new_items_ = 0;
num_cached_new_subsequences_ = 0;
#if DCHECK_IS_ON()
- new_display_item_indices_by_client_.clear();
- new_paint_chunk_indices_by_client_.clear();
+ new_display_item_id_index_map_.clear();
+ new_paint_chunk_id_index_map_.clear();
#endif
cache_is_all_invalid_ = false;
@@ -578,7 +580,7 @@ void PaintController::CommitNewDisplayItems() {
new_paint_chunks_.ReleasePaintChunks());
ResetCurrentListIndices();
- out_of_order_item_indices_.clear();
+ out_of_order_item_id_index_map_.clear();
// We'll allocate the initial buffer when we start the next paint.
new_display_item_list_ = DisplayItemList(0);
@@ -591,69 +593,65 @@ void PaintController::CommitNewDisplayItems() {
}
void PaintController::FinishCycle() {
- if (usage_ != kTransient) {
+ if (usage_ == kTransient || !committed_)
+ return;
+
#if DCHECK_IS_ON()
- DCHECK(new_display_item_list_.IsEmpty());
- DCHECK(new_paint_chunks_.IsInInitialState());
+ DCHECK(new_display_item_list_.IsEmpty());
+ DCHECK(new_paint_chunks_.IsInInitialState());
#endif
- if (committed_) {
- committed_ = false;
+ committed_ = false;
- // Validate display item clients that have validly cached subsequence or
- // display items in this PaintController.
- for (auto& item : current_cached_subsequences_) {
- if (item.key->IsCacheable())
- item.key->Validate();
- }
- 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.
+ // Validate display item clients that have validly cached subsequence or
+ // display items in this PaintController.
+ for (auto& item : current_cached_subsequences_) {
+ if (item.key->IsCacheable())
+ item.key->Validate();
+ }
+ 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());
+ 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()) {
- const auto& client = chunk.id.client;
- if (chunk.is_moved_from_cached_subsequence) {
+ continue;
+ }
+ client.ClearPartialInvalidationVisualRect();
+ if (client.IsCacheable())
+ client.Validate();
+ client.SetIsInPaintControllerBeforeFinishCycle(false);
+ }
+ for (const auto& chunk : current_paint_artifact_->PaintChunks()) {
+ 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());
+ DCHECK(client.IsAlive());
+ DCHECK(client.IsValid() || !client.IsCacheable());
#endif
- continue;
- }
- if (client.IsCacheable())
- client.Validate();
- }
+ continue;
}
-
- current_paint_artifact_->FinishCycle();
+ if (client.IsCacheable())
+ client.Validate();
+ client.SetIsInPaintControllerBeforeFinishCycle(false);
}
+ current_paint_artifact_->FinishCycle();
+
if (VLOG_IS_ON(1)) {
- // Only log for non-transient paint controllers. Before CompositeAfterPaint,
- // there is an additional paint controller used to collect foreign layers,
- // and this can be logged by removing the "usage_ != kTransient" condition.
- if (usage_ != kTransient) {
- LOG(ERROR) << "PaintController::FinishCycle() completed";
+ LOG(ERROR) << "PaintController::FinishCycle() completed";
#if DCHECK_IS_ON()
- if (VLOG_IS_ON(3))
- ShowDebugDataWithPaintRecords();
- else if (VLOG_IS_ON(2))
- ShowDebugData();
- else if (VLOG_IS_ON(1))
- ShowCompactDebugData();
+ if (VLOG_IS_ON(3))
+ ShowDebugDataWithPaintRecords();
+ else if (VLOG_IS_ON(2))
+ ShowDebugData();
+ else if (VLOG_IS_ON(1))
+ ShowCompactDebugData();
#endif
- }
}
}
@@ -830,17 +828,12 @@ void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) {
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_.PaintChunks()[index];
- if (chunk.id == id) {
- ShowDebugData();
- NOTREACHED() << "New paint chunk id " << id
- << " has duplicated id with previous chuck " << chunk;
- }
- }
+ auto it = new_paint_chunk_id_index_map_.find(IdAsHashKey(id));
+ if (it != new_paint_chunk_id_index_map_.end()) {
+ ShowDebugData();
+ NOTREACHED() << "New paint chunk id " << id
+ << " has duplicated id with previous chuck "
+ << new_paint_chunks_.PaintChunks()[it->value];
}
#endif
}
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 1aaea13fdda..fd9cc8ae4fa 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
@@ -21,6 +21,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/hash_functions.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/vector.h"
@@ -233,8 +234,8 @@ class PLATFORM_EXPORT PaintController {
// The current fragment will be part of the ids of all display items and
// paint chunks, to uniquely identify display items in different fragments
// for the same client and type.
- unsigned CurrentFragment() const { return current_fragment_; }
- void SetCurrentFragment(unsigned fragment) { current_fragment_ = fragment; }
+ wtf_size_t CurrentFragment() const { return current_fragment_; }
+ void SetCurrentFragment(wtf_size_t fragment) { current_fragment_ = fragment; }
// The client may skip a paint when nothing changed. In the case, the client
// calls this method to update UMA counts as a fully cached paint.
@@ -280,16 +281,53 @@ class PLATFORM_EXPORT PaintController {
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<wtf_size_t>>;
+ struct IdAsHashKey {
+ IdAsHashKey() = default;
+ explicit IdAsHashKey(const DisplayItem::Id& id)
+ : client(&id.client), type(id.type), fragment(id.fragment) {}
+ explicit IdAsHashKey(WTF::HashTableDeletedValueType) {
+ HashTraits<const DisplayItemClient*>::ConstructDeletedValue(client,
+ false);
+ }
+ bool IsHashTableDeletedValue() const {
+ return HashTraits<const DisplayItemClient*>::IsDeletedValue(client);
+ }
+ bool operator==(const IdAsHashKey& other) const {
+ return client == other.client && type == other.type &&
+ fragment == other.fragment;
+ }
+
+ const DisplayItemClient* client = nullptr;
+ DisplayItem::Type type = static_cast<DisplayItem::Type>(0);
+ wtf_size_t fragment = 0;
+ };
+
+ struct IdHash {
+ STATIC_ONLY(IdHash);
+ static unsigned GetHash(const IdAsHashKey& id) {
+ unsigned hash = PtrHash<const DisplayItemClient>::GetHash(id.client);
+ WTF::AddIntToHash(hash, id.type);
+ WTF::AddIntToHash(hash, id.fragment);
+ return hash;
+ }
+ static bool Equal(const IdAsHashKey& a, const IdAsHashKey& b) {
+ return a == b;
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+ };
+
+ // Maps a display item id to the index of the display item or the paint chunk.
+ using IdIndexMap = HashMap<IdAsHashKey,
+ wtf_size_t,
+ IdHash,
+ SimpleClassHashTraits<IdAsHashKey>>;
- static wtf_size_t FindMatchingItemFromIndex(const DisplayItem::Id&,
- const IndicesByClientMap&,
- const DisplayItemList&);
- static void AddToIndicesByClientMap(const DisplayItemClient&,
- wtf_size_t index,
- IndicesByClientMap&);
+ static wtf_size_t FindItemFromIdIndexMap(const DisplayItem::Id&,
+ const IdIndexMap&,
+ const DisplayItemList&);
+ static void AddToIdIndexMap(const DisplayItem::Id&,
+ wtf_size_t index,
+ IdIndexMap&);
wtf_size_t FindCachedItem(const DisplayItem::Id&);
wtf_size_t FindOutOfOrderCachedItemForward(const DisplayItem::Id&);
@@ -356,7 +394,7 @@ class PLATFORM_EXPORT PaintController {
wtf_size_t num_cached_new_items_ = 0;
wtf_size_t num_cached_new_subsequences_ = 0;
- // Stores indices to valid cacheable display items in
+ // Maps from ids to indices of valid cacheable display items in
// current_paint_artifact_.GetDisplayItemList() that have not been matched by
// requests of cached display items (using UseCachedItemIfPossible() and
// UseCachedSubsequenceIfPossible()) during sequential matching. The indexed
@@ -365,7 +403,7 @@ class PLATFORM_EXPORT PaintController {
// requested, we only traverse at most once over the current display list
// looking for potential matches. Thus we can ensure that the algorithm runs
// in linear time.
- IndicesByClientMap out_of_order_item_indices_;
+ IdIndexMap out_of_order_item_id_index_map_;
// The next item in the current list for sequential match.
wtf_size_t next_item_to_match_ = 0;
@@ -380,9 +418,9 @@ class PLATFORM_EXPORT PaintController {
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_;
+ IdIndexMap new_display_item_id_index_map_;
// This is used to check duplicated ids for new paint chunks.
- IndicesByClientMap new_paint_chunk_indices_by_client_;
+ IdIndexMap new_paint_chunk_id_index_map_;
#endif
// These are set in UseCachedItemIfPossible() and
@@ -402,7 +440,7 @@ class PLATFORM_EXPORT PaintController {
CachedSubsequenceMap new_cached_subsequences_;
wtf_size_t last_cached_subsequence_end_ = 0;
- unsigned current_fragment_ = 0;
+ wtf_size_t current_fragment_ = 0;
// Accumulated counts for UMA metrics. Updated by UpdateUMACounts() and
// UpdateUMACountsOnFullyCached(), and reported as UMA metrics and reset by
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 3f1b1142cce..c7cdde2c21e 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
@@ -111,6 +111,9 @@ MATCHER_P(IsSameId, id, "") {
MATCHER_P2(IsSameId, client, type, "") {
return arg.GetId() == DisplayItem::Id(*client, type);
}
+MATCHER_P3(IsSameId, client, type, fragment, "") {
+ return arg.GetId() == DisplayItem::Id(*client, type, fragment);
+}
// Matcher for checking paint chunks. Sample usage:
// EXPACT_THAT(paint_controller.PaintChunks(),
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 d4598f77c81..7d78ed41dc5 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
@@ -40,7 +40,6 @@ class RasterInvalidatorTest : public testing::Test,
void FinishCycle(PaintArtifact& artifact) {
artifact.FinishCycle();
ClearGeometryMapperCache();
- // See PaintArtifact::FinishCycle() for the reason of doing this.
for (auto& chunk : artifact.PaintChunks())
chunk.properties.ClearChangedToRoot();
}
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 23ff52e3096..021cf196463 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
@@ -16,7 +16,7 @@ class ScopedDisplayItemFragment final {
STACK_ALLOCATED();
public:
- ScopedDisplayItemFragment(GraphicsContext& context, unsigned fragment)
+ ScopedDisplayItemFragment(GraphicsContext& context, wtf_size_t fragment)
: context_(context),
original_fragment_(context.GetPaintController().CurrentFragment()) {
context.GetPaintController().SetCurrentFragment(fragment);
@@ -27,7 +27,7 @@ class ScopedDisplayItemFragment final {
private:
GraphicsContext& context_;
- unsigned original_fragment_;
+ wtf_size_t original_fragment_;
DISALLOW_COPY_AND_ASSIGN(ScopedDisplayItemFragment);
};
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 b44b2c24da8..3ba36544832 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
@@ -127,7 +127,8 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
bool affected_by_outer_viewport_bounds_delta : 1;
bool in_subtree_of_page_scale : 1;
bool animation_is_axis_aligned : 1;
- } flags = {false, false, true, false};
+ bool delegates_to_parent_for_backface : 1;
+ } flags = {false, false, true, false, false};
BackfaceVisibility backface_visibility = BackfaceVisibility::kInherited;
unsigned rendering_context_id = 0;
CompositingReasons direct_compositing_reasons = CompositingReason::kNone;
@@ -145,6 +146,8 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
other.flags.in_subtree_of_page_scale ||
flags.animation_is_axis_aligned !=
other.flags.animation_is_axis_aligned ||
+ flags.delegates_to_parent_for_backface !=
+ other.flags.delegates_to_parent_for_backface ||
backface_visibility != other.backface_visibility ||
rendering_context_id != other.rendering_context_id ||
compositor_element_id != other.compositor_element_id ||
@@ -351,7 +354,8 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
}
bool HasDirectCompositingReasonsOtherThan3dTransform() const {
- return DirectCompositingReasons() & ~CompositingReason::k3DTransform;
+ return DirectCompositingReasons() & ~CompositingReason::k3DTransform &
+ ~CompositingReason::kTrivial3DTransform;
}
// TODO(crbug.com/900241): Use HaveActiveTransformAnimation() instead of this
@@ -386,6 +390,10 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
return state_.compositor_element_id;
}
+ bool DelegatesToParentForBackface() const {
+ return state_.flags.delegates_to_parent_for_backface;
+ }
+
// Content whose transform nodes have a common rendering context ID are 3D
// sorted. If this is 0, content will not be 3D sorted.
unsigned RenderingContextId() const { return state_.rendering_context_id; }
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
index b280227a6ec..dd4b0765fa7 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint_invalidation_reason.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
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 794ee34f59a..4a7480b5b9f 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc
@@ -67,7 +67,7 @@ void DrawIcon(cc::PaintCanvas* canvas,
icon_image->PaintImageForCurrentFrame(),
IntRect(IntPoint::Zero(), icon_image->Size()),
FloatRect(x, y, scale_factor * kIconWidth, scale_factor * kIconHeight),
- &flags, cc::PaintCanvas::kFast_SrcRectConstraint);
+ &flags, SkCanvas::kFast_SrcRectConstraint);
}
void DrawCenteredIcon(cc::PaintCanvas* canvas,
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 b35b6ce5449..1c060f05d52 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
@@ -147,12 +147,11 @@ void DrawPlatformFocusRing(const PrimitiveType&,
float width,
float border_radius);
-// TODO(fmalita): remove in favor of direct SrcRectConstraint use.
-inline cc::PaintCanvas::SrcRectConstraint
-WebCoreClampingModeToSkiaRectConstraint(Image::ImageClampingMode clamp_mode) {
+inline SkCanvas::SrcRectConstraint WebCoreClampingModeToSkiaRectConstraint(
+ Image::ImageClampingMode clamp_mode) {
return clamp_mode == Image::kClampImageToSourceRect
- ? cc::PaintCanvas::kStrict_SrcRectConstraint
- : cc::PaintCanvas::kFast_SrcRectConstraint;
+ ? SkCanvas::kStrict_SrcRectConstraint
+ : SkCanvas::kFast_SrcRectConstraint;
}
// Attempts to allocate an SkData on the PartitionAlloc buffer partition.
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 2531ed49527..8182c62d744 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
@@ -185,8 +185,7 @@ void VideoFrameSubmitter::DidReceiveCompositorFrameAck(
void VideoFrameSubmitter::OnBeginFrame(
const viz::BeginFrameArgs& args,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>
- timing_details) {
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>& timing_details) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
@@ -195,30 +194,37 @@ void VideoFrameSubmitter::OnBeginFrame(
for (const auto& pair : timing_details) {
if (viz::FrameTokenGT(pair.key, *next_frame_token_))
continue;
-
+ auto& feedback = pair.value.presentation_feedback;
#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);
+ bool presentation_failure =
+ 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);
+ feedback.timestamp, feedback.interval, feedback.flags));
+
+ // We assume that presentation feedback is reliable if
+ // 1. (kHWCompletion) OS told us that the frame was shown at that time
+ // or
+ // 2. (kVSync) at least presentation time is aligned with vsyncs intervals
+ uint32_t reliable_feedback_mask =
+ gfx::PresentationFeedback::kHWCompletion |
+ gfx::PresentationFeedback::kVSync;
+ bool reliable_timestamp = feedback.flags & reliable_feedback_mask;
+ roughness_reporter_->FramePresented(pair.key, feedback.timestamp,
+ reliable_timestamp);
}
ignorable_submitted_frames_.erase(pair.key);
TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
"media", "VideoFrameSubmitter", TRACE_ID_LOCAL(pair.key),
- pair.value->presentation_feedback->timestamp);
+ feedback.timestamp);
}
frame_trackers_.NotifyBeginImplFrame(args);
@@ -491,7 +497,7 @@ bool VideoFrameSubmitter::SubmitFrame(
compositor_frame_sink_->SubmitCompositorFrame(
child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(),
- std::move(compositor_frame), nullptr, 0);
+ std::move(compositor_frame), base::nullopt, 0);
frame_trackers_.NotifySubmitFrame(frame_token, false, begin_frame_ack,
last_begin_frame_args_);
resource_provider_->ReleaseFrameResources();
@@ -520,7 +526,7 @@ void VideoFrameSubmitter::SubmitEmptyFrame() {
compositor_frame_sink_->SubmitCompositorFrame(
child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
.local_surface_id(),
- std::move(compositor_frame), nullptr, 0);
+ std::move(compositor_frame), base::nullopt, 0);
frame_trackers_.NotifySubmitFrame(frame_token, false, begin_frame_ack,
last_begin_frame_args_);
@@ -568,9 +574,8 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame(
? video_frame_provider_->GetPreferredRenderInterval()
: viz::BeginFrameArgs::MinInterval();
- base::TimeTicks value;
- if (video_frame && video_frame->metadata()->GetTimeTicks(
- media::VideoFrameMetadata::DECODE_END_TIME, &value)) {
+ if (video_frame && video_frame->metadata()->decode_end_time.has_value()) {
+ base::TimeTicks value = *video_frame->metadata()->decode_end_time;
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
"media", "VideoFrameSubmitter", TRACE_ID_LOCAL(frame_token), value);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
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 e1543a6f8d5..f1d5fb758e8 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
@@ -75,8 +75,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter
const WTF::Vector<viz::ReturnedResource>& resources) override;
void OnBeginFrame(
const viz::BeginFrameArgs&,
- WTF::HashMap<uint32_t, ::viz::mojom::blink::FrameTimingDetailsPtr>)
- override;
+ const WTF::HashMap<uint32_t, viz::FrameTimingDetails>&) override;
void OnBeginFramePausedChanged(bool paused) override {}
void ReclaimResources(
const WTF::Vector<viz::ReturnedResource>& resources) override;
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 3193c5ad4eb..d9f98428558 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
@@ -89,7 +89,7 @@ class VideoMockCompositorFrameSink
void SubmitCompositorFrame(
const viz::LocalSurfaceId& id,
viz::CompositorFrame frame,
- viz::mojom::blink::HitTestRegionListPtr hit_test_region_list,
+ base::Optional<viz::HitTestRegionList> hit_test_region_list,
uint64_t submit_time) override {
last_submitted_compositor_frame_ = std::move(frame);
DoSubmitCompositorFrame(id, &last_submitted_compositor_frame_);
@@ -97,7 +97,7 @@ class VideoMockCompositorFrameSink
void SubmitCompositorFrameSync(
const viz::LocalSurfaceId& id,
viz::CompositorFrame frame,
- viz::mojom::blink::HitTestRegionListPtr hit_test_region_list,
+ base::Optional<viz::HitTestRegionList> hit_test_region_list,
uint64_t submit_time,
const SubmitCompositorFrameSyncCallback callback) override {
last_submitted_compositor_frame_ = std::move(frame);
@@ -959,10 +959,8 @@ TEST_F(VideoFrameSubmitterTest, ProcessTimingDetails) {
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;
+ fps * (cc::VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit + 1);
+ WTF::HashMap<uint32_t, viz::FrameTimingDetails> timing_details;
MakeSubmitter(
base::BindLambdaForTesting([&](int frames, base::TimeDelta duration,
@@ -975,14 +973,13 @@ TEST_F(VideoFrameSubmitterTest, ProcessTimingDetails) {
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 =
+ viz::FrameTimingDetails details;
+ details.presentation_feedback.timestamp =
base::TimeTicks() + frame_duration * token;
+ details.presentation_feedback.flags =
+ gfx::PresentationFeedback::kHWCompletion;
timing_details.clear();
- timing_details.Set(token, std::move(details));
+ timing_details.Set(token, details);
};
EXPECT_CALL(*video_frame_provider_, UpdateCurrentFrame)
@@ -998,14 +995,13 @@ TEST_F(VideoFrameSubmitterTest, ProcessTimingDetails) {
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);
+ frame->metadata()->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));
+ submitter_->OnBeginFrame(args, timing_details);
task_environment_.RunUntilIdle();
AckSubmittedFrame();
}
diff --git a/chromium/third_party/blink/renderer/platform/heap/BUILD.gn b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn
index d868c461141..311ef6c7c4d 100644
--- a/chromium/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -27,6 +27,10 @@ jumbo_source_set("heap_unsanitized") {
configs +=
[ "//third_party/blink/renderer/platform:blink_platform_implementation" ]
+ # std::atomic<>:: functions must be inlined.
+ configs -= [ "//build/config/compiler:default_optimization" ]
+ configs += [ "//build/config/compiler:optimize_max" ]
+
sources = [
"unsanitized_atomic.cc",
"unsanitized_atomic.h",
@@ -68,6 +72,8 @@ blink_platform_sources("heap") {
"heap_stats_collector.cc",
"heap_stats_collector.h",
"heap_traits.h",
+ "marking_scheduling_oracle.cc",
+ "marking_scheduling_oracle.h",
"marking_verifier.cc",
"marking_verifier.h",
"marking_visitor.cc",
@@ -148,30 +154,31 @@ jumbo_source_set("blink_heap_unittests_sources") {
testonly = true
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",
- "heap_stats_collector_test.cc",
- "heap_test.cc",
- "heap_thread_test.cc",
- "heap_traits_test.cc",
- "incremental_marking_test.cc",
- "marking_verifier_test.cc",
- "name_trait_test.cc",
- "object_start_bitmap_test.cc",
- "persistent_test.cc",
- "thread_state_scheduling_test.cc",
- "weakness_marking_test.cc",
- "worklist_test.cc",
- "write_barrier_perftest.cc",
+ "test/blink_gc_memory_dump_provider_test.cc",
+ "test/cancelable_task_scheduler_test.cc",
+ "test/card_table_test.cc",
+ "test/concurrent_marking_test.cc",
+ "test/gc_info_test.cc",
+ "test/heap_compact_test.cc",
+ "test/heap_linked_stack_test.cc",
+ "test/heap_stats_collector_test.cc",
+ "test/heap_test.cc",
+ "test/heap_thread_test.cc",
+ "test/heap_traits_test.cc",
+ "test/incremental_marking_test.cc",
+ "test/marking_scheduling_oracle_test.cc",
+ "test/marking_verifier_test.cc",
+ "test/name_trait_test.cc",
+ "test/object_start_bitmap_test.cc",
+ "test/persistent_test.cc",
+ "test/thread_state_scheduling_test.cc",
+ "test/weakness_marking_test.cc",
+ "test/worklist_test.cc",
+ "test/write_barrier_perftest.cc",
]
if (enable_blink_heap_young_generation) {
- sources += [ "minor_gc_test.cc" ]
+ sources += [ "test/minor_gc_test.cc" ]
}
configs += [
diff --git a/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
index b5889d983d9..7ab1b9e64fe 100644
--- a/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
+++ b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
@@ -93,7 +93,7 @@ The [tracing](#Tracing) method of a garbage-collected class, if any, must contai
class P : public GarbageCollectedMixin {
public:
// OK: Needs to trace q_.
- virtual void Trace(Visitor* visitor) { visitor->Trace(q_); }
+ virtual void Trace(Visitor* visitor) const { visitor->Trace(q_); }
private:
// OK: Allowed to have Member<T>.
Member<Q> q_;
@@ -103,7 +103,7 @@ class A final : public GarbageCollected<A>, public P {
USING_GARBAGE_COLLECTED_MIXIN(A);
public:
// Delegating call for P is needed.
- virtual void Trace(Visitor* visitor) { P::Trace(visitor); }
+ virtual void Trace(Visitor* visitor) const { P::Trace(visitor); }
};
```
@@ -333,19 +333,19 @@ The basic form of tracing is illustrated below:
class SomeGarbageCollectedClass final
: public GarbageCollected<SomeGarbageCollectedClass> {
public:
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
private:
Member<AnotherGarbageCollectedClass> another_;
};
// In an implementation file:
-void SomeGarbageCollectedClass::Trace(Visitor* visitor) {
+void SomeGarbageCollectedClass::Trace(Visitor* visitor) const {
visitor->Trace(another_);
}
```
-Specifically, if your class needs a tracing method, you need to declare and define a `Trace(Visitor*)` method.
+Specifically, if your class needs a tracing method, you need to declare and define a `Trace(Visitor*) const` method.
This method is normally declared in the header file and defined once in the implementation file, but there are variations.
Another common variation is to declare a virtual `Trace()` for base classes that will be subclassed.
@@ -360,7 +360,7 @@ The following example shows more involved usage:
```c++
class A : public GarbageCollected<A> {
public:
- virtual void Trace(Visitor*) {} // Nothing to trace here.
+ virtual void Trace(Visitor*) const {} // Nothing to trace here.
};
class B : public A {
@@ -369,7 +369,7 @@ class B : public A {
class C final : public B {
public:
- void Trace(Visitor*) final;
+ void Trace(Visitor*) const final;
private:
Member<X> x_;
@@ -377,7 +377,7 @@ class C final : public B {
HeapVector<Member<Z>> z_;
};
-void C::Trace(Visitor* visitor) {
+void C::Trace(Visitor* visitor) const {
visitor->Trace(x_);
visitor->Trace(y_); // Weak member needs to be traced.
visitor->Trace(z_); // Heap collection does, too.
@@ -408,7 +408,7 @@ Heap collections do not inherit from `GarbageCollected` but are nonetheless allo
```c++
class A final : public GarbageCollected<A> {
public:
- void Trace(Visitor* visitor) { visitor->Trace(vec_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(vec_); }
private:
HeapVector<Member<B>> vec_;
};
@@ -464,14 +464,14 @@ The following example shows how this can be used:
class W final : public GarbageCollected<W> {
public:
- virtual void Trace(Visitor*);
+ virtual void Trace(Visitor*) const;
private:
void ProcessCustomWeakness(const LivenessBroker&);
UntracedMember<C> other_;
};
-void W::Trace(Visitor* visitor) {
+void W::Trace(Visitor* visitor) const {
visitor->template RegisterCustomWeakMethod<W, &W::ProcessCustomWeakness>(this);
}
@@ -520,7 +520,7 @@ class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass>
class MyNonGCButTraceableClass {
public:
- void Trace(Visitor* visitor) {
+ void Trace(Visitor* visitor) const {
// ...
}
};
@@ -548,7 +548,7 @@ class MyGarbageCollectedClass : public GarbageCollected<MyGarbageCollectedClass>
class MyNonGCButTraceableClass {
public:
- void Trace(Visitor* visitor) {
+ void Trace(Visitor* visitor) const {
// ...
}
};
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 ade27451672..6b8af009533 100644
--- a/chromium/third_party/blink/renderer/platform/heap/blink_gc.h
+++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc.h
@@ -125,9 +125,6 @@ class PLATFORM_EXPORT BlinkGC final {
kV8MajorGC,
};
- // Sentinel used to mark not-fully-constructed during mixins.
- static constexpr void* kNotFullyConstructedObject = nullptr;
-
static const char* ToString(GCReason);
static const char* ToString(MarkingType);
static const char* ToString(StackState);
diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc b/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc
deleted file mode 100644
index 7ad0cee1cea..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider_test.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
-
-#include "base/trace_event/process_memory_dump.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/platform/heap/blink_gc.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/wtf/threading.h"
-
-namespace blink {
-
-namespace {
-class BlinkGCMemoryDumpProviderTest : public TestSupportingGC {};
-
-void CheckBasicHeapDumpStructure(base::trace_event::MemoryAllocatorDump* dump) {
- ASSERT_NE(nullptr, dump);
-
- bool found_allocated_object_size = false;
- bool found_size = false;
- for (const auto& entry : dump->entries()) {
- if (entry.name == "allocated_objects_size")
- found_allocated_object_size = true;
- if (entry.name == "size")
- found_size = true;
- }
- EXPECT_TRUE(found_allocated_object_size);
- EXPECT_TRUE(found_size);
-}
-
-} // namespace
-
-TEST_F(BlinkGCMemoryDumpProviderTest, MainThreadLightDump) {
- base::trace_event::MemoryDumpArgs args = {
- base::trace_event::MemoryDumpLevelOfDetail::LIGHT};
- std::unique_ptr<base::trace_event::ProcessMemoryDump> dump(
- new base::trace_event::ProcessMemoryDump(args));
- std::unique_ptr<BlinkGCMemoryDumpProvider> dump_provider(
- new BlinkGCMemoryDumpProvider(
- ThreadState::Current(), base::ThreadTaskRunnerHandle::Get(),
- BlinkGCMemoryDumpProvider::HeapType::kBlinkMainThread));
- dump_provider->OnMemoryDump(args, dump.get());
-
- auto* main_heap = dump->GetAllocatorDump("blink_gc/main/heap");
- CheckBasicHeapDumpStructure(main_heap);
-}
-
-TEST_F(BlinkGCMemoryDumpProviderTest, MainThreadDetailedDump) {
- base::trace_event::MemoryDumpArgs args = {
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
- std::unique_ptr<base::trace_event::ProcessMemoryDump> dump(
- new base::trace_event::ProcessMemoryDump(args));
- std::unique_ptr<BlinkGCMemoryDumpProvider> dump_provider(
- new BlinkGCMemoryDumpProvider(
- ThreadState::Current(), base::ThreadTaskRunnerHandle::Get(),
- BlinkGCMemoryDumpProvider::HeapType::kBlinkMainThread));
- dump_provider->OnMemoryDump(args, dump.get());
-
- // All arenas should be present in the dump.
-#define CheckArena(name) \
- CheckBasicHeapDumpStructure( \
- dump->GetAllocatorDump("blink_gc/main/heap/" #name "Arena"));
-
- FOR_EACH_ARENA(CheckArena)
-#undef CheckArena
-}
-
-TEST_F(BlinkGCMemoryDumpProviderTest, WorkerLightDump) {
- base::trace_event::MemoryDumpArgs args = {
- base::trace_event::MemoryDumpLevelOfDetail::LIGHT};
- std::unique_ptr<base::trace_event::ProcessMemoryDump> dump(
- new base::trace_event::ProcessMemoryDump(args));
- std::unique_ptr<BlinkGCMemoryDumpProvider> dump_provider(
- new BlinkGCMemoryDumpProvider(
- ThreadState::Current(), base::ThreadTaskRunnerHandle::Get(),
- BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread));
- dump_provider->OnMemoryDump(args, dump.get());
-
- // There should be no main thread heap dump available.
- ASSERT_EQ(nullptr, dump->GetAllocatorDump("blink_gc/main/heap"));
-
- size_t workers_found = 0;
- for (const auto& kvp : dump->allocator_dumps()) {
- if (kvp.first.find("blink_gc/workers/heap") != std::string::npos) {
- workers_found++;
- CheckBasicHeapDumpStructure(dump->GetAllocatorDump(kvp.first));
- }
- }
- EXPECT_EQ(1u, workers_found);
-}
-
-TEST_F(BlinkGCMemoryDumpProviderTest, WorkerDetailedDump) {
- base::trace_event::MemoryDumpArgs args = {
- base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
- std::unique_ptr<base::trace_event::ProcessMemoryDump> dump(
- new base::trace_event::ProcessMemoryDump(args));
- std::unique_ptr<BlinkGCMemoryDumpProvider> dump_provider(
- new BlinkGCMemoryDumpProvider(
- ThreadState::Current(), base::ThreadTaskRunnerHandle::Get(),
- BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread));
- dump_provider->OnMemoryDump(args, dump.get());
-
- // There should be no main thread heap dump available.
- ASSERT_EQ(nullptr, dump->GetAllocatorDump("blink_gc/main/heap"));
-
- // Find worker suffix.
- std::string worker_suffix;
- for (const auto& kvp : dump->allocator_dumps()) {
- if (kvp.first.find("blink_gc/workers/heap/worker_0x") !=
- std::string::npos) {
- auto start_pos = kvp.first.find("_0x");
- auto end_pos = kvp.first.find("/", start_pos);
- worker_suffix = kvp.first.substr(start_pos + 1, end_pos - start_pos - 1);
- }
- }
- std::string worker_base_path =
- "blink_gc/workers/heap/worker_" + worker_suffix;
- CheckBasicHeapDumpStructure(dump->GetAllocatorDump(worker_base_path));
-
-#define CheckArena(name) \
- CheckBasicHeapDumpStructure( \
- dump->GetAllocatorDump(worker_base_path + "/" #name "Arena"));
-
- FOR_EACH_ARENA(CheckArena)
-#undef CheckArena
-}
-
-} // namespace blink
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
deleted file mode 100644
index 97f4c8bf38d..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc
+++ /dev/null
@@ -1,96 +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/heap/cancelable_task_scheduler.h"
-
-#include <atomic>
-
-#include "base/memory/scoped_refptr.h"
-#include "base/task_runner.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
-#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-
-namespace blink {
-
-class ParallelTaskRunner : public base::TaskRunner {
- public:
- bool PostDelayedTask(const base::Location& location,
- base::OnceClosure task,
- base::TimeDelta) override {
- worker_pool::PostTask(location, WTF::CrossThreadBindOnce(std::move(task)));
- return true;
- }
-
- void RunUntilIdle() {}
-};
-
-template <class Runner>
-class CancelableTaskSchedulerTest : public TestSupportingGC {
- public:
- using Task = CancelableTaskScheduler::Task;
-
- void ScheduleTask(Task callback) {
- scheduler_.ScheduleTask(std::move(callback));
- }
-
- void RunTaskRunner() { task_runner_->RunUntilIdle(); }
- size_t CancelAndWait() { return scheduler_.CancelAndWait(); }
-
- size_t NumberOfRegisteredTasks() const {
- return scheduler_.NumberOfTasksForTesting();
- }
-
- private:
- scoped_refptr<Runner> task_runner_ = base::MakeRefCounted<Runner>();
- CancelableTaskScheduler scheduler_{task_runner_};
-};
-
-using RunnerTypes =
- ::testing::Types<scheduler::FakeTaskRunner, ParallelTaskRunner>;
-TYPED_TEST_SUITE(CancelableTaskSchedulerTest, RunnerTypes);
-
-TYPED_TEST(CancelableTaskSchedulerTest, EmptyCancelTasks) {
- const size_t cancelled = this->CancelAndWait();
- EXPECT_EQ(0u, cancelled);
- EXPECT_EQ(0u, this->NumberOfRegisteredTasks());
-}
-
-TYPED_TEST(CancelableTaskSchedulerTest, RunAndCancelTasks) {
- static constexpr size_t kNumberOfTasks = 10u;
-
- const auto callback = [](std::atomic<int>* i) { ++(*i); };
- std::atomic<int> var{0};
-
- for (size_t i = 0; i < kNumberOfTasks; ++i) {
- this->ScheduleTask(
- WTF::CrossThreadBindOnce(callback, WTF::CrossThreadUnretained(&var)));
- EXPECT_GE(i + 1, this->NumberOfRegisteredTasks());
- }
-
- this->RunTaskRunner();
- // Tasks will remove themselves after running
- EXPECT_LE(0u, this->NumberOfRegisteredTasks());
-
- const size_t cancelled = this->CancelAndWait();
- EXPECT_EQ(0u, this->NumberOfRegisteredTasks());
- EXPECT_EQ(kNumberOfTasks, var + cancelled);
-}
-
-TEST(CancelableTaskSchedulerTest, RemoveTasksFromQueue) {
- auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
- CancelableTaskScheduler scheduler{task_runner};
- int var = 0;
- scheduler.ScheduleTask(WTF::CrossThreadBindOnce(
- [](int* var) { ++(*var); }, WTF::CrossThreadUnretained(&var)));
- auto tasks = task_runner->TakePendingTasksForTesting();
- // Clearing the task queue should destroy all cancelable closures, which in
- // turn will notify CancelableTaskScheduler to remove corresponding tasks.
- tasks.clear();
- EXPECT_EQ(0, var);
-}
-
-} // namespace blink
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
deleted file mode 100644
index a4620eadf0a..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/card_table_test.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// 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
index 31e7888b07f..2c0583f660c 100644
--- 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
@@ -241,50 +241,52 @@ struct TraceInCollectionTrait<kNoWeakHandling,
static void Trace(blink::Visitor* visitor,
const KeyValuePair<Key, Value>& self) {
- TraceImpl(visitor, self);
+ TraceImpl::Trace(visitor, self);
}
private:
- template <bool = EphemeronHelper::is_ephemeron>
- static void TraceImpl(blink::Visitor* visitor,
- const KeyValuePair<Key, Value>& self);
-
- // Strongification of ephemerons, i.e., Weak/Strong and Strong/Weak.
- template <>
- static void TraceImpl<true>(blink::Visitor* visitor,
- const KeyValuePair<Key, Value>& self) {
+ struct TraceImplEphemerons {
// Strongification of ephemerons, i.e., Weak/Strong and Strong/Weak.
- // The helper ensures that helper.key always refers to the weak part and
- // helper.value always refers to the dependent part.
- // We distinguish ephemeron from Weak/Weak and Strong/Strong to allow users
- // to override visitation behavior. An example is creating a heap snapshot,
- // where it is useful to annotate values as being kept alive from keys
- // rather than the table.
- EphemeronHelper helper(&self.key, &self.value);
- // Strongify the weak part.
- blink::TraceCollectionIfEnabled<
- kNoWeakHandling, typename EphemeronHelper::KeyType,
- typename EphemeronHelper::KeyTraits>::Trace(visitor, helper.key);
- // Strongify the dependent part.
- visitor->TraceEphemeron(
- *helper.key, helper.value,
- blink::TraceCollectionIfEnabled<
- kNoWeakHandling, typename EphemeronHelper::ValueType,
- typename EphemeronHelper::ValueTraits>::Trace);
- }
+ static void Trace(blink::Visitor* visitor,
+ const KeyValuePair<Key, Value>& self) {
+ // Strongification of ephemerons, i.e., Weak/Strong and Strong/Weak.
+ // The helper ensures that helper.key always refers to the weak part and
+ // helper.value always refers to the dependent part.
+ // We distinguish ephemeron from Weak/Weak and Strong/Strong to allow
+ // users to override visitation behavior. An example is creating a heap
+ // snapshot, where it is useful to annotate values as being kept alive
+ // from keys rather than the table.
+ EphemeronHelper helper(&self.key, &self.value);
+ // Strongify the weak part.
+ blink::TraceCollectionIfEnabled<
+ kNoWeakHandling, typename EphemeronHelper::KeyType,
+ typename EphemeronHelper::KeyTraits>::Trace(visitor, helper.key);
+ // Strongify the dependent part.
+ visitor->TraceEphemeron(
+ *helper.key, helper.value,
+ blink::TraceCollectionIfEnabled<
+ kNoWeakHandling, typename EphemeronHelper::ValueType,
+ typename EphemeronHelper::ValueTraits>::Trace);
+ }
+ };
- template <>
- static void TraceImpl<false>(blink::Visitor* visitor,
- const KeyValuePair<Key, Value>& self) {
- // Strongification of non-ephemeron KVP, i.e., 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);
- }
+ struct TraceImplDefault {
+ static void Trace(blink::Visitor* visitor,
+ const KeyValuePair<Key, Value>& self) {
+ // Strongification of non-ephemeron KVP, i.e., 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);
+ }
+ };
+
+ using TraceImpl = typename std::conditional<EphemeronHelper::is_ephemeron,
+ TraceImplEphemerons,
+ TraceImplDefault>::type;
};
template <typename Key, typename Value, typename Traits>
diff --git a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h
index c0b4e95350a..407d705b379 100644
--- a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h
+++ b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h
@@ -61,14 +61,14 @@ class HeapLinkedStack final : public GarbageCollected<HeapLinkedStack<T>> {
inline const T& Peek() const;
inline void Pop();
- void Trace(Visitor* visitor) { visitor->Trace(head_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(head_); }
private:
class Node final : public GarbageCollected<Node> {
public:
Node(const T&, Node*);
- void Trace(Visitor* visitor) {
+ void Trace(Visitor* visitor) const {
visitor->Trace(data_);
visitor->Trace(next_);
}
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
deleted file mode 100644
index 27b3f0dbf06..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack_test.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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
index 0c66678a444..1799dc517b6 100644
--- 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
@@ -5,7 +5,7 @@
#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 "base/check_op.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"
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
deleted file mode 100644
index 8491225978a..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc
+++ /dev/null
@@ -1,487 +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.
-
-#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"
-#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
-
-namespace blink {
-
-class ConcurrentMarkingTest : public TestSupportingGC {};
-
-namespace concurrent_marking_test {
-
-template <typename T>
-class CollectionWrapper : public GarbageCollected<CollectionWrapper<T>> {
- public:
- CollectionWrapper() : collection_(MakeGarbageCollected<T>()) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(collection_); }
-
- T* GetCollection() { return collection_.Get(); }
-
- private:
- Member<T> collection_;
-};
-
-// =============================================================================
-// Tests that expose data races when modifying collections =====================
-// =============================================================================
-
-template <typename C>
-void AddToCollection() {
- constexpr int kIterations = 10;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- Persistent<CollectionWrapper<C>> persistent =
- MakeGarbageCollected<CollectionWrapper<C>>();
- C* collection = persistent->GetCollection();
- driver.Start();
- for (int i = 0; i < kIterations; ++i) {
- driver.SingleConcurrentStep();
- for (int j = 0; j < kIterations; ++j) {
- int num = kIterations * i + j;
- collection->insert(MakeGarbageCollected<IntegerObject>(num));
- }
- }
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-template <typename C, typename GetLocation>
-void RemoveFromCollectionAtLocation(GetLocation location) {
- constexpr int kIterations = 10;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- 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 < kIterations; ++i) {
- driver.SingleConcurrentStep();
- for (int j = 0; j < kIterations; ++j) {
- collection->erase(location(collection));
- }
- }
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-template <typename C>
-void RemoveFromBeginningOfCollection() {
- RemoveFromCollectionAtLocation<C>(
- [](C* collection) { return collection->begin(); });
-}
-
-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;
- }
- return iterator;
- });
-}
-
-template <typename C>
-void RemoveFromEndOfCollection() {
- RemoveFromCollectionAtLocation<C>([](C* collection) {
- auto iterator = collection->end();
- return --iterator;
- });
-}
-
-template <typename C>
-void ClearCollection() {
- constexpr int kIterations = 10;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- Persistent<CollectionWrapper<C>> persistent =
- MakeGarbageCollected<CollectionWrapper<C>>();
- C* collection = persistent->GetCollection();
- driver.Start();
- for (int i = 0; i < kIterations; ++i) {
- driver.SingleConcurrentStep();
- for (int j = 0; j < kIterations; ++j) {
- collection->insert(MakeGarbageCollected<IntegerObject>(i));
- }
- collection->clear();
- }
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-template <typename C>
-void SwapCollections() {
- constexpr int kIterations = 10;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- Persistent<CollectionWrapper<C>> persistent =
- MakeGarbageCollected<CollectionWrapper<C>>();
- C* collection = persistent->GetCollection();
- driver.Start();
- 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();
- collection->swap(*new_collection);
- }
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-// 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);
- }
-};
-
-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>>>();
-}
-
-// 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 = 10;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- Persistent<CollectionWrapper<V>> persistent =
- MakeGarbageCollected<CollectionWrapper<V>>();
- V* vector = persistent->GetCollection();
- for (int i = 0; i < (kIterations * kIterations); ++i) {
- vector->insert(MakeGarbageCollected<IntegerObject>(i));
- }
- driver.Start();
- for (int i = 0; i < kIterations; ++i) {
- driver.SingleConcurrentStep();
- for (int j = 0; j < kIterations; ++j) {
- vector->pop_back();
- }
- }
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-#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);
- }
-};
-
-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, 10> {};
-
-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/disallow_new_wrapper.h b/chromium/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h
index 4b122d21ee5..1d3b1f3031f 100644
--- a/chromium/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h
+++ b/chromium/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h
@@ -30,7 +30,7 @@ class DisallowNewWrapper final
const T& Value() const { return value_; }
T&& TakeValue() { return std::move(value_); }
- void Trace(Visitor* visitor) { visitor->Trace(value_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(value_); }
private:
T value_;
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 01d34c23137..5c0efe5eef4 100644
--- a/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h
+++ b/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h
@@ -84,85 +84,17 @@ struct TraceDescriptor {
class PLATFORM_EXPORT GarbageCollectedMixin {
public:
typedef int IsGarbageCollectedMixinMarker;
- virtual void Trace(Visitor*) {}
- // Provide default implementations that indicate that the vtable is not yet
- // set up properly. This way it is possible to get infos about mixins so that
- // these objects can processed later on. This is necessary as
- // not-fully-constructed mixin objects potentially require being processed
- // as part emitting a write barrier for incremental marking. See
- // |IncrementalMarkingTest::WriteBarrierDuringMixinConstruction| as an
- // example.
- //
- // The not-fully-constructed objects are handled as follows:
- // 1. Write barrier or marking of not fully constructed mixin gets called.
- // 2. Default implementation of GetTraceDescriptor (and friends) returns
- // kNotFullyConstructedObject as object base payload.
- // 3. Visitor (e.g. MarkingVisitor) can intercept that value and delay
- // processing that object until the atomic pause.
- // 4. In the atomic phase, mark all not-fully-constructed objects that have
- // found in the step 1.-3. conservatively.
- //
- // In general, delaying is required as write barriers are omitted in certain
- // scenarios, e.g., initializing stores. As a result, we cannot depend on the
- // write barriers for catching writes to member fields and thus have to
- // process the object (instead of just marking only the header).
- virtual TraceDescriptor GetTraceDescriptor() const {
- return {BlinkGC::kNotFullyConstructedObject, nullptr};
- }
+ virtual void Trace(Visitor*) const {}
};
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
- public: \
- TraceDescriptor GetTraceDescriptor() const override { \
- static_assert( \
- WTF::IsSubclassOfTemplate<typename std::remove_const<TYPE>::type, \
- blink::GarbageCollected>::value, \
- "only garbage collected objects can have garbage collected mixins"); \
- return {const_cast<TYPE*>(static_cast<const TYPE*>(this)), \
- TraceTrait<TYPE>::Trace}; \
- } \
- \
- private:
-
// The Oilpan GC plugin checks for proper usages of the
// USING_GARBAGE_COLLECTED_MIXIN macro using a typedef marker.
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
- public: \
- typedef int HasUsingGarbageCollectedMixinMacro; \
- \
- private:
-
-// The USING_GARBAGE_COLLECTED_MIXIN macro defines all methods and markers
-// needed for handling mixins.
-#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
- DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
- DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
- IS_GARBAGE_COLLECTED_TYPE()
-
-// Merge two or more Mixins into one:
-//
-// class A : public GarbageCollectedMixin {};
-// class B : public GarbageCollectedMixin {};
-// class C : public A, public B {
-// // C::GetTraceDescriptor is now ambiguous because there are two
-// // candidates: A::GetTraceDescriptor and B::GetTraceDescriptor. Ditto for
-// // other functions.
-//
-// MERGE_GARBAGE_COLLECTED_MIXINS();
-// // The macro defines C::GetTraceDescriptor, similar to
-// GarbageCollectedMixin,
-// // so that they are no longer ambiguous.
-// // USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them later and provides
-// // the implementations.
-// };
-#define MERGE_GARBAGE_COLLECTED_MIXINS() \
- public: \
- TraceDescriptor GetTraceDescriptor() const override { \
- return {BlinkGC::kNotFullyConstructedObject, nullptr}; \
- } \
- \
- private: \
- using merge_garbage_collected_mixins_requires_semicolon = void
+#define USING_GARBAGE_COLLECTED_MIXIN(TYPE) \
+ public: \
+ typedef int HasUsingGarbageCollectedMixinMacro; \
+ \
+ private: \
+ friend class ::WTF::internal::__thisIsHereToForceASemicolonAfterThisMacro
// Base class for objects allocated in the Blink garbage-collected heap.
//
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
deleted file mode 100644
index 0a4e667c914..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc
+++ /dev/null
@@ -1,45 +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/heap/gc_info.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-TEST(GCInfoTest, InitialEmpty) {
- GCInfoTable table;
- EXPECT_EQ(GCInfoTable::kMinIndex, table.NumberOfGCInfos());
-}
-
-TEST(GCInfoTest, ResizeToMaxIndex) {
- GCInfoTable table;
- GCInfo info = {nullptr, nullptr, nullptr, false};
- std::atomic<GCInfoIndex> slot{0};
- for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex;
- i++) {
- slot = 0;
- GCInfoIndex index = table.EnsureGCInfoIndex(&info, &slot);
- EXPECT_EQ(index, slot);
- EXPECT_LT(0u, 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 9b253153a9e..d23b857d4f2 100644
--- a/chromium/third_party/blink/renderer/platform/heap/heap.cc
+++ b/chromium/third_party/blink/renderer/platform/heap/heap.cc
@@ -151,7 +151,7 @@ void ThreadHeap::VisitRememberedSets(MarkingVisitor* visitor) {
// points or by reintroducing nested allocation scopes that avoid
// finalization.
DCHECK(header->IsMarked());
- DCHECK(!MarkingVisitor::IsInConstruction(header));
+ DCHECK(!header->IsInConstruction());
const GCInfo& gc_info = GCInfo::From(header->GcInfoIndex());
gc_info.trace(visitor, header->Payload());
}
@@ -165,33 +165,35 @@ void ThreadHeap::VisitRememberedSets(MarkingVisitor* visitor) {
}
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());
- weak_table_worklist_.reset(new WeakTableWorklist);
- v8_references_worklist_.reset(new V8ReferencesWorklist());
- not_safe_to_concurrently_trace_worklist_.reset(
- new NotSafeToConcurrentlyTraceWorklist());
- DCHECK(ephemeron_callbacks_.IsEmpty());
+ marking_worklist_ = std::make_unique<MarkingWorklist>();
+ write_barrier_worklist_ = std::make_unique<WriteBarrierWorklist>();
+ not_fully_constructed_worklist_ =
+ std::make_unique<NotFullyConstructedWorklist>();
+ previously_not_fully_constructed_worklist_ =
+ std::make_unique<NotFullyConstructedWorklist>();
+ weak_callback_worklist_ = std::make_unique<WeakCallbackWorklist>();
+ discovered_ephemeron_pairs_worklist_ =
+ std::make_unique<EphemeronPairsWorklist>();
+ ephemeron_pairs_to_process_worklist_ =
+ std::make_unique<EphemeronPairsWorklist>();
+ v8_references_worklist_ = std::make_unique<V8ReferencesWorklist>();
+ not_safe_to_concurrently_trace_worklist_ =
+ std::make_unique<NotSafeToConcurrentlyTraceWorklist>();
if (should_initialize_compaction_worklists) {
- movable_reference_worklist_.reset(new MovableReferenceWorklist());
- backing_store_callback_worklist_.reset(new BackingStoreCallbackWorklist());
+ movable_reference_worklist_ = std::make_unique<MovableReferenceWorklist>();
+ backing_store_callback_worklist_ =
+ std::make_unique<BackingStoreCallbackWorklist>();
}
}
void ThreadHeap::DestroyMarkingWorklists(BlinkGC::StackState stack_state) {
- marking_worklist_.reset(nullptr);
- write_barrier_worklist_.reset(nullptr);
- previously_not_fully_constructed_worklist_.reset(nullptr);
- weak_callback_worklist_.reset(nullptr);
- weak_table_worklist_.reset();
+ marking_worklist_.reset();
+ write_barrier_worklist_.reset();
+ previously_not_fully_constructed_worklist_.reset();
+ weak_callback_worklist_.reset();
+ ephemeron_pairs_to_process_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.
// Such objects should have already been found through the stack scan though
// and should thus already be marked.
@@ -218,7 +220,24 @@ void ThreadHeap::DestroyMarkingWorklists(BlinkGC::StackState stack_state) {
not_fully_constructed_worklist_->Clear();
#endif
}
- not_fully_constructed_worklist_.reset(nullptr);
+ not_fully_constructed_worklist_.reset();
+
+ // |discovered_ephemeron_pairs_worklist_| may still hold ephemeron pairs with
+ // dead keys.
+ if (!discovered_ephemeron_pairs_worklist_->IsGlobalEmpty()) {
+#if DCHECK_IS_ON()
+ EphemeronPairItem item;
+ while (discovered_ephemeron_pairs_worklist_->Pop(
+ WorklistTaskId::MutatorThread, &item)) {
+ const HeapObjectHeader* const header = HeapObjectHeader::FromInnerAddress(
+ reinterpret_cast<ConstAddress>(item.key));
+ DCHECK(!header->IsMarked());
+ }
+#else
+ discovered_ephemeron_pairs_worklist_->Clear();
+#endif
+ }
+ discovered_ephemeron_pairs_worklist_.reset();
}
void ThreadHeap::DestroyCompactionWorklists() {
@@ -247,6 +266,16 @@ void ThreadHeap::FlushNotFullyConstructedObjects() {
DCHECK(view.IsLocalViewEmpty());
}
+void ThreadHeap::FlushEphemeronPairs() {
+ EphemeronPairsWorklist::View view(discovered_ephemeron_pairs_worklist_.get(),
+ WorklistTaskId::MutatorThread);
+ if (!view.IsLocalViewEmpty()) {
+ view.FlushToGlobal();
+ ephemeron_pairs_to_process_worklist_->MergeGlobalPool(
+ discovered_ephemeron_pairs_worklist_.get());
+ }
+}
+
void ThreadHeap::MarkNotFullyConstructedObjects(MarkingVisitor* visitor) {
DCHECK(!thread_state_->IsIncrementalMarking());
ThreadHeapStatsCollector::Scope stats_scope(
@@ -290,39 +319,23 @@ bool DrainWorklistWithDeadline(base::TimeTicks deadline,
bool ThreadHeap::InvokeEphemeronCallbacks(MarkingVisitor* visitor,
base::TimeTicks deadline) {
+ FlushEphemeronPairs();
+
// 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);
- }
+ deadline, ephemeron_pairs_to_process_worklist_.get(),
+ [visitor](EphemeronPairItem& item) {
+ visitor->VisitEphemeron(item.key, item.value,
+ item.value_trace_callback);
},
WorklistTaskId::MutatorThread);
}
@@ -343,19 +356,6 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor,
// 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);
@@ -373,12 +373,25 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor,
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](NotFullyConstructedItem& item) {
+ visitor->DynamicallyMarkAddress(
+ reinterpret_cast<ConstAddress>(item));
+ },
+ WorklistTaskId::MutatorThread);
+ if (!finished)
+ break;
+
finished = DrainWorklistWithDeadline(
deadline, marking_worklist_.get(),
[visitor](const MarkingItem& item) {
HeapObjectHeader* header =
HeapObjectHeader::FromPayload(item.base_object_payload);
- DCHECK(!MarkingVisitor::IsInConstruction(header));
+ DCHECK(!header->IsInConstruction());
item.callback(visitor, item.base_object_payload);
visitor->AccountMarkedBytes(header);
},
@@ -389,7 +402,7 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor,
finished = DrainWorklistWithDeadline(
deadline, write_barrier_worklist_.get(),
[visitor](HeapObjectHeader* header) {
- DCHECK(!MarkingVisitor::IsInConstruction(header));
+ DCHECK(!header->IsInConstruction());
GCInfo::From(header->GcInfoIndex())
.trace(visitor, header->Payload());
visitor->AccountMarkedBytes(header);
@@ -411,13 +424,26 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor,
bool ThreadHeap::HasWorkForConcurrentMarking() const {
return !marking_worklist_->IsGlobalPoolEmpty() ||
- !write_barrier_worklist_->IsGlobalPoolEmpty();
+ !write_barrier_worklist_->IsGlobalPoolEmpty() ||
+ !previously_not_fully_constructed_worklist_->IsGlobalPoolEmpty();
}
bool ThreadHeap::AdvanceConcurrentMarking(ConcurrentMarkingVisitor* visitor,
base::TimeTicks deadline) {
bool finished;
do {
+ // 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));
+ },
+ visitor->task_id());
+ if (!finished)
+ break;
+
// Iteratively mark all objects that are reachable from the objects
// currently pushed onto the marking worklist.
finished = DrainWorklistWithDeadline(
@@ -426,9 +452,11 @@ bool ThreadHeap::AdvanceConcurrentMarking(ConcurrentMarkingVisitor* visitor,
HeapObjectHeader* header =
HeapObjectHeader::FromPayload(item.base_object_payload);
PageFromObject(header)->SynchronizedLoad();
- DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header));
+ DCHECK(
+ !header
+ ->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>());
item.callback(visitor, item.base_object_payload);
- visitor->AccountMarkedBytesSafe(header);
+ visitor->AccountMarkedBytes(header);
},
visitor->task_id());
if (!finished)
@@ -438,9 +466,11 @@ bool ThreadHeap::AdvanceConcurrentMarking(ConcurrentMarkingVisitor* visitor,
deadline, write_barrier_worklist_.get(),
[visitor](HeapObjectHeader* header) {
PageFromObject(header)->SynchronizedLoad();
- DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header));
+ DCHECK(
+ !header
+ ->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>());
GCInfo::From(header->GcInfoIndex()).trace(visitor, header->Payload());
- visitor->AccountMarkedBytesSafe(header);
+ visitor->AccountMarkedBytes(header);
},
visitor->task_id());
if (!finished)
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap.h b/chromium/third_party/blink/renderer/platform/heap/heap.h
index 18dc3460722..7db3bc367f9 100644
--- a/chromium/third_party/blink/renderer/platform/heap/heap.h
+++ b/chromium/third_party/blink/renderer/platform/heap/heap.h
@@ -56,10 +56,6 @@ namespace incremental_marking_test {
class IncrementalMarkingScopeBase;
} // namespace incremental_marking_test
-namespace weakness_marking_test {
-class EphemeronCallbacksCounter;
-} // namespace weakness_marking_test
-
class ConcurrentMarkingVisitor;
class ThreadHeapStatsCollector;
class PageBloomFilter;
@@ -69,7 +65,12 @@ class RegionTree;
using MarkingItem = TraceDescriptor;
using NotFullyConstructedItem = const void*;
-using WeakTableItem = MarkingItem;
+
+struct EphemeronPairItem {
+ const void* key;
+ const void* value;
+ TraceCallback value_trace_callback;
+};
struct BackingStoreCallbackItem {
const void* backing;
@@ -95,7 +96,8 @@ using WeakCallbackWorklist =
// regressions.
using MovableReferenceWorklist =
Worklist<const MovableReference*, 256 /* local entries */>;
-using WeakTableWorklist = Worklist<WeakTableItem, 16 /* local entries */>;
+using EphemeronPairsWorklist =
+ Worklist<EphemeronPairItem, 64 /* local entries */>;
using BackingStoreCallbackWorklist =
Worklist<BackingStoreCallbackItem, 16 /* local entries */>;
using V8ReferencesWorklist = Worklist<V8Reference, 16 /* local entries */>;
@@ -175,12 +177,9 @@ class ObjectAliveTrait<T, true> {
NO_SANITIZE_ADDRESS
static bool IsHeapObjectAlive(const T* object) {
static_assert(sizeof(T), "T must be fully defined");
- const HeapObjectHeader* header = HeapObjectHeader::FromTraceDescriptor(
- TraceTrait<T>::GetTraceDescriptor(object));
- if (header == BlinkGC::kNotFullyConstructedObject) {
- // Objects under construction are always alive.
- return true;
- }
+ const HeapObjectHeader* header = HeapObjectHeader::FromPayload(
+ TraceTrait<T>::GetTraceDescriptor(object).base_object_payload);
+ DCHECK(!header->IsInConstruction() || header->IsMarked());
return header->IsMarked();
}
};
@@ -214,6 +213,11 @@ class PLATFORM_EXPORT ThreadHeap {
return not_fully_constructed_worklist_.get();
}
+ NotFullyConstructedWorklist* GetPreviouslyNotFullyConstructedWorklist()
+ const {
+ return previously_not_fully_constructed_worklist_.get();
+ }
+
WeakCallbackWorklist* GetWeakCallbackWorklist() const {
return weak_callback_worklist_.get();
}
@@ -222,8 +226,12 @@ class PLATFORM_EXPORT ThreadHeap {
return movable_reference_worklist_.get();
}
- WeakTableWorklist* GetWeakTableWorklist() const {
- return weak_table_worklist_.get();
+ EphemeronPairsWorklist* GetDiscoveredEphemeronPairsWorklist() const {
+ return discovered_ephemeron_pairs_worklist_.get();
+ }
+
+ EphemeronPairsWorklist* GetEphemeronPairsToProcessWorklist() const {
+ return ephemeron_pairs_to_process_worklist_.get();
}
BackingStoreCallbackWorklist* GetBackingStoreCallbackWorklist() const {
@@ -274,6 +282,10 @@ class PLATFORM_EXPORT ThreadHeap {
// not need to rely on conservative handling.
void FlushNotFullyConstructedObjects();
+ // Moves ephemeron pairs from |discovered_ephemeron_pairs_worklist_| to
+ // |ephemeron_pairs_to_process_worklist_|
+ void FlushEphemeronPairs();
+
// Marks not fully constructed objects.
void MarkNotFullyConstructedObjects(MarkingVisitor*);
// Marks the transitive closure including ephemerons.
@@ -421,7 +433,8 @@ class PLATFORM_EXPORT ThreadHeap {
// Worklist of ephemeron callbacks. Used to pass new callbacks from
// MarkingVisitor to ThreadHeap.
- std::unique_ptr<WeakTableWorklist> weak_table_worklist_;
+ std::unique_ptr<EphemeronPairsWorklist> discovered_ephemeron_pairs_worklist_;
+ std::unique_ptr<EphemeronPairsWorklist> ephemeron_pairs_to_process_worklist_;
// This worklist is used to passing backing store callback to HeapCompact.
std::unique_ptr<BackingStoreCallbackWorklist>
@@ -434,10 +447,6 @@ class PLATFORM_EXPORT ThreadHeap {
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<const void*, EphemeronCallback> ephemeron_callbacks_;
-
std::unique_ptr<HeapCompact> compaction_;
LastAllocatedRegion last_allocated_region_;
@@ -450,7 +459,6 @@ class PLATFORM_EXPORT ThreadHeap {
template <typename T>
friend class Member;
friend class ThreadState;
- friend class weakness_marking_test::EphemeronCallbacksCounter;
};
template <typename T>
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
deleted file mode 100644
index f87e00b143f..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc
+++ /dev/null
@@ -1,502 +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/heap/heap_compact.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/heap/handle.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/wtf/deque.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-#include <memory>
-
-namespace {
-
-enum VerifyArenaCompaction {
- NoVerify,
- VectorsAreCompacted,
- HashTablesAreCompacted,
-};
-
-class IntWrapper : public blink::GarbageCollected<IntWrapper> {
- public:
- static bool did_verify_at_least_once;
-
- static IntWrapper* Create(int x, VerifyArenaCompaction verify = NoVerify) {
- did_verify_at_least_once = false;
- return blink::MakeGarbageCollected<IntWrapper>(x, verify);
- }
-
- virtual ~IntWrapper() = default;
-
- void Trace(blink::Visitor* visitor) {
- // Verify if compaction is indeed activated.
-
- // There may be multiple passes over objects during a GC, even after
- // compaction is finished. Filter out that cases here.
- if (!visitor->Heap().Compaction()->IsCompacting())
- return;
-
- did_verify_at_least_once = true;
- // What arenas end up being compacted is dependent on residency,
- // so approximate the arena checks to fit.
- blink::HeapCompact* compaction = visitor->Heap().Compaction();
- switch (verify_) {
- case NoVerify:
- return;
- case HashTablesAreCompacted:
- CHECK(compaction->IsCompactingArena(
- blink::BlinkGC::kHashTableArenaIndex));
- return;
- case VectorsAreCompacted:
- CHECK(compaction->IsCompactingVectorArenasForTesting());
- return;
- }
- }
-
- int Value() const { return x_; }
-
- bool operator==(const IntWrapper& other) const {
- return other.Value() == Value();
- }
-
- unsigned GetHash() { return IntHash<int>::GetHash(x_); }
-
- IntWrapper(int x, VerifyArenaCompaction verify) : x_(x), verify_(verify) {}
-
- private:
- IntWrapper() = delete;
-
- int x_;
- VerifyArenaCompaction verify_;
-};
-
-bool IntWrapper::did_verify_at_least_once = false;
-
-static_assert(WTF::IsTraceable<IntWrapper>::value,
- "IsTraceable<> template failed to recognize trace method.");
-
-} // namespace
-
-using IntVector = blink::HeapVector<blink::Member<IntWrapper>>;
-using IntDeque = blink::HeapDeque<blink::Member<IntWrapper>>;
-using IntMap = blink::HeapHashMap<blink::Member<IntWrapper>, int>;
-// TODO(sof): decide if this ought to be a global trait specialization.
-// (i.e., for HeapHash*<T>.)
-WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(IntMap)
-
-namespace blink {
-
-class HeapCompactTest : public TestSupportingGC {
- public:
- void PerformHeapCompaction() {
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- PreciselyCollectGarbage();
- }
-};
-
-TEST_F(HeapCompactTest, CompactVector) {
- ClearOutOldGarbage();
-
- IntWrapper* val = IntWrapper::Create(1, VectorsAreCompacted);
- Persistent<IntVector> vector = MakeGarbageCollected<IntVector>(10, val);
- EXPECT_EQ(10u, vector->size());
-
- for (IntWrapper* item : *vector)
- EXPECT_EQ(val, item);
-
- PerformHeapCompaction();
-
- for (IntWrapper* item : *vector)
- EXPECT_EQ(val, item);
-}
-
-TEST_F(HeapCompactTest, CompactHashMap) {
- ClearOutOldGarbage();
-
- Persistent<IntMap> int_map = MakeGarbageCollected<IntMap>();
- for (wtf_size_t i = 0; i < 100; ++i) {
- IntWrapper* val = IntWrapper::Create(i, HashTablesAreCompacted);
- int_map->insert(val, 100 - i);
- }
-
- EXPECT_EQ(100u, int_map->size());
- for (auto k : *int_map)
- EXPECT_EQ(k.key->Value(), 100 - k.value);
-
- PerformHeapCompaction();
- EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
- for (auto k : *int_map)
- EXPECT_EQ(k.key->Value(), 100 - k.value);
-}
-
-TEST_F(HeapCompactTest, CompactVectorPartHashMap) {
- ClearOutOldGarbage();
-
- using IntMapVector = HeapVector<IntMap>;
-
- Persistent<IntMapVector> int_map_vector =
- MakeGarbageCollected<IntMapVector>();
- for (size_t i = 0; i < 10; ++i) {
- IntMap map;
- for (wtf_size_t j = 0; j < 10; ++j) {
- IntWrapper* val = IntWrapper::Create(j, VectorsAreCompacted);
- map.insert(val, 10 - j);
- }
- int_map_vector->push_back(map);
- }
-
- EXPECT_EQ(10u, int_map_vector->size());
- for (auto map : *int_map_vector) {
- EXPECT_EQ(10u, map.size());
- for (auto k : map) {
- EXPECT_EQ(k.key->Value(), 10 - k.value);
- }
- }
-
- PerformHeapCompaction();
- EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
- EXPECT_EQ(10u, int_map_vector->size());
- for (auto map : *int_map_vector) {
- EXPECT_EQ(10u, map.size());
- for (auto k : map) {
- EXPECT_EQ(k.key->Value(), 10 - k.value);
- }
- }
-}
-
-TEST_F(HeapCompactTest, CompactHashPartVector) {
- ClearOutOldGarbage();
-
- using IntVectorMap = HeapHashMap<int, IntVector>;
-
- Persistent<IntVectorMap> int_vector_map =
- MakeGarbageCollected<IntVectorMap>();
- for (wtf_size_t i = 0; i < 10; ++i) {
- IntVector vector;
- for (wtf_size_t j = 0; j < 10; ++j) {
- vector.push_back(IntWrapper::Create(j, HashTablesAreCompacted));
- }
- int_vector_map->insert(1 + i, vector);
- }
-
- EXPECT_EQ(10u, int_vector_map->size());
- for (const IntVector& int_vector : int_vector_map->Values()) {
- EXPECT_EQ(10u, int_vector.size());
- for (wtf_size_t i = 0; i < int_vector.size(); ++i) {
- EXPECT_EQ(static_cast<int>(i), int_vector[i]->Value());
- }
- }
-
- PerformHeapCompaction();
- EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
- EXPECT_EQ(10u, int_vector_map->size());
- for (const IntVector& int_vector : int_vector_map->Values()) {
- EXPECT_EQ(10u, int_vector.size());
- for (wtf_size_t i = 0; i < int_vector.size(); ++i) {
- EXPECT_EQ(static_cast<int>(i), int_vector[i]->Value());
- }
- }
-}
-
-TEST_F(HeapCompactTest, CompactDeques) {
- Persistent<IntDeque> deque = MakeGarbageCollected<IntDeque>();
- for (int i = 0; i < 8; ++i) {
- deque->push_front(IntWrapper::Create(i, VectorsAreCompacted));
- }
- 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)->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)->Value());
-}
-
-TEST_F(HeapCompactTest, CompactLinkedHashSet) {
- using OrderedHashSet = HeapLinkedHashSet<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++;
- }
-
- PerformHeapCompaction();
- EXPECT_TRUE(IntWrapper::did_verify_at_least_once);
-
- expected = 0;
- for (IntWrapper* v : *set) {
- EXPECT_EQ(expected, v->Value());
- expected++;
- }
-}
-
-TEST_F(HeapCompactTest, CompactLinkedHashSetVector) {
- using OrderedHashSet = HeapLinkedHashSet<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, CompactLinkedHashSetMap) {
- using Inner = HeapHashSet<Member<IntWrapper>>;
- using OrderedHashSet = HeapLinkedHashSet<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, CompactLinkedHashSetNested) {
- using Inner = HeapLinkedHashSet<Member<IntWrapper>>;
- using OrderedHashSet = HeapLinkedHashSet<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, 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
- //
- // This test checks that compaction properly updates pointers to statically
- // allocated inline backings, see e.g. Vector::inline_buffer_.
-
- // Use a Key with pre-defined hash traits.
- using Key = Member<IntWrapper>;
- // Value uses a statically allocated inline backing of size 64. As long as no
- // more than elements are added no out-of-line allocation is triggered.
- // The internal forwarding pointer to the inlined storage needs to be handled
- // by compaction.
- using Value = HeapVector<Member<IntWrapper>, 64>;
- using MapWithInlinedBacking = HeapHashMap<Key, Value>;
-
- Persistent<MapWithInlinedBacking> map =
- MakeGarbageCollected<MapWithInlinedBacking>();
- {
- // Create a map that is reclaimed during compaction.
- (MakeGarbageCollected<MapWithInlinedBacking>())
- ->insert(IntWrapper::Create(1, HashTablesAreCompacted), Value());
-
- IntWrapper* wrapper = IntWrapper::Create(1, HashTablesAreCompacted);
- Value storage;
- storage.push_front(wrapper);
- map->insert(wrapper, std::move(storage));
- }
- PerformHeapCompaction();
- // The first GC should update the pointer accordingly and thus not crash on
- // the second GC.
- PerformHeapCompaction();
-}
-
-} // namespace blink
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 672a3640e9b..b2929c1ce10 100644
--- a/chromium/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -434,7 +434,7 @@ void NormalPageArena::AddToFreeList(Address address, size_t size) {
free_list_.Add(address, size);
static_cast<NormalPage*>(PageFromObject(address))
->object_start_bit_map()
- ->SetBit(address);
+ ->SetBit<HeapObjectHeader::AccessMode::kAtomic>(address);
}
void NormalPageArena::MakeConsistentForGC() {
@@ -715,6 +715,9 @@ void NormalPageArena::FreePage(NormalPage* page) {
GetThreadState()->Heap().GetFreePagePool()->Add(ArenaIndex(), memory);
}
+PlatformAwareObjectStartBitmap::PlatformAwareObjectStartBitmap(Address offset)
+ : ObjectStartBitmap(offset) {}
+
ObjectStartBitmap::ObjectStartBitmap(Address offset) : offset_(offset) {
Clear();
}
@@ -724,6 +727,7 @@ void ObjectStartBitmap::Clear() {
}
void NormalPageArena::PromptlyFreeObject(HeapObjectHeader* header) {
+ DCHECK(!GetThreadState()->IsMarkingInProgress());
DCHECK(!GetThreadState()->SweepForbidden());
Address address = reinterpret_cast<Address>(header);
Address payload = header->Payload();
@@ -897,7 +901,8 @@ void NormalPageArena::SetAllocationPoint(Address point, size_t size) {
// because the area can grow or shrink. Will be added back before a GC when
// clearing the allocation point.
NormalPage* page = reinterpret_cast<NormalPage*>(PageFromObject(point));
- page->object_start_bit_map()->ClearBit(point);
+ page->object_start_bit_map()
+ ->ClearBit<HeapObjectHeader::AccessMode::kAtomic>(point);
// Mark page as containing young objects.
page->SetAsYoung(true);
}
@@ -1465,9 +1470,10 @@ void NormalPage::MergeFreeLists() {
}
bool NormalPage::Sweep(FinalizeType finalize_type) {
- ObjectStartBitmap* bitmap;
+ PlatformAwareObjectStartBitmap* bitmap;
#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION)
- cached_object_start_bit_map_ = std::make_unique<ObjectStartBitmap>(Payload());
+ cached_object_start_bit_map_ =
+ std::make_unique<PlatformAwareObjectStartBitmap>(Payload());
bitmap = cached_object_start_bit_map_.get();
#else
object_start_bit_map()->Clear();
@@ -1761,29 +1767,6 @@ void LargeObjectPage::VerifyMarking() {
verifier.VerifyObject(ObjectHeader());
}
-Address ObjectStartBitmap::FindHeader(
- 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;
- size_t cell_index = object_start_number / kCellSize;
-#if DCHECK_IS_ON()
- const size_t bitmap_size = kReservedForBitmap;
- DCHECK_LT(cell_index, bitmap_size);
-#endif
- size_t bit = object_start_number & kCellMask;
- uint8_t byte = object_start_bit_map_[cell_index] & ((1 << (bit + 1)) - 1);
- while (!byte) {
- DCHECK_LT(0u, cell_index);
- byte = object_start_bit_map_[--cell_index];
- }
- int leading_zeroes = base::bits::CountLeadingZeroBits(byte);
- object_start_number =
- (cell_index * kCellSize) + (kCellSize - 1) - leading_zeroes;
- object_offset = object_start_number * kAllocationGranularity;
- return object_offset + offset_;
-}
-
HeapObjectHeader* NormalPage::ConservativelyFindHeaderFromAddress(
ConstAddress address) const {
if (!ContainedInObjectPayload(address))
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 ba1417c96ef..7338ac72c1d 100644
--- a/chromium/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.h
@@ -205,7 +205,6 @@ class PLATFORM_EXPORT HeapObjectHeader {
enum class AccessMode : uint8_t { kNonAtomic, kAtomic };
static HeapObjectHeader* FromPayload(const void*);
- static inline HeapObjectHeader* FromTraceDescriptor(const TraceDescriptor&);
template <AccessMode = AccessMode::kNonAtomic>
static HeapObjectHeader* FromInnerAddress(const void*);
@@ -608,11 +607,19 @@ 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.
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
Address FindHeader(
ConstAddress address_maybe_pointing_to_the_middle_of_object) const;
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
inline void SetBit(Address);
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
inline void ClearBit(Address);
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
inline bool CheckBit(Address) const;
// Iterates all object starts recorded in the bitmap.
@@ -627,6 +634,13 @@ class PLATFORM_EXPORT ObjectStartBitmap {
void Clear();
private:
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
+ void store(size_t cell_index, uint8_t value);
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
+ uint8_t load(size_t cell_index) const;
+
static const size_t kCellSize = sizeof(uint8_t) * 8;
static const size_t kCellMask = sizeof(uint8_t) * 8 - 1;
static const size_t kBitmapSize =
@@ -643,6 +657,27 @@ class PLATFORM_EXPORT ObjectStartBitmap {
uint8_t object_start_bit_map_[kReservedForBitmap];
};
+// A platform aware version of ObjectStartBitmap to provide platform specific
+// optimizations (e.g. Use non-atomic stores on ARMv7 when not marking).
+class PLATFORM_EXPORT PlatformAwareObjectStartBitmap
+ : public ObjectStartBitmap {
+ USING_FAST_MALLOC(PlatformAwareObjectStartBitmap);
+
+ public:
+ explicit PlatformAwareObjectStartBitmap(Address offset);
+
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
+ inline void SetBit(Address);
+ template <
+ HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic>
+ inline void ClearBit(Address);
+
+ private:
+ template <HeapObjectHeader::AccessMode>
+ static bool ShouldForceNonAtomic();
+};
+
class PLATFORM_EXPORT NormalPage final : public BasePage {
public:
NormalPage(PageMemory*, BaseArena*);
@@ -710,8 +745,10 @@ class PLATFORM_EXPORT NormalPage final : public BasePage {
void SweepAndCompact(CompactionContext&);
// Object start bitmap of this page.
- ObjectStartBitmap* object_start_bit_map() { return &object_start_bit_map_; }
- const ObjectStartBitmap* object_start_bit_map() const {
+ PlatformAwareObjectStartBitmap* object_start_bit_map() {
+ return &object_start_bit_map_;
+ }
+ const PlatformAwareObjectStartBitmap* object_start_bit_map() const {
return &object_start_bit_map_;
}
@@ -722,6 +759,8 @@ 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.
+ // This method is called only during stack scanning when there are no
+ // concurrent markers, thus no atomics required.
HeapObjectHeader* ConservativelyFindHeaderFromAddress(ConstAddress) const;
// Uses the object_start_bit_map_ to find an object for a given address. It is
@@ -822,9 +861,9 @@ class PLATFORM_EXPORT NormalPage final : public BasePage {
bool found_finalizer);
CardTable card_table_;
- ObjectStartBitmap object_start_bit_map_;
+ PlatformAwareObjectStartBitmap object_start_bit_map_;
#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION)
- std::unique_ptr<ObjectStartBitmap> cached_object_start_bit_map_;
+ std::unique_ptr<PlatformAwareObjectStartBitmap> cached_object_start_bit_map_;
#endif
Vector<ToBeFinalizedObject> to_be_finalized_objects_;
FreeList cached_freelist_;
@@ -1146,16 +1185,6 @@ inline HeapObjectHeader* HeapObjectHeader::FromPayload(const void* payload) {
return header;
}
-// static
-HeapObjectHeader* HeapObjectHeader::FromTraceDescriptor(
- const TraceDescriptor& desc) {
- static_assert(!BlinkGC::kNotFullyConstructedObject,
- "Expecting kNotFullyConstructedObject == nullptr");
- return desc.base_object_payload
- ? HeapObjectHeader::FromPayload(desc.base_object_payload)
- : nullptr;
-}
-
template <HeapObjectHeader::AccessMode mode>
inline HeapObjectHeader* HeapObjectHeader::FromInnerAddress(
const void* address) {
@@ -1283,7 +1312,7 @@ inline Address NormalPageArena::AllocateObject(size_t allocation_size,
DCHECK(!PageFromObject(header_address)->IsLargeObjectPage());
static_cast<NormalPage*>(PageFromObject(header_address))
->object_start_bit_map()
- ->SetBit(header_address);
+ ->SetBit<HeapObjectHeader::AccessMode::kAtomic>(header_address);
Address result = header_address + sizeof(HeapObjectHeader);
DCHECK(!(reinterpret_cast<uintptr_t>(result) & kAllocationMask));
@@ -1323,22 +1352,81 @@ inline void LargeObjectArena::IterateAndClearRememberedPages(
}
}
+// static
+template <HeapObjectHeader::AccessMode mode>
+bool PlatformAwareObjectStartBitmap::ShouldForceNonAtomic() {
+#if defined(ARCH_CPU_ARMEL)
+ // Use non-atomic accesses on ARMv7 when marking is not active.
+ if (mode == HeapObjectHeader::AccessMode::kAtomic) {
+ if (LIKELY(!ThreadState::Current()->IsAnyIncrementalMarking()))
+ return true;
+ }
+#endif // defined(ARCH_CPU_ARMEL)
+ return false;
+}
+
+template <HeapObjectHeader::AccessMode mode>
+inline void PlatformAwareObjectStartBitmap::SetBit(Address header_address) {
+ if (ShouldForceNonAtomic<mode>()) {
+ ObjectStartBitmap::SetBit<HeapObjectHeader::AccessMode::kNonAtomic>(
+ header_address);
+ return;
+ }
+ ObjectStartBitmap::SetBit<mode>(header_address);
+}
+
+template <HeapObjectHeader::AccessMode mode>
+inline void PlatformAwareObjectStartBitmap::ClearBit(Address header_address) {
+ if (ShouldForceNonAtomic<mode>()) {
+ ObjectStartBitmap::ClearBit<HeapObjectHeader::AccessMode::kNonAtomic>(
+ header_address);
+ return;
+ }
+ ObjectStartBitmap::ClearBit<mode>(header_address);
+}
+
+template <HeapObjectHeader::AccessMode mode>
+inline void ObjectStartBitmap::store(size_t cell_index, uint8_t value) {
+ if (mode == HeapObjectHeader::AccessMode::kNonAtomic) {
+ object_start_bit_map_[cell_index] = value;
+ return;
+ }
+ WTF::AsAtomicPtr(&object_start_bit_map_[cell_index])
+ ->store(value, std::memory_order_release);
+}
+
+template <HeapObjectHeader::AccessMode mode>
+inline uint8_t ObjectStartBitmap::load(size_t cell_index) const {
+ if (mode == HeapObjectHeader::AccessMode::kNonAtomic) {
+ return object_start_bit_map_[cell_index];
+ }
+ return WTF::AsAtomicPtr(&object_start_bit_map_[cell_index])
+ ->load(std::memory_order_acquire);
+}
+
+template <HeapObjectHeader::AccessMode mode>
inline void ObjectStartBitmap::SetBit(Address header_address) {
size_t cell_index, object_bit;
ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
- object_start_bit_map_[cell_index] |= (1 << object_bit);
+ // Only the mutator thread writes to the bitmap during concurrent marking,
+ // so no need for CAS here.
+ store<mode>(cell_index,
+ static_cast<uint8_t>(load(cell_index) | (1 << object_bit)));
}
+template <HeapObjectHeader::AccessMode mode>
inline void ObjectStartBitmap::ClearBit(Address header_address) {
size_t cell_index, object_bit;
ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
- object_start_bit_map_[cell_index] &= ~(1 << object_bit);
+ store<mode>(cell_index,
+ static_cast<uint8_t>(load(cell_index) & ~(1 << object_bit)));
}
+template <HeapObjectHeader::AccessMode mode>
inline bool ObjectStartBitmap::CheckBit(Address header_address) const {
size_t cell_index, object_bit;
ObjectStartIndexAndBit(header_address, &cell_index, &object_bit);
- return object_start_bit_map_[cell_index] & (1 << object_bit);
+ return load<mode>(cell_index) & (1 << object_bit);
}
inline void ObjectStartBitmap::ObjectStartIndexAndBit(Address header_address,
@@ -1358,10 +1446,7 @@ inline void ObjectStartBitmap::ObjectStartIndexAndBit(Address header_address,
template <typename Callback>
inline void ObjectStartBitmap::Iterate(Callback callback) const {
for (size_t cell_index = 0; cell_index < kReservedForBitmap; cell_index++) {
- if (!object_start_bit_map_[cell_index])
- continue;
-
- uint8_t value = object_start_bit_map_[cell_index];
+ uint8_t value = load(cell_index);
while (value) {
const int trailing_zeroes = base::bits::CountTrailingZeroBits(value);
const size_t object_start_number =
@@ -1375,6 +1460,30 @@ inline void ObjectStartBitmap::Iterate(Callback callback) const {
}
}
+template <HeapObjectHeader::AccessMode mode>
+Address ObjectStartBitmap::FindHeader(
+ 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;
+ size_t cell_index = object_start_number / kCellSize;
+#if DCHECK_IS_ON()
+ const size_t bitmap_size = kReservedForBitmap;
+ DCHECK_LT(cell_index, bitmap_size);
+#endif
+ size_t bit = object_start_number & kCellMask;
+ uint8_t byte = load<mode>(cell_index) & ((1 << (bit + 1)) - 1);
+ while (!byte) {
+ DCHECK_LT(0u, cell_index);
+ byte = load<mode>(--cell_index);
+ }
+ int leading_zeroes = base::bits::CountLeadingZeroBits(byte);
+ object_start_number =
+ (cell_index * kCellSize) + (kCellSize - 1) - leading_zeroes;
+ object_offset = object_start_number * kAllocationGranularity;
+ return object_offset + offset_;
+}
+
NO_SANITIZE_ADDRESS inline HeapObjectHeader::HeapObjectHeader(
size_t size,
size_t gc_info_index) {
@@ -1436,11 +1545,11 @@ template <HeapObjectHeader::AccessMode mode>
HeapObjectHeader* NormalPage::FindHeaderFromAddress(
ConstAddress address) const {
DCHECK(ContainedInObjectPayload(address));
- DCHECK(!ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address));
HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(
- object_start_bit_map()->FindHeader(address));
- DCHECK_LT(0u, header->GcInfoIndex());
- DCHECK_GT(header->PayloadEnd<mode>(), address);
+ object_start_bit_map()->FindHeader<mode>(address));
+ DCHECK_LT(0u, header->GcInfoIndex<mode>());
+ DCHECK_GT(header->PayloadEnd<HeapObjectHeader::AccessMode::kAtomic>(),
+ address);
return header;
}
@@ -1450,7 +1559,7 @@ void NormalPage::IterateCardTable(Function function) const {
// the loop (this may in turn pessimize barrier implementation).
for (auto card : card_table_) {
if (UNLIKELY(card.bit)) {
- IterateOnCard(std::move(function), card.index);
+ IterateOnCard(function, card.index);
}
}
}
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 be70f668bb2..11a0b93222c 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
@@ -146,21 +146,6 @@ size_t ThreadHeapStatsCollector::object_size_in_bytes() const {
allocated_bytes_since_prev_gc_);
}
-double ThreadHeapStatsCollector::estimated_marking_time_in_seconds() const {
- // Assume 8ms time for an initial heap. 8 ms is long enough for low-end mobile
- // devices to mark common real-world object graphs.
- constexpr double kInitialMarkingTimeInSeconds = 0.008;
-
- const double prev_marking_speed =
- previous().marking_time_in_bytes_per_second();
- return prev_marking_speed ? prev_marking_speed * object_size_in_bytes()
- : kInitialMarkingTimeInSeconds;
-}
-
-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];
}
@@ -171,6 +156,11 @@ base::TimeDelta ThreadHeapStatsCollector::Event::incremental_marking_time()
scope_data[kIncrementalMarkingStep] + scope_data[kUnifiedMarkingStep];
}
+base::TimeDelta
+ThreadHeapStatsCollector::Event::worklist_processing_time_foreground() const {
+ return scope_data[kMarkProcessWorklist];
+}
+
base::TimeDelta ThreadHeapStatsCollector::Event::atomic_marking_time() const {
return scope_data[kAtomicPauseMarkPrologue] +
scope_data[kAtomicPauseMarkRoots] +
@@ -243,6 +233,11 @@ base::TimeDelta ThreadHeapStatsCollector::marking_time_so_far() const {
return current_.marking_time();
}
+base::TimeDelta ThreadHeapStatsCollector::worklist_processing_time_foreground()
+ const {
+ return current_.worklist_processing_time_foreground();
+}
+
size_t ThreadHeapStatsCollector::allocated_space_bytes() const {
return allocated_space_bytes_;
}
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 aa02c04c1f3..d6c1dd75efa 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
@@ -232,6 +232,9 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector {
// Time spent incrementally marking the heap.
base::TimeDelta incremental_marking_time() const;
+ // Time spent processing worklist in the foreground thread.
+ base::TimeDelta worklist_processing_time_foreground() const;
+
// Time spent in foreground tasks marking the heap.
base::TimeDelta foreground_marking_time() const;
@@ -315,15 +318,11 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector {
// and newly allocated bytes since the previous cycle.
size_t object_size_in_bytes() const;
- // Estimated marking time in seconds. Based on marked bytes and mark speed in
- // the previous cycle assuming that the collection rate of the current cycle
- // is similar to the rate of the last GC.
- double estimated_marking_time_in_seconds() const;
- base::TimeDelta estimated_marking_time() const;
-
size_t marked_bytes() const;
base::TimeDelta marking_time_so_far() const;
+ base::TimeDelta worklist_processing_time_foreground() const;
+
int64_t allocated_bytes_since_prev_gc() const;
size_t allocated_space_bytes() const;
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
deleted file mode 100644
index 493a5d1658b..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
+++ /dev/null
@@ -1,662 +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/heap/heap_stats_collector.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-constexpr size_t kNoMarkedBytes = 0;
-
-} // namespace
-
-// =============================================================================
-// ThreadHeapStatsCollector. ===================================================
-// =============================================================================
-
-TEST(ThreadHeapStatsCollectorTest, InitialEmpty) {
- ThreadHeapStatsCollector stats_collector;
- 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]);
- }
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, IncreaseScopeTime) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(1));
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
- stats_collector.current()
- .scope_data[ThreadHeapStatsCollector::kIncrementalMarkingStep]);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, StopMovesCurrentToPrevious) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
- stats_collector.previous()
- .scope_data[ThreadHeapStatsCollector::kIncrementalMarkingStep]);
-}
-
-TEST(ThreadHeapStatsCollectorTest, StopResetsCurrent) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(base::TimeDelta(),
- stats_collector.current()
- .scope_data[ThreadHeapStatsCollector::kIncrementalMarkingStep]);
-}
-
-TEST(ThreadHeapStatsCollectorTest, StartStop) {
- ThreadHeapStatsCollector stats_collector;
- EXPECT_FALSE(stats_collector.is_started());
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- EXPECT_TRUE(stats_collector.is_started());
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_FALSE(stats_collector.is_started());
-}
-
-TEST(ThreadHeapStatsCollectorTest, ScopeToString) {
- EXPECT_STREQ("BlinkGC.IncrementalMarkingStartMarking",
- ThreadHeapStatsCollector::ToString(
- ThreadHeapStatsCollector::kIncrementalMarkingStartMarking,
- BlinkGC::CollectionType::kMajor));
-}
-
-TEST(ThreadHeapStatsCollectorTest, UpdateReason) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.UpdateReason(BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(BlinkGC::GCReason::kForcedGCForTesting,
- stats_collector.previous().reason);
-}
-
-TEST(ThreadHeapStatsCollectorTest, InitialEstimatedObjectSize) {
- ThreadHeapStatsCollector stats_collector;
- 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();
-}
-
-TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeNoMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- 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);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeWithMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(128);
- stats_collector.NotifySweepingCompleted();
- 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());
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EstimatedObjectSizeDoNotCountCurrentlyMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(128);
- stats_collector.NotifySweepingCompleted();
- 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);
- EXPECT_EQ(640u, stats_collector.object_size_in_bytes());
- stats_collector.NotifySweepingCompleted();
-}
-
-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::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- EXPECT_LT(0u, stats_collector.estimated_marking_time_in_seconds());
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime1) {
- ThreadHeapStatsCollector stats_collector;
- 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::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- EXPECT_DOUBLE_EQ(1.0, stats_collector.estimated_marking_time_in_seconds());
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime2) {
- ThreadHeapStatsCollector stats_collector;
- 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::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);
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, SubMilliSecondMarkingTime) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStartMarking,
- base::TimeDelta::FromMillisecondsD(.5));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- EXPECT_DOUBLE_EQ(0.5,
- stats_collector.marking_time_so_far().InMillisecondsF());
- stats_collector.NotifySweepingCompleted();
-}
-
-TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesInitialZero) {
- ThreadHeapStatsCollector stats_collector;
- EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
- 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());
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
-}
-
-TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesIncrease) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.IncreaseAllocatedSpace(1024);
- EXPECT_EQ(1024u, stats_collector.allocated_space_bytes());
-}
-
-TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesDecrease) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.IncreaseAllocatedSpace(1024);
- stats_collector.DecreaseAllocatedSpace(1024);
- EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
-}
-
-// =============================================================================
-// ThreadHeapStatsCollector::Event. ============================================
-// =============================================================================
-
-TEST(ThreadHeapStatsCollectorTest, EventPrevGCMarkedObjectSize) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(1024);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(1024u, stats_collector.previous().marked_bytes);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventMarkingTimeFromIncrementalStandAloneGC) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStartMarking,
- base::TimeDelta::FromMilliseconds(7));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(2));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromMilliseconds(4));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_DOUBLE_EQ(13.0,
- stats_collector.previous().marking_time().InMillisecondsF());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeFromIncrementalUnifiedGC) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStartMarking,
- base::TimeDelta::FromMilliseconds(7));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(2));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kUnifiedMarkingStep,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkPrologue,
- base::TimeDelta::FromMilliseconds(3));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromMilliseconds(2));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkEpilogue,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_DOUBLE_EQ(16.0,
- stats_collector.previous().marking_time().InMillisecondsF());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventMarkingTime) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kIncrementalMarkingStep,
- base::TimeDelta::FromMilliseconds(2));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromMilliseconds(11));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_DOUBLE_EQ(13.0,
- stats_collector.previous().marking_time().InMillisecondsF());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventAtomicMarkingTime) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkPrologue,
- base::TimeDelta::FromMilliseconds(5));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromMilliseconds(3));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkEpilogue,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(9),
- stats_collector.previous().atomic_marking_time());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventAtomicPause) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromMilliseconds(17));
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseSweepAndCompact,
- base::TimeDelta::FromMilliseconds(15));
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(32),
- stats_collector.previous().atomic_pause_time());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure,
- base::TimeDelta::FromSeconds(1));
- stats_collector.NotifyMarkingCompleted(1000);
- stats_collector.NotifySweepingCompleted();
- EXPECT_DOUBLE_EQ(
- .001, stats_collector.previous().marking_time_in_bytes_per_second());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventSweepingTime) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle,
- base::TimeDelta::FromMilliseconds(1));
- stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle,
- base::TimeDelta::FromMilliseconds(2));
- stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle,
- base::TimeDelta::FromMilliseconds(3));
- stats_collector.IncreaseScopeTime(
- ThreadHeapStatsCollector::kLazySweepOnAllocation,
- base::TimeDelta::FromMilliseconds(4));
- stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kCompleteSweep,
- base::TimeDelta::FromMilliseconds(5));
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(15),
- stats_collector.previous().sweeping_time());
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.IncreaseCompactionFreedSize(512);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(512u, stats_collector.previous().compaction_freed_bytes);
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedPages) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.IncreaseCompactionFreedPages(3);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(3u, stats_collector.previous().compaction_freed_pages);
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventInitialEstimatedLiveObjectRate) {
- ThreadHeapStatsCollector stats_collector;
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateSameMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(128);
- stats_collector.NotifySweepingCompleted();
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateHalfMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(256);
- stats_collector.NotifySweepingCompleted();
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateNoMarkedBytes) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(256);
- stats_collector.NotifySweepingCompleted();
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifySweepingCompleted();
- EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateWithAllocatedBytes1) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(128);
- stats_collector.NotifySweepingCompleted();
- stats_collector.IncreaseAllocatedObjectSize(128);
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateWithAllocatedBytes2) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.NotifySweepingCompleted();
- stats_collector.IncreaseAllocatedObjectSize(128);
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateWithAllocatedBytes3) {
- ThreadHeapStatsCollector stats_collector;
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest,
- EventEstimatedLiveObjectRateWithAllocatedBytes4) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.NotifyMarkingCompleted(128);
- stats_collector.NotifySweepingCompleted();
- 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);
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping1) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseAllocatedSpace(1024);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.IncreaseAllocatedSpace(2048);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(
- 1024u,
- stats_collector.previous().allocated_space_in_bytes_before_sweeping);
-}
-
-TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping2) {
- ThreadHeapStatsCollector stats_collector;
- stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector.IncreaseAllocatedSpace(1024);
- stats_collector.NotifyMarkingCompleted(kNoMarkedBytes);
- stats_collector.DecreaseAllocatedSpace(1024);
- stats_collector.NotifySweepingCompleted();
- EXPECT_EQ(
- 1024u,
- stats_collector.previous().allocated_space_in_bytes_before_sweeping);
-}
-
-// =============================================================================
-// ThreadHeapStatsObserver. ====================================================
-// =============================================================================
-
-namespace {
-
-class MockThreadHeapStatsObserver : public ThreadHeapStatsObserver {
- public:
- MOCK_METHOD1(IncreaseAllocatedSpace, void(size_t));
- MOCK_METHOD1(DecreaseAllocatedSpace, void(size_t));
- MOCK_METHOD1(ResetAllocatedObjectSize, void(size_t));
- MOCK_METHOD1(IncreaseAllocatedObjectSize, void(size_t));
- MOCK_METHOD1(DecreaseAllocatedObjectSize, void(size_t));
-};
-
-void FakeGC(ThreadHeapStatsCollector* stats_collector, size_t marked_bytes) {
- stats_collector->NotifyMarkingStarted(BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- stats_collector->NotifyMarkingCompleted(marked_bytes);
- stats_collector->NotifySweepingCompleted();
-}
-
-} // namespace
-
-TEST(ThreadHeapStatsCollectorTest, RegisterUnregisterObserver) {
- ThreadHeapStatsCollector stats_collector;
- MockThreadHeapStatsObserver observer;
- stats_collector.RegisterObserver(&observer);
- stats_collector.UnregisterObserver(&observer);
-}
-
-TEST(ThreadHeapStatsCollectorTest, ObserveAllocatedSpace) {
- ThreadHeapStatsCollector stats_collector;
- MockThreadHeapStatsObserver observer;
- stats_collector.RegisterObserver(&observer);
- EXPECT_CALL(observer, IncreaseAllocatedSpace(1024));
- stats_collector.IncreaseAllocatedSpace(1024);
- EXPECT_CALL(observer, DecreaseAllocatedSpace(1024));
- stats_collector.DecreaseAllocatedSpace(1024);
- stats_collector.UnregisterObserver(&observer);
-}
-
-TEST(ThreadHeapStatsCollectorTest, ObserveResetAllocatedObjectSize) {
- ThreadHeapStatsCollector stats_collector;
- MockThreadHeapStatsObserver observer;
- stats_collector.RegisterObserver(&observer);
- EXPECT_CALL(observer, ResetAllocatedObjectSize(2048));
- FakeGC(&stats_collector, 2048);
- stats_collector.UnregisterObserver(&observer);
-}
-
-TEST(ThreadHeapStatsCollectorTest, ObserveAllocatedObjectSize) {
- ThreadHeapStatsCollector stats_collector;
- MockThreadHeapStatsObserver observer;
- stats_collector.RegisterObserver(&observer);
- EXPECT_CALL(observer, IncreaseAllocatedObjectSize(1024));
- stats_collector.IncreaseAllocatedObjectSizeForTesting(1024);
- EXPECT_CALL(observer, DecreaseAllocatedObjectSize(1024));
- stats_collector.DecreaseAllocatedObjectSizeForTesting(1024);
- stats_collector.UnregisterObserver(&observer);
-}
-
-namespace {
-
-class ObserverTriggeringGC final : public ThreadHeapStatsObserver {
- public:
- explicit ObserverTriggeringGC(ThreadHeapStatsCollector* stats_collector)
- : stats_collector_(stats_collector) {}
-
- void IncreaseAllocatedObjectSize(size_t bytes) final {
- increase_call_count++;
- increased_size_bytes_ += bytes;
- if (increase_call_count == 1) {
- FakeGC(stats_collector_, bytes);
- }
- }
-
- void ResetAllocatedObjectSize(size_t marked) final {
- reset_call_count++;
- marked_bytes_ = marked;
- }
-
- // Mock out the rest to trigger warnings if used.
- MOCK_METHOD1(IncreaseAllocatedSpace, void(size_t));
- MOCK_METHOD1(DecreaseAllocatedSpace, void(size_t));
- MOCK_METHOD1(DecreaseAllocatedObjectSize, void(size_t));
-
- size_t marked_bytes() const { return marked_bytes_; }
- size_t increased_size_bytes() const { return increased_size_bytes_; }
-
- size_t increase_call_count = 0;
- size_t reset_call_count = 0;
-
- private:
- ThreadHeapStatsCollector* const stats_collector_;
- size_t marked_bytes_ = 0;
- size_t increased_size_bytes_ = 0;
-};
-
-} // namespace
-
-TEST(ThreadHeapStatsCollectorTest, ObserverTriggersGC) {
- ThreadHeapStatsCollector stats_collector;
- ObserverTriggeringGC gc_observer(&stats_collector);
- MockThreadHeapStatsObserver mock_observer;
- // Internal detail: First registered observer is also notified first.
- stats_collector.RegisterObserver(&gc_observer);
- stats_collector.RegisterObserver(&mock_observer);
-
- // mock_observer is notified after triggering GC. This means that it should
- // see the reset call with the fully marked size (as gc_observer fakes a GC
- // with that size).
- EXPECT_CALL(mock_observer, ResetAllocatedObjectSize(1024));
- // Since the GC clears counters, it should see an increase call with a delta
- // of zero bytes.
- EXPECT_CALL(mock_observer, IncreaseAllocatedObjectSize(0));
-
- // Trigger scenario.
- stats_collector.IncreaseAllocatedObjectSizeForTesting(1024);
-
- // gc_observer sees both calls exactly once.
- EXPECT_EQ(1u, gc_observer.increase_call_count);
- EXPECT_EQ(1u, gc_observer.reset_call_count);
- // gc_observer sees the increased bytes and the reset call with the fully
- // marked size.
- EXPECT_EQ(1024u, gc_observer.increased_size_bytes());
- EXPECT_EQ(1024u, gc_observer.marked_bytes());
-
- stats_collector.UnregisterObserver(&gc_observer);
- stats_collector.UnregisterObserver(&mock_observer);
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_test.cc
deleted file mode 100644
index 25e72fb74fb..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/heap_test.cc
+++ /dev/null
@@ -1,5417 +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:
- *
- * * 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 <algorithm>
-#include <atomic>
-#include <memory>
-#include <utility>
-
-#include "base/atomic_ref_count.h"
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/memory/ptr_util.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#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_stats_collector.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
-#include "third_party/blink/renderer/platform/heap/self_keep_alive.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
-#include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
-#include "third_party/blink/renderer/platform/heap/visitor.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/unit_test_helpers.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
-#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h"
-
-namespace blink {
-
-namespace {
-
-class HeapTest : public TestSupportingGC {};
-
-class IntWrapper : public GarbageCollected<IntWrapper> {
- public:
- virtual ~IntWrapper() {
- destructor_calls_.fetch_add(1, std::memory_order_relaxed);
- }
-
- static std::atomic_int destructor_calls_;
- void Trace(Visitor* visitor) {}
-
- int Value() const { return x_; }
-
- bool operator==(const IntWrapper& other) const {
- return other.Value() == Value();
- }
-
- unsigned GetHash() { return IntHash<int>::GetHash(x_); }
-
- IntWrapper(int x) : x_(x) {}
-
- private:
- IntWrapper() = delete;
- int x_;
-};
-
-std::atomic_int IntWrapper::destructor_calls_{0};
-
-struct IntWrapperHash {
- static unsigned GetHash(const IntWrapper& key) {
- return WTF::HashInt(static_cast<uint32_t>(key.Value()));
- }
-
- static bool Equal(const IntWrapper& a, const IntWrapper& b) { return a == b; }
-};
-
-static_assert(WTF::IsTraceable<IntWrapper>::value,
- "IsTraceable<> template failed to recognize trace method.");
-static_assert(WTF::IsTraceable<HeapVector<IntWrapper>>::value,
- "HeapVector<IntWrapper> must be traceable.");
-static_assert(WTF::IsTraceable<HeapDeque<IntWrapper>>::value,
- "HeapDeque<IntWrapper> must be traceable.");
-static_assert(WTF::IsTraceable<HeapHashSet<IntWrapper, IntWrapperHash>>::value,
- "HeapHashSet<IntWrapper> must be traceable.");
-static_assert(WTF::IsTraceable<HeapHashMap<int, IntWrapper>>::value,
- "HeapHashMap<int, IntWrapper> must be traceable.");
-
-class KeyWithCopyingMoveConstructor final {
- DISALLOW_NEW();
-
- public:
- struct Hash final {
- STATIC_ONLY(Hash);
-
- public:
- static unsigned GetHash(const KeyWithCopyingMoveConstructor& key) {
- return key.hash_;
- }
-
- static bool Equal(const KeyWithCopyingMoveConstructor& x,
- const KeyWithCopyingMoveConstructor& y) {
- return x.hash_ == y.hash_;
- }
-
- static constexpr bool safe_to_compare_to_empty_or_deleted = true;
- };
-
- KeyWithCopyingMoveConstructor() = default;
- KeyWithCopyingMoveConstructor(WTF::HashTableDeletedValueType) : hash_(-1) {}
- ~KeyWithCopyingMoveConstructor() = default;
- KeyWithCopyingMoveConstructor(unsigned hash, const String& string)
- : hash_(hash), string_(string) {
- DCHECK_NE(hash_, 0);
- DCHECK_NE(hash_, -1);
- }
- KeyWithCopyingMoveConstructor(const KeyWithCopyingMoveConstructor&) = default;
- // The move constructor delegates to the copy constructor intentionally.
- KeyWithCopyingMoveConstructor(KeyWithCopyingMoveConstructor&& x)
- : KeyWithCopyingMoveConstructor(x) {}
- KeyWithCopyingMoveConstructor& operator=(
- const KeyWithCopyingMoveConstructor&) = default;
- bool operator==(const KeyWithCopyingMoveConstructor& x) const {
- return hash_ == x.hash_;
- }
-
- bool IsHashTableDeletedValue() const { return hash_ == -1; }
-
- private:
- int hash_ = 0;
- String string_;
-};
-
-struct SameSizeAsPersistent {
- void* pointer_[4];
-#if BUILDFLAG(RAW_HEAP_SNAPSHOTS)
- PersistentLocation location;
-#endif // BUILDFLAG(RAW_HEAP_SNAPSHOTS)
-};
-
-static_assert(sizeof(Persistent<IntWrapper>) <= sizeof(SameSizeAsPersistent),
- "Persistent handle should stay small");
-
-class ThreadMarker {
- DISALLOW_NEW();
-
- public:
- ThreadMarker()
- : creating_thread_(reinterpret_cast<ThreadState*>(0)), num_(0) {}
- ThreadMarker(unsigned i)
- : creating_thread_(ThreadState::Current()), num_(i) {}
- ThreadMarker(WTF::HashTableDeletedValueType deleted)
- : creating_thread_(reinterpret_cast<ThreadState*>(-1)), num_(0) {}
- ~ThreadMarker() {
- EXPECT_TRUE((creating_thread_ == ThreadState::Current()) ||
- (creating_thread_ == reinterpret_cast<ThreadState*>(0)) ||
- (creating_thread_ == reinterpret_cast<ThreadState*>(-1)));
- }
- bool IsHashTableDeletedValue() const {
- return creating_thread_ == reinterpret_cast<ThreadState*>(-1);
- }
- bool operator==(const ThreadMarker& other) const {
- return other.creating_thread_ == creating_thread_ && other.num_ == num_;
- }
- ThreadState* creating_thread_;
- unsigned num_;
-};
-
-struct ThreadMarkerHash {
- static unsigned GetHash(const ThreadMarker& key) {
- return static_cast<unsigned>(
- reinterpret_cast<uintptr_t>(key.creating_thread_) + key.num_);
- }
-
- static bool Equal(const ThreadMarker& a, const ThreadMarker& b) {
- return a == b;
- }
-
- static const bool safe_to_compare_to_empty_or_deleted = false;
-};
-
-} // namespace
-
-} // namespace blink
-
-namespace WTF {
-
-template <typename T>
-struct DefaultHash;
-template <>
-struct DefaultHash<blink::ThreadMarker> {
- typedef blink::ThreadMarkerHash Hash;
-};
-
-// ThreadMarkerHash is the default hash for ThreadMarker
-template <>
-struct HashTraits<blink::ThreadMarker>
- : GenericHashTraits<blink::ThreadMarker> {
- static const bool kEmptyValueIsZero = true;
- static void ConstructDeletedValue(blink::ThreadMarker& slot, bool) {
- new (NotNull, &slot) blink::ThreadMarker(kHashTableDeletedValue);
- }
- static bool IsDeletedValue(const blink::ThreadMarker& slot) {
- return slot.IsHashTableDeletedValue();
- }
-};
-
-template <>
-struct DefaultHash<blink::KeyWithCopyingMoveConstructor> {
- using Hash = blink::KeyWithCopyingMoveConstructor::Hash;
-};
-
-template <>
-struct HashTraits<blink::KeyWithCopyingMoveConstructor>
- : public SimpleClassHashTraits<blink::KeyWithCopyingMoveConstructor> {};
-
-} // namespace WTF
-
-namespace blink {
-
-class TestGCCollectGarbageScope {
- STACK_ALLOCATED();
-
- public:
- explicit TestGCCollectGarbageScope(BlinkGC::StackState state) {
- DCHECK(ThreadState::Current()->CheckThread());
- }
-
- ~TestGCCollectGarbageScope() { ThreadState::Current()->CompleteSweep(); }
-};
-
-class TestGCScope : public TestGCCollectGarbageScope {
- public:
- explicit TestGCScope(BlinkGC::StackState state)
- : TestGCCollectGarbageScope(state) {
- ThreadState::Current()->Heap().stats_collector()->NotifyMarkingStarted(
- BlinkGC::CollectionType::kMajor,
- BlinkGC::GCReason::kForcedGCForTesting);
- ThreadState::Current()->AtomicPauseMarkPrologue(
- BlinkGC::CollectionType::kMajor, state, BlinkGC::kAtomicMarking,
- BlinkGC::GCReason::kForcedGCForTesting);
- }
- ~TestGCScope() {
- ThreadState::Current()->AtomicPauseMarkEpilogue(BlinkGC::kAtomicMarking);
- ThreadState::Current()->AtomicPauseSweepAndCompact(
- BlinkGC::CollectionType::kMajor, BlinkGC::kAtomicMarking,
- BlinkGC::kEagerSweeping);
- ThreadState::Current()->AtomicPauseEpilogue();
- }
-};
-
-class SimpleObject : public GarbageCollected<SimpleObject> {
- public:
- SimpleObject() = default;
- 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
- // as the super class for objects that also have garbage
- // collected mixins and having a virtual here makes sure
- // that adjustment is needed both for marking and for isAlive
- // checks.
- virtual void VirtualMethod() {}
-
- protected:
- char payload[64];
-};
-
-class HeapTestSuperClass : public GarbageCollected<HeapTestSuperClass> {
- public:
- HeapTestSuperClass() = default;
- virtual ~HeapTestSuperClass() { ++destructor_calls_; }
-
- static int destructor_calls_;
- void Trace(Visitor* visitor) {}
-};
-
-int HeapTestSuperClass::destructor_calls_ = 0;
-
-class HeapTestOtherSuperClass {
- public:
- int payload;
-};
-
-static const size_t kClassMagic = 0xABCDDBCA;
-
-class HeapTestSubClass : public HeapTestOtherSuperClass,
- public HeapTestSuperClass {
- public:
- HeapTestSubClass() : magic_(kClassMagic) {}
- ~HeapTestSubClass() override {
- EXPECT_EQ(kClassMagic, magic_);
- ++destructor_calls_;
- }
-
- static int destructor_calls_;
-
- private:
- const size_t magic_;
-};
-
-int HeapTestSubClass::destructor_calls_ = 0;
-
-class HeapAllocatedArray : public GarbageCollected<HeapAllocatedArray> {
- public:
- HeapAllocatedArray() {
- for (int i = 0; i < kArraySize; ++i) {
- array_[i] = i % 128;
- }
- }
-
- int8_t at(size_t i) { return array_[i]; }
- void Trace(Visitor* visitor) {}
-
- private:
- static const int kArraySize = 1000;
- int8_t array_[kArraySize];
-};
-
-class OffHeapInt : public RefCounted<OffHeapInt> {
- USING_FAST_MALLOC(OffHeapInt);
-
- public:
- static scoped_refptr<OffHeapInt> Create(int x) {
- return base::AdoptRef(new OffHeapInt(x));
- }
-
- virtual ~OffHeapInt() { ++destructor_calls_; }
-
- static int destructor_calls_;
-
- int Value() const { return x_; }
-
- bool operator==(const OffHeapInt& other) const {
- return other.Value() == Value();
- }
-
- unsigned GetHash() { return IntHash<int>::GetHash(x_); }
- void VoidFunction() {}
-
- protected:
- OffHeapInt(int x) : x_(x) {}
-
- private:
- OffHeapInt() = delete;
- int x_;
-};
-
-int OffHeapInt::destructor_calls_ = 0;
-
-class ThreadedTesterBase {
- protected:
- static void Test(ThreadedTesterBase* tester) {
- std::unique_ptr<Thread> threads[kNumberOfThreads];
- for (auto& thread : threads) {
- thread = Platform::Current()->CreateThread(
- ThreadCreationParams(ThreadType::kTestThread)
- .SetThreadNameForTest("blink gc testing thread"));
- PostCrossThreadTask(
- *thread->GetTaskRunner(), FROM_HERE,
- CrossThreadBindOnce(ThreadFunc, CrossThreadUnretained(tester)));
- }
- tester->done_.Wait();
- delete tester;
- }
-
- virtual void RunThread() = 0;
-
- protected:
- static const int kNumberOfThreads = 10;
- static const int kGcPerThread = 5;
- static const int kNumberOfAllocations = 50;
-
- virtual ~ThreadedTesterBase() = default;
-
- inline bool Done() const {
- return gc_count_.load(std::memory_order_acquire) >=
- kNumberOfThreads * kGcPerThread;
- }
-
- std::atomic_int gc_count_{0};
-
- private:
- static void ThreadFunc(ThreadedTesterBase* tester) {
- ThreadState::AttachCurrentThread();
- tester->RunThread();
- ThreadState::DetachCurrentThread();
- if (!tester->threads_to_finish_.Decrement())
- tester->done_.Signal();
- }
-
- base::AtomicRefCount threads_to_finish_{kNumberOfThreads};
- base::WaitableEvent done_;
-};
-
-// Needed to give this variable a definition (the initializer above is only a
-// declaration), so that subclasses can use it.
-const int ThreadedTesterBase::kNumberOfThreads;
-
-class ThreadedHeapTester : public ThreadedTesterBase {
- public:
- static void Test() { ThreadedTesterBase::Test(new ThreadedHeapTester); }
-
- ~ThreadedHeapTester() override {
- // Verify that the threads cleared their CTPs when
- // terminating, preventing access to a finalized heap.
- for (auto& global_int_wrapper : cross_persistents_) {
- DCHECK(global_int_wrapper.get());
- EXPECT_FALSE(global_int_wrapper.get()->Get());
- }
- }
-
- protected:
- using GlobalIntWrapperPersistent = CrossThreadPersistent<IntWrapper>;
-
- Mutex mutex_;
- Vector<std::unique_ptr<GlobalIntWrapperPersistent>> cross_persistents_;
-
- std::unique_ptr<GlobalIntWrapperPersistent> CreateGlobalPersistent(
- int value) {
- return std::make_unique<GlobalIntWrapperPersistent>(
- MakeGarbageCollected<IntWrapper>(value));
- }
-
- void AddGlobalPersistent() {
- MutexLocker lock(mutex_);
- cross_persistents_.push_back(CreateGlobalPersistent(0x2a2a2a2a));
- }
-
- void RunThread() override {
- // Add a cross-thread persistent from this thread; the test object
- // verifies that it will have been cleared out after the threads
- // have all detached, running their termination GCs while doing so.
- AddGlobalPersistent();
-
- int gc_count = 0;
- while (!Done()) {
- {
- Persistent<IntWrapper> wrapper;
-
- std::unique_ptr<GlobalIntWrapperPersistent> global_persistent =
- CreateGlobalPersistent(0x0ed0cabb);
-
- for (int i = 0; i < kNumberOfAllocations; i++) {
- wrapper = MakeGarbageCollected<IntWrapper>(0x0bbac0de);
- if (!(i % 10)) {
- global_persistent = CreateGlobalPersistent(0x0ed0cabb);
- }
- test::YieldCurrentThread();
- }
-
- if (gc_count < kGcPerThread) {
- TestSupportingGC::PreciselyCollectGarbage();
- gc_count++;
- gc_count_.fetch_add(1, std::memory_order_release);
- }
-
- TestSupportingGC::PreciselyCollectGarbage();
- EXPECT_EQ(wrapper->Value(), 0x0bbac0de);
- EXPECT_EQ((*global_persistent)->Value(), 0x0ed0cabb);
- }
- test::YieldCurrentThread();
- }
- }
-};
-
-class ThreadedWeaknessTester : public ThreadedTesterBase {
- public:
- static void Test() { ThreadedTesterBase::Test(new ThreadedWeaknessTester); }
-
- private:
- void RunThread() override {
- int gc_count = 0;
- while (!Done()) {
- {
- Persistent<HeapHashMap<ThreadMarker, WeakMember<IntWrapper>>> weak_map =
- MakeGarbageCollected<
- HeapHashMap<ThreadMarker, WeakMember<IntWrapper>>>();
-
- for (int i = 0; i < kNumberOfAllocations; i++) {
- weak_map->insert(static_cast<unsigned>(i),
- MakeGarbageCollected<IntWrapper>(0));
- test::YieldCurrentThread();
- }
-
- if (gc_count < kGcPerThread) {
- TestSupportingGC::PreciselyCollectGarbage();
- gc_count++;
- gc_count_.fetch_add(1, std::memory_order_release);
- }
-
- TestSupportingGC::PreciselyCollectGarbage();
- EXPECT_TRUE(weak_map->IsEmpty());
- }
- test::YieldCurrentThread();
- }
- }
-};
-
-class ThreadPersistentHeapTester : public ThreadedTesterBase {
- public:
- static void Test() {
- ThreadedTesterBase::Test(new ThreadPersistentHeapTester);
- }
-
- protected:
- class Local final : public GarbageCollected<Local> {
- public:
- Local() = default;
-
- void Trace(Visitor* visitor) {}
- };
-
- class PersistentChain;
-
- class RefCountedChain : public RefCounted<RefCountedChain> {
- public:
- static RefCountedChain* Create(int count) {
- return new RefCountedChain(count);
- }
-
- private:
- explicit RefCountedChain(int count) {
- if (count > 0) {
- --count;
- persistent_chain_ = MakeGarbageCollected<PersistentChain>(count);
- }
- }
-
- Persistent<PersistentChain> persistent_chain_;
- };
-
- class PersistentChain final : public GarbageCollected<PersistentChain> {
- public:
- explicit PersistentChain(int count) {
- ref_counted_chain_ = base::AdoptRef(RefCountedChain::Create(count));
- }
-
- void Trace(Visitor* visitor) {}
-
- private:
- scoped_refptr<RefCountedChain> ref_counted_chain_;
- };
-
- void RunThread() override {
- MakeGarbageCollected<PersistentChain>(100);
-
- // Upon thread detach, GCs will run until all persistents have been
- // released. We verify that the draining of persistents proceeds
- // as expected by dropping one Persistent<> per GC until there
- // are none left.
- }
-};
-
-// The accounting for memory includes the memory used by rounding up object
-// sizes. This is done in a different way on 32 bit and 64 bit, so we have to
-// have some slack in the tests.
-template <typename T>
-void CheckWithSlack(T expected, T actual, int slack) {
- EXPECT_LE(expected, actual);
- EXPECT_GE((intptr_t)expected + slack, (intptr_t)actual);
-}
-
-class TraceCounter final : public GarbageCollected<TraceCounter> {
- public:
- TraceCounter() : trace_count_(0) {}
-
- void Trace(Visitor* visitor) { trace_count_++; }
- int TraceCount() const { return trace_count_; }
-
- private:
- int trace_count_;
-};
-
-TEST_F(HeapTest, IsHeapObjectAliveForConstPointer) {
- // See http://crbug.com/661363.
- auto* object = MakeGarbageCollected<SimpleObject>();
- HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
- LivenessBroker broker = internal::LivenessBrokerFactory::Create();
- EXPECT_TRUE(header->TryMark());
- EXPECT_TRUE(broker.IsHeapObjectAlive(object));
- const SimpleObject* const_object = const_cast<const SimpleObject*>(object);
- EXPECT_TRUE(broker.IsHeapObjectAlive(const_object));
-}
-
-class ClassWithMember : public GarbageCollected<ClassWithMember> {
- public:
- ClassWithMember() : trace_counter_(MakeGarbageCollected<TraceCounter>()) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(trace_counter_); }
- int TraceCount() const { return trace_counter_->TraceCount(); }
-
- private:
- Member<TraceCounter> trace_counter_;
-};
-
-class SimpleFinalizedObject final
- : public GarbageCollected<SimpleFinalizedObject> {
- public:
- SimpleFinalizedObject() = default;
- ~SimpleFinalizedObject() { ++destructor_calls_; }
-
- static int destructor_calls_;
-
- void Trace(Visitor* visitor) {}
-};
-
-int SimpleFinalizedObject::destructor_calls_ = 0;
-
-class IntNode : public GarbageCollected<IntNode> {
- public:
- // IntNode is used to test typed heap allocation. Instead of
- // redefining blink::Node to our test version, we keep it separate
- // so as to avoid possible warnings about linker duplicates.
- // Override operator new to allocate IntNode subtype objects onto
- // the dedicated heap for blink::Node.
- //
- // TODO(haraken): untangling the heap unit tests from Blink would
- // simplify and avoid running into this problem - http://crbug.com/425381
- GC_PLUGIN_IGNORE("crbug.com/443854")
- void* operator new(size_t size) {
- ThreadState* state = ThreadState::Current();
- const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(IntNode);
- return state->Heap().AllocateOnArenaIndex(
- state, size, BlinkGC::kNodeArenaIndex, GCInfoTrait<IntNode>::Index(),
- type_name);
- }
-
- static IntNode* Create(int i) { return new IntNode(i); }
-
- void Trace(Visitor* visitor) {}
-
- int Value() { return value_; }
-
- private:
- IntNode(int i) : value_(i) {}
- int value_;
-};
-
-class Bar : public GarbageCollected<Bar> {
- public:
- Bar() : magic_(kMagic) { live_++; }
-
- void FinalizeGarbageCollectedObject() {
- EXPECT_TRUE(magic_ == kMagic);
- magic_ = 0;
- live_--;
- }
- bool HasBeenFinalized() const { return !magic_; }
-
- virtual void Trace(Visitor* visitor) {}
- static unsigned live_;
-
- protected:
- static const int kMagic = 1337;
- int magic_;
-};
-
-unsigned Bar::live_ = 0;
-
-class Baz : public GarbageCollected<Baz> {
- public:
- explicit Baz(Bar* bar) : bar_(bar) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(bar_); }
-
- void Clear() { bar_.Release(); }
-
- // willFinalize is called by FinalizationObserver.
- void WillFinalize() { EXPECT_TRUE(!bar_->HasBeenFinalized()); }
-
- private:
- Member<Bar> bar_;
-};
-
-class Foo : public Bar {
- public:
- Foo(Bar* bar) : Bar(), bar_(bar), points_to_foo_(false) {}
-
- Foo(Foo* foo) : Bar(), bar_(foo), points_to_foo_(true) {}
-
- void Trace(Visitor* visitor) override {
- if (points_to_foo_)
- visitor->Trace(static_cast<const Foo*>(bar_));
- else
- visitor->Trace(bar_);
- }
-
- private:
- const Bar* bar_;
- const bool points_to_foo_;
-};
-
-class Bars : public Bar {
- public:
- Bars() : width_(0) {
- for (unsigned i = 0; i < kWidth; i++) {
- bars_[i] = MakeGarbageCollected<Bar>();
- width_++;
- }
- }
-
- void Trace(Visitor* visitor) override {
- for (unsigned i = 0; i < width_; i++)
- visitor->Trace(bars_[i]);
- }
-
- unsigned GetWidth() const { return width_; }
-
- static const unsigned kWidth = 7500;
-
- private:
- unsigned width_;
- Member<Bar> bars_[kWidth];
-};
-
-class ConstructorAllocation : public GarbageCollected<ConstructorAllocation> {
- public:
- ConstructorAllocation() {
- int_wrapper_ = MakeGarbageCollected<IntWrapper>(42);
- }
-
- void Trace(Visitor* visitor) { visitor->Trace(int_wrapper_); }
-
- private:
- Member<IntWrapper> int_wrapper_;
-};
-
-class LargeHeapObject final : public GarbageCollected<LargeHeapObject> {
- public:
- LargeHeapObject() { int_wrapper_ = MakeGarbageCollected<IntWrapper>(23); }
- ~LargeHeapObject() { destructor_calls_++; }
-
- 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(Visitor* visitor) { visitor->Trace(int_wrapper_); }
- static int destructor_calls_;
-
- private:
- static const size_t kLength = 1024 * 1024;
- Member<IntWrapper> int_wrapper_;
- char data_[kLength];
-};
-
-int LargeHeapObject::destructor_calls_ = 0;
-
-// This test class served a more important role while Blink
-// was transitioned over to using Oilpan. That required classes
-// that were hybrid, both ref-counted and on the Oilpan heap
-// (the RefCountedGarbageCollected<> class providing just that.)
-//
-// There's no current need for having a ref-counted veneer on
-// top of a GCed class, but we preserve it here to exercise the
-// implementation technique that it used -- keeping an internal
-// "keep alive" persistent reference that is set & cleared across
-// ref-counting operations.
-//
-class RefCountedAndGarbageCollected final
- : public GarbageCollected<RefCountedAndGarbageCollected> {
- public:
- RefCountedAndGarbageCollected() : keep_alive_(PERSISTENT_FROM_HERE) {}
- ~RefCountedAndGarbageCollected() { ++destructor_calls_; }
-
- void AddRef() {
- if (UNLIKELY(!ref_count_)) {
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
- reinterpret_cast<Address>(this)));
-#endif
- keep_alive_ = this;
- }
- ++ref_count_;
- }
-
- void Release() {
- DCHECK_GT(ref_count_, 0);
- if (!--ref_count_)
- keep_alive_.Clear();
- }
-
- void Trace(Visitor* visitor) {}
-
- static int destructor_calls_;
-
- private:
- int ref_count_ = 0;
- SelfKeepAlive<RefCountedAndGarbageCollected> keep_alive_;
-};
-
-int RefCountedAndGarbageCollected::destructor_calls_ = 0;
-
-class RefCountedAndGarbageCollected2 final
- : public HeapTestOtherSuperClass,
- public GarbageCollected<RefCountedAndGarbageCollected2> {
- public:
- RefCountedAndGarbageCollected2() : keep_alive_(PERSISTENT_FROM_HERE) {}
- ~RefCountedAndGarbageCollected2() { ++destructor_calls_; }
-
- void Ref() {
- if (UNLIKELY(!ref_count_)) {
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
- reinterpret_cast<Address>(this)));
-#endif
- keep_alive_ = this;
- }
- ++ref_count_;
- }
-
- void Deref() {
- DCHECK_GT(ref_count_, 0);
- if (!--ref_count_)
- keep_alive_.Clear();
- }
-
- void Trace(Visitor* visitor) {}
-
- static int destructor_calls_;
-
- private:
- int ref_count_ = 0;
- SelfKeepAlive<RefCountedAndGarbageCollected2> keep_alive_;
-};
-
-int RefCountedAndGarbageCollected2::destructor_calls_ = 0;
-
-class Weak : public Bar {
- public:
- Weak(Bar* strong_bar, Bar* weak_bar)
- : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {}
-
- void Trace(Visitor* visitor) override {
- visitor->Trace(strong_bar_);
- visitor->template RegisterWeakCallbackMethod<Weak, &Weak::ZapWeakMembers>(
- this);
- }
-
- void ZapWeakMembers(const LivenessBroker& info) {
- if (!info.IsHeapObjectAlive(weak_bar_))
- weak_bar_ = nullptr;
- }
-
- bool StrongIsThere() { return !!strong_bar_; }
- bool WeakIsThere() { return !!weak_bar_; }
-
- private:
- Member<Bar> strong_bar_;
- Bar* weak_bar_;
-};
-
-class WithWeakMember : public Bar {
- public:
- WithWeakMember(Bar* strong_bar, Bar* weak_bar)
- : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {}
-
- void Trace(Visitor* visitor) override {
- visitor->Trace(strong_bar_);
- visitor->Trace(weak_bar_);
- }
-
- bool StrongIsThere() { return !!strong_bar_; }
- bool WeakIsThere() { return !!weak_bar_; }
-
- private:
- Member<Bar> strong_bar_;
- WeakMember<Bar> weak_bar_;
-};
-
-class Observable final : public GarbageCollected<Observable> {
- USING_PRE_FINALIZER(Observable, WillFinalize);
-
- public:
- explicit Observable(Bar* bar) : bar_(bar), was_destructed_(false) {}
- ~Observable() { was_destructed_ = true; }
- void Trace(Visitor* visitor) { visitor->Trace(bar_); }
-
- // willFinalize is called by FinalizationObserver. willFinalize can touch
- // other on-heap objects.
- void WillFinalize() {
- EXPECT_FALSE(was_destructed_);
- EXPECT_FALSE(bar_->HasBeenFinalized());
- will_finalize_was_called_ = true;
- }
- static bool will_finalize_was_called_;
-
- private:
- Member<Bar> bar_;
- bool was_destructed_;
-};
-
-bool Observable::will_finalize_was_called_ = false;
-
-class ObservableWithPreFinalizer final
- : public GarbageCollected<ObservableWithPreFinalizer> {
- USING_PRE_FINALIZER(ObservableWithPreFinalizer, Dispose);
-
- public:
- ObservableWithPreFinalizer() : was_destructed_(false) {}
- ~ObservableWithPreFinalizer() { was_destructed_ = true; }
- void Trace(Visitor* visitor) {}
- void Dispose() {
- EXPECT_FALSE(was_destructed_);
- dispose_was_called_ = true;
- }
- static bool dispose_was_called_;
-
- protected:
- bool was_destructed_;
-};
-
-bool ObservableWithPreFinalizer::dispose_was_called_ = false;
-
-bool g_dispose_was_called_for_pre_finalizer_base = false;
-bool g_dispose_was_called_for_pre_finalizer_mixin = false;
-bool g_dispose_was_called_for_pre_finalizer_sub_class = false;
-
-class PreFinalizerBase : public GarbageCollected<PreFinalizerBase> {
- USING_PRE_FINALIZER(PreFinalizerBase, Dispose);
-
- public:
- PreFinalizerBase() : was_destructed_(false) {}
- virtual ~PreFinalizerBase() { was_destructed_ = true; }
- 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);
- EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_mixin);
- EXPECT_FALSE(was_destructed_);
- g_dispose_was_called_for_pre_finalizer_base = true;
- }
-
- protected:
- bool was_destructed_;
-};
-
-class PreFinalizerMixin : public GarbageCollectedMixin {
- USING_PRE_FINALIZER(PreFinalizerMixin, Dispose);
-
- public:
- ~PreFinalizerMixin() { was_destructed_ = true; }
- 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);
- EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_mixin);
- EXPECT_FALSE(was_destructed_);
- g_dispose_was_called_for_pre_finalizer_mixin = true;
- }
-
- protected:
- PreFinalizerMixin() : was_destructed_(false) {}
- bool was_destructed_;
-};
-
-class PreFinalizerSubClass : public PreFinalizerBase, public PreFinalizerMixin {
- USING_GARBAGE_COLLECTED_MIXIN(PreFinalizerSubClass);
- USING_PRE_FINALIZER(PreFinalizerSubClass, Dispose);
-
- public:
- PreFinalizerSubClass() : was_destructed_(false) {}
- ~PreFinalizerSubClass() override { was_destructed_ = true; }
- 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);
- EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_mixin);
- EXPECT_FALSE(was_destructed_);
- g_dispose_was_called_for_pre_finalizer_sub_class = true;
- }
-
- protected:
- bool was_destructed_;
-};
-
-template <typename T>
-class FinalizationObserver : public GarbageCollected<FinalizationObserver<T>> {
- public:
- FinalizationObserver(T* data) : data_(data), did_call_will_finalize_(false) {}
-
- bool DidCallWillFinalize() const { return did_call_will_finalize_; }
-
- void Trace(Visitor* visitor) {
- visitor->template RegisterWeakCallbackMethod<
- FinalizationObserver<T>, &FinalizationObserver<T>::ZapWeakMembers>(
- this);
- }
-
- void ZapWeakMembers(const LivenessBroker& info) {
- if (data_ && !info.IsHeapObjectAlive(data_)) {
- data_->WillFinalize();
- data_ = nullptr;
- did_call_will_finalize_ = true;
- }
- }
-
- private:
- WeakMember<T> data_;
- bool did_call_will_finalize_;
-};
-
-class FinalizationObserverWithHashMap {
- public:
- typedef HeapHashMap<WeakMember<Observable>,
- std::unique_ptr<FinalizationObserverWithHashMap>>
- ObserverMap;
-
- explicit FinalizationObserverWithHashMap(Observable& target)
- : target_(target) {}
- ~FinalizationObserverWithHashMap() {
- target_.WillFinalize();
- did_call_will_finalize_ = true;
- }
-
- static ObserverMap& Observe(Observable& target) {
- ObserverMap& map = Observers();
- ObserverMap::AddResult result = map.insert(&target, nullptr);
- if (result.is_new_entry) {
- result.stored_value->value =
- std::make_unique<FinalizationObserverWithHashMap>(target);
- } else {
- DCHECK(result.stored_value->value);
- }
- return map;
- }
-
- static void ClearObservers() {
- delete observer_map_;
- observer_map_ = nullptr;
- }
-
- static bool did_call_will_finalize_;
-
- private:
- static ObserverMap& Observers() {
- if (!observer_map_) {
- observer_map_ =
- new Persistent<ObserverMap>(MakeGarbageCollected<ObserverMap>());
- }
- return **observer_map_;
- }
-
- Observable& target_;
- static Persistent<ObserverMap>* observer_map_;
-};
-
-bool FinalizationObserverWithHashMap::did_call_will_finalize_ = false;
-Persistent<FinalizationObserverWithHashMap::ObserverMap>*
- FinalizationObserverWithHashMap::observer_map_;
-
-class SuperClass;
-
-class PointsBack final : public GarbageCollected<PointsBack> {
- public:
- PointsBack() : back_pointer_(nullptr) { ++alive_count_; }
- ~PointsBack() { --alive_count_; }
-
- void SetBackPointer(SuperClass* back_pointer) {
- back_pointer_ = back_pointer;
- }
-
- SuperClass* BackPointer() const { return back_pointer_; }
-
- void Trace(Visitor* visitor) { visitor->Trace(back_pointer_); }
-
- static int alive_count_;
-
- private:
- WeakMember<SuperClass> back_pointer_;
-};
-
-int PointsBack::alive_count_ = 0;
-
-class SuperClass : public GarbageCollected<SuperClass> {
- public:
- explicit SuperClass(PointsBack* points_back) : points_back_(points_back) {
- points_back_->SetBackPointer(this);
- ++alive_count_;
- }
- virtual ~SuperClass() { --alive_count_; }
-
- void DoStuff(SuperClass* target,
- PointsBack* points_back,
- int super_class_count) {
- TestSupportingGC::ConservativelyCollectGarbage();
- EXPECT_EQ(points_back, target->GetPointsBack());
- EXPECT_EQ(super_class_count, SuperClass::alive_count_);
- }
-
- virtual void Trace(Visitor* visitor) { visitor->Trace(points_back_); }
-
- PointsBack* GetPointsBack() const { return points_back_.Get(); }
-
- static int alive_count_;
-
- private:
- Member<PointsBack> points_back_;
-};
-
-int SuperClass::alive_count_ = 0;
-class SubData final : public GarbageCollected<SubData> {
- public:
- SubData() { ++alive_count_; }
- ~SubData() { --alive_count_; }
-
- void Trace(Visitor* visitor) {}
-
- static int alive_count_;
-};
-
-int SubData::alive_count_ = 0;
-
-class SubClass : public SuperClass {
- public:
- explicit SubClass(PointsBack* points_back)
- : SuperClass(points_back), data_(MakeGarbageCollected<SubData>()) {
- ++alive_count_;
- }
- ~SubClass() override { --alive_count_; }
-
- void Trace(Visitor* visitor) override {
- visitor->Trace(data_);
- SuperClass::Trace(visitor);
- }
-
- static int alive_count_;
-
- private:
- Member<SubData> data_;
-};
-
-int SubClass::alive_count_ = 0;
-
-class Mixin : public GarbageCollectedMixin {
- public:
- void Trace(Visitor* visitor) override {}
-
- virtual char GetPayload(int i) { return padding_[i]; }
-
- protected:
- int padding_[8];
-};
-
-class UseMixin : public SimpleObject, public Mixin {
- USING_GARBAGE_COLLECTED_MIXIN(UseMixin);
-
- public:
- UseMixin() {
- // Verify that WTF::IsGarbageCollectedType<> works as expected for mixins.
- static_assert(WTF::IsGarbageCollectedType<UseMixin>::value,
- "IsGarbageCollectedType<> sanity check failed for GC mixin.");
- trace_count_ = 0;
- }
-
- static int trace_count_;
- void Trace(Visitor* visitor) override {
- SimpleObject::Trace(visitor);
- Mixin::Trace(visitor);
- ++trace_count_;
- }
-};
-
-int UseMixin::trace_count_ = 0;
-
-class VectorObject {
- DISALLOW_NEW();
-
- public:
- VectorObject() { value_ = MakeGarbageCollected<SimpleFinalizedObject>(); }
-
- void Trace(Visitor* visitor) { visitor->Trace(value_); }
-
- private:
- Member<SimpleFinalizedObject> value_;
-};
-
-class VectorObjectInheritedTrace : public VectorObject {};
-
-class VectorObjectNoTrace {
- DISALLOW_NEW();
-
- public:
- VectorObjectNoTrace() {
- value_ = MakeGarbageCollected<SimpleFinalizedObject>();
- }
-
- private:
- Member<SimpleFinalizedObject> value_;
-};
-
-class TerminatedArrayItem {
- DISALLOW_NEW();
-
- public:
- TerminatedArrayItem(IntWrapper* payload)
- : payload_(payload), is_last_(false) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(payload_); }
-
- bool IsLastInArray() const { return is_last_; }
- void SetLastInArray(bool value) { is_last_ = value; }
-
- IntWrapper* Payload() const { return payload_; }
-
- private:
- Member<IntWrapper> payload_;
- bool is_last_;
-};
-
-} // namespace blink
-
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::TerminatedArrayItem)
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::VectorObject)
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
- blink::VectorObjectInheritedTrace)
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::VectorObjectNoTrace)
-
-namespace blink {
-
-class OneKiloByteObject final : public GarbageCollected<OneKiloByteObject> {
- public:
- ~OneKiloByteObject() { destructor_calls_++; }
- char* Data() { return data_; }
- void Trace(Visitor* visitor) {}
- static int destructor_calls_;
-
- private:
- static const size_t kLength = 1024;
- char data_[kLength];
-};
-
-int OneKiloByteObject::destructor_calls_ = 0;
-
-class DynamicallySizedObject : public GarbageCollected<DynamicallySizedObject> {
- public:
- static DynamicallySizedObject* Create(size_t size) {
- void* slot = ThreadHeap::Allocate<DynamicallySizedObject>(size);
- return new (slot) DynamicallySizedObject();
- }
-
- void* operator new(std::size_t, void* location) { return location; }
-
- uint8_t Get(int i) { return *(reinterpret_cast<uint8_t*>(this) + i); }
-
- void Trace(Visitor* visitor) {}
-
- private:
- DynamicallySizedObject() = default;
-};
-
-class FinalizationAllocator final
- : public GarbageCollected<FinalizationAllocator> {
- public:
- FinalizationAllocator(Persistent<IntWrapper>* wrapper) : wrapper_(wrapper) {}
-
- ~FinalizationAllocator() {
- for (int i = 0; i < 10; ++i)
- *wrapper_ = MakeGarbageCollected<IntWrapper>(42);
- for (int i = 0; i < 512; ++i)
- MakeGarbageCollected<OneKiloByteObject>();
- for (int i = 0; i < 32; ++i)
- MakeGarbageCollected<LargeHeapObject>();
- }
-
- void Trace(Visitor* visitor) {}
-
- private:
- Persistent<IntWrapper>* wrapper_;
-};
-
-class PreFinalizerBackingShrinkForbidden final
- : public GarbageCollected<PreFinalizerBackingShrinkForbidden> {
- USING_PRE_FINALIZER(PreFinalizerBackingShrinkForbidden, Dispose);
-
- public:
- PreFinalizerBackingShrinkForbidden() {
- for (int i = 0; i < 32; ++i) {
- vector_.push_back(MakeGarbageCollected<IntWrapper>(i));
- }
- EXPECT_LT(31ul, vector_.capacity());
-
- for (int i = 0; i < 32; ++i) {
- map_.insert(i + 1, MakeGarbageCollected<IntWrapper>(i + 1));
- }
- EXPECT_LT(31ul, map_.Capacity());
- }
-
- void Dispose() {
- // Remove all elemets except one so that vector_ will try to shrink.
- for (int i = 1; i < 32; ++i) {
- vector_.pop_back();
- }
- // Check that vector_ hasn't shrunk.
- EXPECT_LT(31ul, vector_.capacity());
- // Just releasing the backing is allowed.
- vector_.clear();
- EXPECT_EQ(0ul, vector_.capacity());
-
- // Remove elemets so that map_ will try to shrink.
- for (int i = 0; i < 32; ++i) {
- map_.erase(i + 1);
- }
- // Check that map_ hasn't shrunk.
- EXPECT_LT(31ul, map_.Capacity());
- // Just releasing the backing is allowed.
- map_.clear();
- EXPECT_EQ(0ul, map_.Capacity());
- }
-
- void Trace(Visitor* visitor) {
- visitor->Trace(vector_);
- visitor->Trace(map_);
- }
-
- private:
- HeapVector<Member<IntWrapper>> vector_;
- HeapHashMap<int, Member<IntWrapper>> map_;
-};
-
-// Following 2 tests check for allocation failures. These failures happen
-// only when DCHECK is on.
-#if DCHECK_IS_ON()
-TEST_F(HeapTest, PreFinalizerBackingShrinkForbidden) {
- MakeGarbageCollected<PreFinalizerBackingShrinkForbidden>();
- PreciselyCollectGarbage();
-}
-
-class PreFinalizerVectorBackingExpandForbidden final
- : public GarbageCollected<PreFinalizerVectorBackingExpandForbidden> {
- USING_PRE_FINALIZER(PreFinalizerVectorBackingExpandForbidden, Dispose);
-
- public:
- PreFinalizerVectorBackingExpandForbidden() {
- vector_.push_back(MakeGarbageCollected<IntWrapper>(1));
- }
-
- void Dispose() { EXPECT_DEATH(Test(), ""); }
-
- void Test() {
- // vector_'s backing will need to expand.
- for (int i = 0; i < 32; ++i) {
- vector_.push_back(nullptr);
- }
- }
-
- void Trace(Visitor* visitor) { visitor->Trace(vector_); }
-
- private:
- HeapVector<Member<IntWrapper>> vector_;
-};
-
-TEST(HeapDeathTest, PreFinalizerVectorBackingExpandForbidden) {
- MakeGarbageCollected<PreFinalizerVectorBackingExpandForbidden>();
- TestSupportingGC::PreciselyCollectGarbage();
-}
-
-class PreFinalizerHashTableBackingExpandForbidden final
- : public GarbageCollected<PreFinalizerHashTableBackingExpandForbidden> {
- USING_PRE_FINALIZER(PreFinalizerHashTableBackingExpandForbidden, Dispose);
-
- public:
- PreFinalizerHashTableBackingExpandForbidden() {
- map_.insert(123, MakeGarbageCollected<IntWrapper>(123));
- }
-
- void Dispose() { EXPECT_DEATH(Test(), ""); }
-
- void Test() {
- // map_'s backing will need to expand.
- for (int i = 1; i < 32; ++i) {
- map_.insert(i, nullptr);
- }
- }
-
- void Trace(Visitor* visitor) { visitor->Trace(map_); }
-
- private:
- HeapHashMap<int, Member<IntWrapper>> map_;
-};
-
-TEST(HeapDeathTest, PreFinalizerHashTableBackingExpandForbidden) {
- MakeGarbageCollected<PreFinalizerHashTableBackingExpandForbidden>();
- TestSupportingGC::PreciselyCollectGarbage();
-}
-#endif // DCHECK_IS_ON()
-
-class PreFinalizerAllocationForbidden
- : public GarbageCollected<PreFinalizerAllocationForbidden> {
- USING_PRE_FINALIZER(PreFinalizerAllocationForbidden, Dispose);
-
- public:
- void Dispose() {
- EXPECT_FALSE(ThreadState::Current()->IsAllocationAllowed());
-#if DCHECK_IS_ON()
- EXPECT_DEATH(MakeGarbageCollected<IntWrapper>(1), "");
-#endif // DCHECK_IS_ON()
- }
-
- void Trace(Visitor* visitor) {}
-};
-
-TEST(HeapDeathTest, PreFinalizerAllocationForbidden) {
- MakeGarbageCollected<PreFinalizerAllocationForbidden>();
- TestSupportingGC::PreciselyCollectGarbage();
-}
-
-#if DCHECK_IS_ON()
-namespace {
-
-class HeapTestResurrectingPreFinalizer
- : public GarbageCollected<HeapTestResurrectingPreFinalizer> {
- USING_PRE_FINALIZER(HeapTestResurrectingPreFinalizer, Dispose);
-
- public:
- enum TestType {
- kHeapVectorMember,
- kHeapHashSetMember,
- kHeapHashSetWeakMember
- };
-
- class GlobalStorage : public GarbageCollected<GlobalStorage> {
- public:
- GlobalStorage() {
- // Reserve storage upfront to avoid allocations during pre-finalizer
- // insertion.
- vector_member.ReserveCapacity(32);
- hash_set_member.ReserveCapacityForSize(32);
- hash_set_weak_member.ReserveCapacityForSize(32);
- }
-
- void Trace(Visitor* visitor) {
- visitor->Trace(vector_member);
- visitor->Trace(hash_set_member);
- visitor->Trace(hash_set_weak_member);
- }
-
- HeapVector<Member<LinkedObject>> vector_member;
- HeapHashSet<Member<LinkedObject>> hash_set_member;
- HeapHashSet<WeakMember<LinkedObject>> hash_set_weak_member;
- };
-
- HeapTestResurrectingPreFinalizer(TestType test_type,
- GlobalStorage* storage,
- LinkedObject* object_that_dies)
- : test_type_(test_type),
- storage_(storage),
- object_that_dies_(object_that_dies) {}
-
- void Trace(Visitor* visitor) {
- visitor->Trace(storage_);
- visitor->Trace(object_that_dies_);
- }
-
- private:
- void Dispose() { EXPECT_DEATH(Test(), ""); }
-
- void Test() {
- switch (test_type_) {
- case TestType::kHeapVectorMember:
- storage_->vector_member.push_back(object_that_dies_);
- break;
- case TestType::kHeapHashSetMember:
- storage_->hash_set_member.insert(object_that_dies_);
- break;
- case TestType::kHeapHashSetWeakMember:
- storage_->hash_set_weak_member.insert(object_that_dies_);
- break;
- }
- }
-
- TestType test_type_;
- Member<GlobalStorage> storage_;
- Member<LinkedObject> object_that_dies_;
-};
-
-} // namespace
-
-TEST(HeapDeathTest, DiesOnResurrectedHeapVectorMember) {
- Persistent<HeapTestResurrectingPreFinalizer::GlobalStorage> storage(
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer::GlobalStorage>());
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer>(
- HeapTestResurrectingPreFinalizer::kHeapVectorMember, storage.Get(),
- MakeGarbageCollected<LinkedObject>());
- TestSupportingGC::PreciselyCollectGarbage();
-}
-
-TEST(HeapDeathTest, DiesOnResurrectedHeapHashSetMember) {
- Persistent<HeapTestResurrectingPreFinalizer::GlobalStorage> storage(
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer::GlobalStorage>());
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer>(
- HeapTestResurrectingPreFinalizer::kHeapHashSetMember, storage.Get(),
- MakeGarbageCollected<LinkedObject>());
- TestSupportingGC::PreciselyCollectGarbage();
-}
-
-TEST(HeapDeathTest, DiesOnResurrectedHeapHashSetWeakMember) {
- Persistent<HeapTestResurrectingPreFinalizer::GlobalStorage> storage(
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer::GlobalStorage>());
- MakeGarbageCollected<HeapTestResurrectingPreFinalizer>(
- HeapTestResurrectingPreFinalizer::kHeapHashSetWeakMember, storage.Get(),
- MakeGarbageCollected<LinkedObject>());
- TestSupportingGC::PreciselyCollectGarbage();
-}
-#endif // DCHECK_IS_ON()
-
-class LargeMixin : public GarbageCollected<LargeMixin>, public Mixin {
- USING_GARBAGE_COLLECTED_MIXIN(LargeMixin);
-
- private:
- char data[65536];
-};
-
-TEST(HeapDeathTest, LargeGarbageCollectedMixin) {
- EXPECT_DEATH(MakeGarbageCollected<LargeMixin>(AdditionalBytes(1)), "");
-}
-
-TEST_F(HeapTest, Transition) {
- {
- RefCountedAndGarbageCollected::destructor_calls_ = 0;
- Persistent<RefCountedAndGarbageCollected> ref_counted =
- MakeGarbageCollected<RefCountedAndGarbageCollected>();
- PreciselyCollectGarbage();
- EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
- RefCountedAndGarbageCollected::destructor_calls_ = 0;
-
- Persistent<PointsBack> points_back1 = MakeGarbageCollected<PointsBack>();
- Persistent<PointsBack> points_back2 = MakeGarbageCollected<PointsBack>();
- Persistent<SuperClass> super_class =
- MakeGarbageCollected<SuperClass>(points_back1);
- Persistent<SubClass> sub_class = MakeGarbageCollected<SubClass>(points_back2);
- EXPECT_EQ(2, PointsBack::alive_count_);
- EXPECT_EQ(2, SuperClass::alive_count_);
- EXPECT_EQ(1, SubClass::alive_count_);
- EXPECT_EQ(1, SubData::alive_count_);
-
- PreciselyCollectGarbage();
- EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
- EXPECT_EQ(2, PointsBack::alive_count_);
- EXPECT_EQ(2, SuperClass::alive_count_);
- EXPECT_EQ(1, SubClass::alive_count_);
- EXPECT_EQ(1, SubData::alive_count_);
-
- super_class->DoStuff(super_class.Release(), points_back1.Get(), 2);
- PreciselyCollectGarbage();
- EXPECT_EQ(2, PointsBack::alive_count_);
- EXPECT_EQ(1, SuperClass::alive_count_);
- EXPECT_EQ(1, SubClass::alive_count_);
- EXPECT_EQ(1, SubData::alive_count_);
- EXPECT_EQ(nullptr, points_back1->BackPointer());
-
- points_back1.Release();
- PreciselyCollectGarbage();
- EXPECT_EQ(1, PointsBack::alive_count_);
- EXPECT_EQ(1, SuperClass::alive_count_);
- EXPECT_EQ(1, SubClass::alive_count_);
- EXPECT_EQ(1, SubData::alive_count_);
-
- sub_class->DoStuff(sub_class.Release(), points_back2.Get(), 1);
- PreciselyCollectGarbage();
- EXPECT_EQ(1, PointsBack::alive_count_);
- EXPECT_EQ(0, SuperClass::alive_count_);
- EXPECT_EQ(0, SubClass::alive_count_);
- EXPECT_EQ(0, SubData::alive_count_);
- EXPECT_EQ(nullptr, points_back2->BackPointer());
-
- points_back2.Release();
- PreciselyCollectGarbage();
- EXPECT_EQ(0, PointsBack::alive_count_);
- EXPECT_EQ(0, SuperClass::alive_count_);
- EXPECT_EQ(0, SubClass::alive_count_);
- EXPECT_EQ(0, SubData::alive_count_);
-
- EXPECT_TRUE(super_class == sub_class);
-}
-
-TEST_F(HeapTest, Threading) {
- ThreadedHeapTester::Test();
-}
-
-TEST_F(HeapTest, ThreadedWeakness) {
- ThreadedWeaknessTester::Test();
-}
-
-TEST_F(HeapTest, ThreadPersistent) {
- ThreadPersistentHeapTester::Test();
-}
-
-TEST_F(HeapTest, BasicFunctionality) {
- ThreadHeap& heap = ThreadState::Current()->Heap();
- ClearOutOldGarbage();
- size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
- {
- wtf_size_t slack = 0;
-
- // When the test starts there may already have been leaked some memory
- // on the heap, so we establish a base line.
- size_t base_level = initial_object_payload_size;
- bool test_pages_allocated = !base_level;
- if (test_pages_allocated)
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes());
-
- // This allocates objects on the general heap which should add a page of
- // memory.
- DynamicallySizedObject* alloc32 = DynamicallySizedObject::Create(32);
- slack += 4;
- memset(alloc32, 40, 32);
- DynamicallySizedObject* alloc64 = DynamicallySizedObject::Create(64);
- slack += 4;
- memset(alloc64, 27, 64);
-
- size_t total = 96;
-
- CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
- slack);
- if (test_pages_allocated) {
- EXPECT_EQ(kBlinkPageSize * 2,
- heap.stats_collector()->allocated_space_bytes());
- }
-
- EXPECT_EQ(alloc32->Get(0), 40);
- EXPECT_EQ(alloc32->Get(31), 40);
- EXPECT_EQ(alloc64->Get(0), 27);
- EXPECT_EQ(alloc64->Get(63), 27);
-
- ConservativelyCollectGarbage();
-
- EXPECT_EQ(alloc32->Get(0), 40);
- EXPECT_EQ(alloc32->Get(31), 40);
- EXPECT_EQ(alloc64->Get(0), 27);
- EXPECT_EQ(alloc64->Get(63), 27);
- }
-
- ClearOutOldGarbage();
- size_t total = 0;
- wtf_size_t slack = 0;
- size_t base_level = heap.ObjectPayloadSizeForTesting();
- bool test_pages_allocated = !base_level;
- if (test_pages_allocated)
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes());
-
- size_t big = 1008;
- Persistent<DynamicallySizedObject> big_area =
- DynamicallySizedObject::Create(big);
- total += big;
- slack += 4;
-
- size_t persistent_count = 0;
- const size_t kNumPersistents = 100000;
- Persistent<DynamicallySizedObject>* persistents[kNumPersistents];
-
- for (int i = 0; i < 1000; i++) {
- size_t size = 128 + i * 8;
- total += size;
- persistents[persistent_count++] = new Persistent<DynamicallySizedObject>(
- DynamicallySizedObject::Create(size));
- slack += 4;
- // The allocations in the loop may trigger GC with lazy sweeping.
- CompleteSweepingIfNeeded();
- CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
- slack);
- if (test_pages_allocated) {
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes() &
- (kBlinkPageSize - 1));
- }
- }
-
- {
- DynamicallySizedObject* alloc32b(DynamicallySizedObject::Create(32));
- slack += 4;
- memset(alloc32b, 40, 32);
- DynamicallySizedObject* alloc64b(DynamicallySizedObject::Create(64));
- slack += 4;
- memset(alloc64b, 27, 64);
- EXPECT_TRUE(alloc32b != alloc64b);
-
- total += 96;
- CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(),
- slack);
- if (test_pages_allocated) {
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes() &
- (kBlinkPageSize - 1));
- }
- }
-
- ClearOutOldGarbage();
- total -= 96;
- slack -= 8;
- if (test_pages_allocated) {
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes() &
- (kBlinkPageSize - 1));
- }
-
- // Clear the persistent, so that the big area will be garbage collected.
- big_area.Release();
- ClearOutOldGarbage();
-
- total -= big;
- slack -= 4;
- CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(), slack);
- if (test_pages_allocated) {
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes() &
- (kBlinkPageSize - 1));
- }
-
- CheckWithSlack(base_level + total, heap.ObjectPayloadSizeForTesting(), slack);
- if (test_pages_allocated) {
- EXPECT_EQ(0ul, heap.stats_collector()->allocated_space_bytes() &
- (kBlinkPageSize - 1));
- }
-
- for (size_t i = 0; i < persistent_count; i++) {
- delete persistents[i];
- persistents[i] = nullptr;
- }
-}
-
-TEST_F(HeapTest, SimpleAllocation) {
- ThreadHeap& heap = ThreadState::Current()->Heap();
- ClearOutOldGarbage();
- EXPECT_EQ(0ul, heap.ObjectPayloadSizeForTesting());
-
- // Allocate an object in the heap.
- HeapAllocatedArray* array = MakeGarbageCollected<HeapAllocatedArray>();
- EXPECT_TRUE(heap.ObjectPayloadSizeForTesting() >= sizeof(HeapAllocatedArray));
-
- // Sanity check of the contents in the heap.
- EXPECT_EQ(0, array->at(0));
- EXPECT_EQ(42, array->at(42));
- EXPECT_EQ(0, array->at(128));
- EXPECT_EQ(999 % 128, array->at(999));
-}
-
-TEST_F(HeapTest, SimplePersistent) {
- Persistent<TraceCounter> trace_counter = MakeGarbageCollected<TraceCounter>();
- EXPECT_EQ(0, trace_counter->TraceCount());
- PreciselyCollectGarbage();
- int saved_trace_count = trace_counter->TraceCount();
- EXPECT_LT(0, saved_trace_count);
-
- Persistent<ClassWithMember> class_with_member =
- MakeGarbageCollected<ClassWithMember>();
- EXPECT_EQ(0, class_with_member->TraceCount());
- PreciselyCollectGarbage();
- EXPECT_LT(0, class_with_member->TraceCount());
- EXPECT_LT(saved_trace_count, trace_counter->TraceCount());
-}
-
-TEST_F(HeapTest, SimpleFinalization) {
- ClearOutOldGarbage();
- {
- SimpleFinalizedObject::destructor_calls_ = 0;
- Persistent<SimpleFinalizedObject> finalized =
- MakeGarbageCollected<SimpleFinalizedObject>();
- EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
- PreciselyCollectGarbage();
- EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
- }
-
- PreciselyCollectGarbage();
- EXPECT_EQ(1, SimpleFinalizedObject::destructor_calls_);
-}
-
-#if DCHECK_IS_ON() || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
-TEST_F(HeapTest, FreelistReuse) {
- ClearOutOldGarbage();
-
- for (int i = 0; i < 100; i++)
- MakeGarbageCollected<IntWrapper>(i);
- IntWrapper* p1 = MakeGarbageCollected<IntWrapper>(100);
- PreciselyCollectGarbage();
- // In non-production builds, we delay reusing freed memory for at least
- // one GC cycle.
- for (int i = 0; i < 100; i++) {
- IntWrapper* p2 = MakeGarbageCollected<IntWrapper>(i);
- EXPECT_NE(p1, p2);
- }
-
- PreciselyCollectGarbage();
- PreciselyCollectGarbage();
- // Now the freed memory in the first GC should be reused.
- bool reused_memory_found = false;
- for (int i = 0; i < 10000; i++) {
- IntWrapper* p2 = MakeGarbageCollected<IntWrapper>(i);
- if (p1 == p2) {
- reused_memory_found = true;
- break;
- }
- }
- EXPECT_TRUE(reused_memory_found);
-}
-#endif
-
-TEST_F(HeapTest, LazySweepingPages) {
- ClearOutOldGarbage();
-
- SimpleFinalizedObject::destructor_calls_ = 0;
- EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
- for (int i = 0; i < 1000; i++)
- MakeGarbageCollected<SimpleFinalizedObject>();
- ThreadState::Current()->CollectGarbageForTesting(
- 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++)
- MakeGarbageCollected<SimpleFinalizedObject>();
- EXPECT_EQ(1000, SimpleFinalizedObject::destructor_calls_);
- PreciselyCollectGarbage();
- EXPECT_EQ(11000, SimpleFinalizedObject::destructor_calls_);
-}
-
-TEST_F(HeapTest, LazySweepingLargeObjectPages) {
- // Disable concurrent sweeping to check lazy sweeping on allocation.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(
- blink::features::kBlinkHeapConcurrentSweeping);
-
- ClearOutOldGarbage();
-
- // Create free lists that can be reused for IntWrappers created in
- // MakeGarbageCollected<LargeHeapObject>().
- Persistent<IntWrapper> p1 = MakeGarbageCollected<IntWrapper>(1);
- for (int i = 0; i < 100; i++) {
- MakeGarbageCollected<IntWrapper>(i);
- }
- Persistent<IntWrapper> p2 = MakeGarbageCollected<IntWrapper>(2);
- PreciselyCollectGarbage();
- PreciselyCollectGarbage();
-
- LargeHeapObject::destructor_calls_ = 0;
- EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
- for (int i = 0; i < 10; i++)
- MakeGarbageCollected<LargeHeapObject>();
- ThreadState::Current()->CollectGarbageForTesting(
- 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++) {
- MakeGarbageCollected<LargeHeapObject>();
- EXPECT_EQ(i + 1, LargeHeapObject::destructor_calls_);
- }
- MakeGarbageCollected<LargeHeapObject>();
- MakeGarbageCollected<LargeHeapObject>();
- EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
- ThreadState::Current()->CollectGarbageForTesting(
- BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack,
- BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping,
- BlinkGC::GCReason::kForcedGCForTesting);
- EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
- PreciselyCollectGarbage();
- EXPECT_EQ(22, LargeHeapObject::destructor_calls_);
-}
-
-TEST_F(HeapTest, Finalization) {
- {
- HeapTestSubClass::destructor_calls_ = 0;
- HeapTestSuperClass::destructor_calls_ = 0;
- auto* t1 = MakeGarbageCollected<HeapTestSubClass>();
- auto* t2 = MakeGarbageCollected<HeapTestSubClass>();
- auto* t3 = MakeGarbageCollected<HeapTestSuperClass>();
- // FIXME(oilpan): Ignore unused variables.
- (void)t1;
- (void)t2;
- (void)t3;
- }
- // Nothing is marked so the GC should free everything and call
- // the finalizer on all three objects.
- PreciselyCollectGarbage();
- EXPECT_EQ(2, HeapTestSubClass::destructor_calls_);
- EXPECT_EQ(3, HeapTestSuperClass::destructor_calls_);
- // Destructors not called again when GCing again.
- PreciselyCollectGarbage();
- EXPECT_EQ(2, HeapTestSubClass::destructor_calls_);
- EXPECT_EQ(3, HeapTestSuperClass::destructor_calls_);
-}
-
-TEST_F(HeapTest, TypedArenaSanity) {
- // We use TraceCounter for allocating an object on the general heap.
- Persistent<TraceCounter> general_heap_object =
- MakeGarbageCollected<TraceCounter>();
- Persistent<IntNode> typed_heap_object = IntNode::Create(0);
- EXPECT_NE(PageFromObject(general_heap_object.Get()),
- PageFromObject(typed_heap_object.Get()));
-}
-
-TEST_F(HeapTest, NoAllocation) {
- ThreadState* state = ThreadState::Current();
- EXPECT_TRUE(state->IsAllocationAllowed());
- {
- // Disallow allocation
- ThreadState::NoAllocationScope no_allocation_scope(state);
- EXPECT_FALSE(state->IsAllocationAllowed());
- }
- EXPECT_TRUE(state->IsAllocationAllowed());
-}
-
-TEST_F(HeapTest, Members) {
- ClearOutOldGarbage();
- Bar::live_ = 0;
- {
- Persistent<Baz> h1;
- Persistent<Baz> h2;
- {
- h1 = MakeGarbageCollected<Baz>(MakeGarbageCollected<Bar>());
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, Bar::live_);
- h2 = MakeGarbageCollected<Baz>(MakeGarbageCollected<Bar>());
- PreciselyCollectGarbage();
- EXPECT_EQ(2u, Bar::live_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(2u, Bar::live_);
- h1->Clear();
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, Bar::live_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
-}
-
-TEST_F(HeapTest, MarkTest) {
- ClearOutOldGarbage();
- {
- Bar::live_ = 0;
- Persistent<Bar> bar = MakeGarbageCollected<Bar>();
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(bar));
-#endif
- EXPECT_EQ(1u, Bar::live_);
- {
- auto* foo = MakeGarbageCollected<Foo>(bar);
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
-#endif
- EXPECT_EQ(2u, Bar::live_);
- EXPECT_TRUE(reinterpret_cast<Address>(foo) !=
- reinterpret_cast<Address>(bar.Get()));
- ConservativelyCollectGarbage();
- EXPECT_TRUE(foo != bar); // To make sure foo is kept alive.
- EXPECT_EQ(2u, Bar::live_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, Bar::live_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
-}
-
-TEST_F(HeapTest, DeepTest) {
- ClearOutOldGarbage();
- const unsigned kDepth = 100000;
- Bar::live_ = 0;
- {
- auto* bar = MakeGarbageCollected<Bar>();
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(bar));
-#endif
- auto* foo = MakeGarbageCollected<Foo>(bar);
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
-#endif
- EXPECT_EQ(2u, Bar::live_);
- for (unsigned i = 0; i < kDepth; i++) {
- auto* foo2 = MakeGarbageCollected<Foo>(foo);
- foo = foo2;
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(foo));
-#endif
- }
- EXPECT_EQ(kDepth + 2, Bar::live_);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(foo != bar); // To make sure foo and bar are kept alive.
- EXPECT_EQ(kDepth + 2, Bar::live_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
-}
-
-TEST_F(HeapTest, WideTest) {
- ClearOutOldGarbage();
- Bar::live_ = 0;
- {
- auto* bars = MakeGarbageCollected<Bars>();
- unsigned width = Bars::kWidth;
- EXPECT_EQ(width + 1, Bar::live_);
- ConservativelyCollectGarbage();
- EXPECT_EQ(width + 1, Bar::live_);
- // Use bars here to make sure that it will be on the stack
- // for the conservative stack scan to find.
- EXPECT_EQ(width, bars->GetWidth());
- }
- EXPECT_EQ(Bars::kWidth + 1, Bar::live_);
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
-}
-
-TEST_F(HeapTest, HashMapOfMembers) {
- ClearOutOldGarbage();
- ThreadHeap& heap = ThreadState::Current()->Heap();
- IntWrapper::destructor_calls_ = 0;
- size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
- {
- typedef HeapHashMap<Member<IntWrapper>, Member<IntWrapper>,
- DefaultHash<Member<IntWrapper>>::Hash,
- HashTraits<Member<IntWrapper>>,
- HashTraits<Member<IntWrapper>>>
- HeapObjectIdentityMap;
-
- Persistent<HeapObjectIdentityMap> map =
- MakeGarbageCollected<HeapObjectIdentityMap>();
-
- map->clear();
- size_t after_set_was_created = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(after_set_was_created > initial_object_payload_size);
-
- PreciselyCollectGarbage();
- size_t after_gc = heap.ObjectPayloadSizeForTesting();
- EXPECT_EQ(after_gc, after_set_was_created);
-
- // If the additions below cause garbage collections, these
- // pointers should be found by conservative stack scanning.
- auto* one(MakeGarbageCollected<IntWrapper>(1));
- auto* another_one(MakeGarbageCollected<IntWrapper>(1));
-
- map->insert(one, one);
-
- size_t after_one_add = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(after_one_add > after_gc);
-
- HeapObjectIdentityMap::iterator it(map->begin());
- HeapObjectIdentityMap::iterator it2(map->begin());
- ++it;
- ++it2;
-
- map->insert(another_one, one);
-
- // The addition above can cause an allocation of a new
- // backing store. We therefore garbage collect before
- // taking the heap stats in order to get rid of the old
- // backing store. We make sure to not use conservative
- // stack scanning as that could find a pointer to the
- // old backing.
- PreciselyCollectGarbage();
- size_t after_add_and_gc = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(after_add_and_gc >= after_one_add);
-
- EXPECT_EQ(map->size(), 2u); // Two different wrappings of '1' are distinct.
-
- PreciselyCollectGarbage();
- EXPECT_TRUE(map->Contains(one));
- EXPECT_TRUE(map->Contains(another_one));
-
- IntWrapper* gotten(map->at(one));
- EXPECT_EQ(gotten->Value(), one->Value());
- EXPECT_EQ(gotten, one);
-
- size_t after_gc2 = heap.ObjectPayloadSizeForTesting();
- EXPECT_EQ(after_gc2, after_add_and_gc);
-
- IntWrapper* dozen = nullptr;
-
- for (int i = 1; i < 1000; i++) { // 999 iterations.
- auto* i_wrapper(MakeGarbageCollected<IntWrapper>(i));
- auto* i_squared(MakeGarbageCollected<IntWrapper>(i * i));
- map->insert(i_wrapper, i_squared);
- if (i == 12)
- dozen = i_wrapper;
- }
- size_t after_adding1000 = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(after_adding1000 > after_gc2);
-
- IntWrapper* gross(map->at(dozen));
- EXPECT_EQ(gross->Value(), 144);
-
- // This should clear out any junk backings created by all the adds.
- PreciselyCollectGarbage();
- size_t after_gc3 = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(after_gc3 <= after_adding1000);
- }
-
- PreciselyCollectGarbage();
- // The objects 'one', anotherOne, and the 999 other pairs.
- EXPECT_EQ(IntWrapper::destructor_calls_, 2000);
- size_t after_gc4 = heap.ObjectPayloadSizeForTesting();
- EXPECT_EQ(after_gc4, initial_object_payload_size);
-}
-
-TEST_F(HeapTest, NestedAllocation) {
- ThreadHeap& heap = ThreadState::Current()->Heap();
- ClearOutOldGarbage();
- size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
- {
- Persistent<ConstructorAllocation> constructor_allocation =
- MakeGarbageCollected<ConstructorAllocation>();
- }
- ClearOutOldGarbage();
- size_t after_free = heap.ObjectPayloadSizeForTesting();
- EXPECT_TRUE(initial_object_payload_size == after_free);
-}
-
-TEST_F(HeapTest, LargeHeapObjects) {
- ThreadHeap& heap = ThreadState::Current()->Heap();
- ClearOutOldGarbage();
- size_t initial_object_payload_size = heap.ObjectPayloadSizeForTesting();
- size_t initial_allocated_space =
- heap.stats_collector()->allocated_space_bytes();
- IntWrapper::destructor_calls_ = 0;
- LargeHeapObject::destructor_calls_ = 0;
- {
- int slack =
- 8; // LargeHeapObject points to an IntWrapper that is also allocated.
- Persistent<LargeHeapObject> object =
- MakeGarbageCollected<LargeHeapObject>();
-#if DCHECK_IS_ON()
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(object));
- DCHECK(ThreadState::Current()->Heap().FindPageFromAddress(
- reinterpret_cast<char*>(object.Get()) + sizeof(LargeHeapObject) - 1));
-#endif
- ClearOutOldGarbage();
- size_t after_allocation = heap.stats_collector()->allocated_space_bytes();
- {
- object->Set(0, 'a');
- EXPECT_EQ('a', object->Get(0));
- object->Set(object->length() - 1, 'b');
- EXPECT_EQ('b', object->Get(object->length() - 1));
- size_t expected_large_heap_object_payload_size =
- ThreadHeap::AllocationSizeFromSize(sizeof(LargeHeapObject)) -
- sizeof(HeapObjectHeader);
- size_t expected_object_payload_size =
- expected_large_heap_object_payload_size + sizeof(IntWrapper);
- size_t actual_object_payload_size =
- heap.ObjectPayloadSizeForTesting() - initial_object_payload_size;
- CheckWithSlack(expected_object_payload_size, actual_object_payload_size,
- slack);
- // There is probably space for the IntWrapper in a heap page without
- // allocating extra pages. However, the IntWrapper allocation might cause
- // the addition of a heap page.
- size_t large_object_allocation_size =
- sizeof(LargeObjectPage) + expected_large_heap_object_payload_size;
- size_t allocated_space_lower_bound =
- initial_allocated_space + large_object_allocation_size;
- size_t allocated_space_upper_bound =
- allocated_space_lower_bound + slack + kBlinkPageSize;
- EXPECT_LE(allocated_space_lower_bound, after_allocation);
- EXPECT_LE(after_allocation, allocated_space_upper_bound);
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
- for (int i = 0; i < 10; i++)
- object = MakeGarbageCollected<LargeHeapObject>();
- }
- ClearOutOldGarbage();
- EXPECT_EQ(after_allocation,
- heap.stats_collector()->allocated_space_bytes());
- EXPECT_EQ(10, IntWrapper::destructor_calls_);
- EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
- }
- ClearOutOldGarbage();
- EXPECT_TRUE(initial_object_payload_size ==
- heap.ObjectPayloadSizeForTesting());
- EXPECT_EQ(initial_allocated_space,
- heap.stats_collector()->allocated_space_bytes());
- EXPECT_EQ(11, IntWrapper::destructor_calls_);
- EXPECT_EQ(11, LargeHeapObject::destructor_calls_);
- PreciselyCollectGarbage();
-}
-
-// This test often fails on Android (https://crbug.com/843032).
-// We run out of memory on Android devices because ReserveCapacityForSize
-// actually allocates a much larger backing than specified (in this case 400MB).
-#if defined(OS_ANDROID)
-#define MAYBE_LargeHashMap DISABLED_LargeHashMap
-#else
-#define MAYBE_LargeHashMap LargeHashMap
-#endif
-TEST_F(HeapTest, MAYBE_LargeHashMap) {
- ClearOutOldGarbage();
-
- // Try to allocate a HashTable larger than kMaxHeapObjectSize
- // (crbug.com/597953).
- wtf_size_t size = kMaxHeapObjectSize /
- sizeof(HeapHashMap<int, Member<IntWrapper>>::ValueType);
- Persistent<HeapHashMap<int, Member<IntWrapper>>> map =
- MakeGarbageCollected<HeapHashMap<int, Member<IntWrapper>>>();
- map->ReserveCapacityForSize(size);
- EXPECT_LE(size, map->Capacity());
-}
-
-TEST_F(HeapTest, LargeVector) {
- ClearOutOldGarbage();
-
- // Try to allocate a HeapVectors larger than kMaxHeapObjectSize
- // (crbug.com/597953).
- const wtf_size_t size = kMaxHeapObjectSize / sizeof(Member<IntWrapper>);
- Persistent<HeapVector<Member<IntWrapper>>> vector =
- MakeGarbageCollected<HeapVector<Member<IntWrapper>>>(size);
- EXPECT_LE(size, vector->capacity());
-}
-
-typedef std::pair<Member<IntWrapper>, int> PairWrappedUnwrapped;
-typedef std::pair<int, Member<IntWrapper>> PairUnwrappedWrapped;
-typedef std::pair<WeakMember<IntWrapper>, Member<IntWrapper>> PairWeakStrong;
-typedef std::pair<Member<IntWrapper>, WeakMember<IntWrapper>> PairStrongWeak;
-typedef std::pair<WeakMember<IntWrapper>, int> PairWeakUnwrapped;
-typedef std::pair<int, WeakMember<IntWrapper>> PairUnwrappedWeak;
-
-class Container final : public GarbageCollected<Container> {
- public:
- HeapHashMap<Member<IntWrapper>, Member<IntWrapper>> map;
- HeapHashSet<Member<IntWrapper>> set;
- HeapHashSet<Member<IntWrapper>> set2;
- HeapHashCountedSet<Member<IntWrapper>> set3;
- HeapVector<Member<IntWrapper>, 2> vector;
- HeapVector<PairWrappedUnwrapped, 2> vector_wu;
- HeapVector<PairUnwrappedWrapped, 2> vector_uw;
- HeapDeque<Member<IntWrapper>> deque;
- void Trace(Visitor* visitor) {
- visitor->Trace(map);
- visitor->Trace(set);
- visitor->Trace(set2);
- visitor->Trace(set3);
- visitor->Trace(vector);
- visitor->Trace(vector_wu);
- visitor->Trace(vector_uw);
- visitor->Trace(deque);
- }
-};
-
-struct NeedsTracingTrait {
- explicit NeedsTracingTrait(IntWrapper* wrapper) : wrapper_(wrapper) {}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
- Member<IntWrapper> wrapper_;
-};
-
-TEST_F(HeapTest, HeapVectorFilledWithValue) {
- auto* val = MakeGarbageCollected<IntWrapper>(1);
- HeapVector<Member<IntWrapper>> vector(10, val);
- EXPECT_EQ(10u, vector.size());
- for (wtf_size_t i = 0; i < vector.size(); i++)
- EXPECT_EQ(val, vector[i]);
-}
-
-TEST_F(HeapTest, HeapVectorWithInlineCapacity) {
- auto* one = MakeGarbageCollected<IntWrapper>(1);
- auto* two = MakeGarbageCollected<IntWrapper>(2);
- auto* three = MakeGarbageCollected<IntWrapper>(3);
- auto* four = MakeGarbageCollected<IntWrapper>(4);
- auto* five = MakeGarbageCollected<IntWrapper>(5);
- auto* six = MakeGarbageCollected<IntWrapper>(6);
- {
- HeapVector<Member<IntWrapper>, 2> vector;
- vector.push_back(one);
- vector.push_back(two);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
-
- vector.push_back(three);
- vector.push_back(four);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
- EXPECT_TRUE(vector.Contains(three));
- EXPECT_TRUE(vector.Contains(four));
-
- vector.Shrink(1);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_FALSE(vector.Contains(two));
- EXPECT_FALSE(vector.Contains(three));
- EXPECT_FALSE(vector.Contains(four));
- }
- {
- HeapVector<Member<IntWrapper>, 2> vector1;
- HeapVector<Member<IntWrapper>, 2> vector2;
-
- vector1.push_back(one);
- vector2.push_back(two);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(two));
- EXPECT_TRUE(vector2.Contains(one));
- }
- {
- HeapVector<Member<IntWrapper>, 2> vector1;
- HeapVector<Member<IntWrapper>, 2> vector2;
-
- vector1.push_back(one);
- vector1.push_back(two);
- vector2.push_back(three);
- vector2.push_back(four);
- vector2.push_back(five);
- vector2.push_back(six);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(three));
- EXPECT_TRUE(vector1.Contains(four));
- EXPECT_TRUE(vector1.Contains(five));
- EXPECT_TRUE(vector1.Contains(six));
- EXPECT_TRUE(vector2.Contains(one));
- EXPECT_TRUE(vector2.Contains(two));
- }
-}
-
-TEST_F(HeapTest, HeapVectorShrinkCapacity) {
- ClearOutOldGarbage();
- HeapVector<Member<IntWrapper>> vector1;
- HeapVector<Member<IntWrapper>> vector2;
- vector1.ReserveCapacity(96);
- EXPECT_LE(96u, vector1.capacity());
- vector1.Grow(vector1.capacity());
-
- // Assumes none was allocated just after a vector backing of vector1.
- vector1.Shrink(56);
- vector1.ShrinkToFit();
- EXPECT_GT(96u, vector1.capacity());
-
- vector2.ReserveCapacity(20);
- // Assumes another vector backing was allocated just after the vector
- // backing of vector1.
- vector1.Shrink(10);
- vector1.ShrinkToFit();
- EXPECT_GT(56u, vector1.capacity());
-
- vector1.Grow(192);
- EXPECT_LE(192u, vector1.capacity());
-}
-
-TEST_F(HeapTest, HeapVectorShrinkInlineCapacity) {
- ClearOutOldGarbage();
- const size_t kInlineCapacity = 64;
- HeapVector<Member<IntWrapper>, kInlineCapacity> vector1;
- vector1.ReserveCapacity(128);
- EXPECT_LE(128u, vector1.capacity());
- vector1.Grow(vector1.capacity());
-
- // Shrink the external buffer.
- vector1.Shrink(90);
- vector1.ShrinkToFit();
- EXPECT_GT(128u, vector1.capacity());
-
-// TODO(sof): if the ASan support for 'contiguous containers' is enabled,
-// Vector inline buffers are disabled; that constraint should be attempted
-// removed, but until that time, disable testing handling of capacities
-// of inline buffers.
-#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
- // Shrinking switches the buffer from the external one to the inline one.
- vector1.Shrink(kInlineCapacity - 1);
- vector1.ShrinkToFit();
- EXPECT_EQ(kInlineCapacity, vector1.capacity());
-
- // Try to shrink the inline buffer.
- vector1.Shrink(1);
- vector1.ShrinkToFit();
- EXPECT_EQ(kInlineCapacity, vector1.capacity());
-#endif
-}
-
-TEST_F(HeapTest, HeapVectorOnStackLargeObjectPageSized) {
- ClearOutOldGarbage();
- // Try to allocate a vector of a size that will end exactly where the
- // LargeObjectPage ends.
- using Container = HeapVector<Member<IntWrapper>>;
- Container vector;
- wtf_size_t size =
- (kLargeObjectSizeThreshold + kBlinkGuardPageSize -
- static_cast<wtf_size_t>(LargeObjectPage::PageHeaderSize()) -
- sizeof(HeapObjectHeader)) /
- sizeof(Container::ValueType);
- vector.ReserveCapacity(size);
- for (unsigned i = 0; i < size; ++i)
- vector.push_back(MakeGarbageCollected<IntWrapper>(i));
- ConservativelyCollectGarbage();
-}
-
-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;
- }
- return false;
-}
-
-TEST_F(HeapTest, HeapCollectionTypes) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef HeapHashMap<Member<IntWrapper>, Member<IntWrapper>> MemberMember;
- typedef HeapHashMap<Member<IntWrapper>, int> MemberPrimitive;
- typedef HeapHashMap<int, Member<IntWrapper>> PrimitiveMember;
-
- typedef HeapHashSet<Member<IntWrapper>> MemberSet;
- typedef HeapHashCountedSet<Member<IntWrapper>> MemberCountedSet;
-
- typedef HeapVector<Member<IntWrapper>, 2> MemberVector;
- typedef HeapDeque<Member<IntWrapper>> MemberDeque;
-
- typedef HeapVector<PairWrappedUnwrapped, 2> VectorWU;
- typedef HeapVector<PairUnwrappedWrapped, 2> VectorUW;
-
- Persistent<MemberMember> member_member = MakeGarbageCollected<MemberMember>();
- Persistent<MemberMember> member_member2 =
- MakeGarbageCollected<MemberMember>();
- Persistent<MemberMember> member_member3 =
- MakeGarbageCollected<MemberMember>();
- Persistent<MemberPrimitive> member_primitive =
- MakeGarbageCollected<MemberPrimitive>();
- Persistent<PrimitiveMember> primitive_member =
- MakeGarbageCollected<PrimitiveMember>();
- Persistent<MemberSet> set = MakeGarbageCollected<MemberSet>();
- Persistent<MemberSet> set2 = MakeGarbageCollected<MemberSet>();
- Persistent<MemberCountedSet> set3 = MakeGarbageCollected<MemberCountedSet>();
- Persistent<MemberVector> vector = MakeGarbageCollected<MemberVector>();
- Persistent<MemberVector> vector2 = MakeGarbageCollected<MemberVector>();
- Persistent<VectorWU> vector_wu = MakeGarbageCollected<VectorWU>();
- Persistent<VectorWU> vector_wu2 = MakeGarbageCollected<VectorWU>();
- Persistent<VectorUW> vector_uw = MakeGarbageCollected<VectorUW>();
- Persistent<VectorUW> vector_uw2 = MakeGarbageCollected<VectorUW>();
- Persistent<MemberDeque> deque = MakeGarbageCollected<MemberDeque>();
- Persistent<MemberDeque> deque2 = MakeGarbageCollected<MemberDeque>();
- Persistent<Container> container = MakeGarbageCollected<Container>();
-
- ClearOutOldGarbage();
- {
- Persistent<IntWrapper> one(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> two(MakeGarbageCollected<IntWrapper>(2));
- Persistent<IntWrapper> one_b(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> two_b(MakeGarbageCollected<IntWrapper>(2));
- Persistent<IntWrapper> one_c(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> one_d(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> one_e(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> one_f(MakeGarbageCollected<IntWrapper>(1));
- {
- auto* three_b(MakeGarbageCollected<IntWrapper>(3));
- auto* three_c(MakeGarbageCollected<IntWrapper>(3));
- auto* three_d(MakeGarbageCollected<IntWrapper>(3));
- auto* three_e(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(MakeGarbageCollected<IntWrapper>(4));
- auto* five_c(MakeGarbageCollected<IntWrapper>(5));
- auto* five_d(MakeGarbageCollected<IntWrapper>(5));
-
- // Member Collections.
- member_member2->insert(one, two);
- member_member2->insert(two, three);
- member_member2->insert(three, four);
- member_member2->insert(four, one);
- primitive_member->insert(1, two);
- primitive_member->insert(2, three);
- primitive_member->insert(3, four);
- primitive_member->insert(4, one);
- member_primitive->insert(one, 2);
- member_primitive->insert(two, 3);
- member_primitive->insert(three, 4);
- member_primitive->insert(four, 1);
- set2->insert(one);
- set2->insert(two);
- set2->insert(three);
- set2->insert(four);
- set->insert(one_b);
- set3->insert(one_b);
- set3->insert(one_b);
- vector->push_back(one_b);
- deque->push_back(one_b);
- vector2->push_back(three_b);
- vector2->push_back(four_b);
- deque2->push_back(three_e);
- deque2->push_back(four_e);
- vector_wu->push_back(PairWrappedUnwrapped(&*one_c, 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));
- 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));
-
- EXPECT_TRUE(DequeContains(*deque, one_b));
-
- // Collect garbage. This should change nothing since we are keeping
- // alive the IntWrapper objects with on-stack pointers.
- ConservativelyCollectGarbage();
-
- EXPECT_TRUE(DequeContains(*deque, one_b));
-
- EXPECT_EQ(0u, member_member->size());
- EXPECT_EQ(4u, member_member2->size());
- EXPECT_EQ(4u, primitive_member->size());
- EXPECT_EQ(4u, member_primitive->size());
- EXPECT_EQ(1u, set->size());
- EXPECT_EQ(4u, set2->size());
- EXPECT_EQ(1u, set3->size());
- EXPECT_EQ(1u, vector->size());
- EXPECT_EQ(2u, vector2->size());
- EXPECT_EQ(1u, vector_wu->size());
- EXPECT_EQ(3u, vector_wu2->size());
- EXPECT_EQ(1u, vector_uw->size());
- EXPECT_EQ(3u, vector_uw2->size());
- EXPECT_EQ(1u, deque->size());
- EXPECT_EQ(2u, deque2->size());
-
- MemberVector& cvec = container->vector;
- cvec.swap(*vector.Get());
- vector2->swap(cvec);
- vector->swap(cvec);
-
- VectorWU& cvec_wu = container->vector_wu;
- cvec_wu.swap(*vector_wu.Get());
- vector_wu2->swap(cvec_wu);
- vector_wu->swap(cvec_wu);
-
- VectorUW& cvec_uw = container->vector_uw;
- cvec_uw.swap(*vector_uw.Get());
- vector_uw2->swap(cvec_uw);
- vector_uw->swap(cvec_uw);
-
- MemberDeque& c_deque = container->deque;
- c_deque.Swap(*deque.Get());
- deque2->Swap(c_deque);
- deque->Swap(c_deque);
-
- // Swap set and set2 in a roundabout way.
- MemberSet& cset1 = container->set;
- MemberSet& cset2 = container->set2;
- set->swap(cset1);
- set2->swap(cset2);
- set->swap(cset2);
- cset1.swap(cset2);
- cset2.swap(*set2);
-
- MemberCountedSet& c_counted_set = container->set3;
- set3->swap(c_counted_set);
- EXPECT_EQ(0u, set3->size());
- set3->swap(c_counted_set);
-
- // Triple swap.
- container->map.swap(*member_member2);
- MemberMember& contained_map = container->map;
- member_member3->swap(contained_map);
- member_member3->swap(*member_member);
-
- EXPECT_TRUE(member_member->at(one) == two);
- EXPECT_TRUE(member_member->at(two) == three);
- EXPECT_TRUE(member_member->at(three) == four);
- EXPECT_TRUE(member_member->at(four) == one);
- EXPECT_TRUE(primitive_member->at(1) == two);
- EXPECT_TRUE(primitive_member->at(2) == three);
- EXPECT_TRUE(primitive_member->at(3) == four);
- EXPECT_TRUE(primitive_member->at(4) == one);
- EXPECT_EQ(1, member_primitive->at(four));
- EXPECT_EQ(2, member_primitive->at(one));
- EXPECT_EQ(3, member_primitive->at(two));
- EXPECT_EQ(4, member_primitive->at(three));
- EXPECT_TRUE(set->Contains(one));
- EXPECT_TRUE(set->Contains(two));
- EXPECT_TRUE(set->Contains(three));
- EXPECT_TRUE(set->Contains(four));
- EXPECT_TRUE(set2->Contains(one_b));
- EXPECT_TRUE(set3->Contains(one_b));
- EXPECT_TRUE(vector->Contains(three_b));
- EXPECT_TRUE(vector->Contains(four_b));
- EXPECT_TRUE(DequeContains(*deque, three_e));
- EXPECT_TRUE(DequeContains(*deque, four_e));
- EXPECT_TRUE(vector2->Contains(one_b));
- EXPECT_FALSE(vector2->Contains(three_b));
- EXPECT_TRUE(DequeContains(*deque2, one_b));
- EXPECT_FALSE(DequeContains(*deque2, three_e));
- EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*three_c, 43)));
- EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*four_c, 44)));
- EXPECT_TRUE(vector_wu->Contains(PairWrappedUnwrapped(&*five_c, 45)));
- EXPECT_TRUE(vector_wu2->Contains(PairWrappedUnwrapped(&*one_c, 42)));
- EXPECT_FALSE(vector_wu2->Contains(PairWrappedUnwrapped(&*three_c, 43)));
- EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(103, &*three_d)));
- EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(104, &*four_d)));
- 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)));
- }
-
- PreciselyCollectGarbage();
-
- EXPECT_EQ(4u, member_member->size());
- EXPECT_EQ(0u, member_member2->size());
- EXPECT_EQ(4u, primitive_member->size());
- EXPECT_EQ(4u, member_primitive->size());
- EXPECT_EQ(4u, set->size());
- EXPECT_EQ(1u, set2->size());
- EXPECT_EQ(1u, set3->size());
- EXPECT_EQ(2u, vector->size());
- EXPECT_EQ(1u, vector2->size());
- EXPECT_EQ(3u, vector_uw->size());
- EXPECT_EQ(1u, vector2->size());
- EXPECT_EQ(2u, deque->size());
- EXPECT_EQ(1u, deque2->size());
- EXPECT_EQ(1u, deque2->size());
-
- EXPECT_TRUE(member_member->at(one) == two);
- EXPECT_TRUE(primitive_member->at(1) == two);
- EXPECT_TRUE(primitive_member->at(4) == one);
- EXPECT_EQ(2, member_primitive->at(one));
- EXPECT_EQ(3, member_primitive->at(two));
- EXPECT_TRUE(set->Contains(one));
- EXPECT_TRUE(set->Contains(two));
- EXPECT_FALSE(set->Contains(one_b));
- EXPECT_TRUE(set2->Contains(one_b));
- EXPECT_TRUE(set3->Contains(one_b));
- EXPECT_EQ(2u, set3->find(one_b)->value);
- EXPECT_EQ(3, vector->at(0)->Value());
- EXPECT_EQ(4, vector->at(1)->Value());
- EXPECT_EQ(3, deque->begin()->Get()->Value());
- }
-
- PreciselyCollectGarbage();
- PreciselyCollectGarbage();
-
- EXPECT_EQ(4u, member_member->size());
- EXPECT_EQ(4u, primitive_member->size());
- EXPECT_EQ(4u, member_primitive->size());
- EXPECT_EQ(4u, set->size());
- EXPECT_EQ(1u, set2->size());
- EXPECT_EQ(2u, vector->size());
- EXPECT_EQ(1u, vector2->size());
- EXPECT_EQ(3u, vector_wu->size());
- EXPECT_EQ(1u, vector_wu2->size());
- EXPECT_EQ(3u, vector_uw->size());
- EXPECT_EQ(1u, vector_uw2->size());
- EXPECT_EQ(2u, deque->size());
- EXPECT_EQ(1u, deque2->size());
-}
-
-TEST_F(HeapTest, PersistentVector) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef Vector<Persistent<IntWrapper>> PersistentVector;
-
- Persistent<IntWrapper> one(MakeGarbageCollected<IntWrapper>(1));
- Persistent<IntWrapper> two(MakeGarbageCollected<IntWrapper>(2));
- Persistent<IntWrapper> three(MakeGarbageCollected<IntWrapper>(3));
- Persistent<IntWrapper> four(MakeGarbageCollected<IntWrapper>(4));
- Persistent<IntWrapper> five(MakeGarbageCollected<IntWrapper>(5));
- Persistent<IntWrapper> six(MakeGarbageCollected<IntWrapper>(6));
- {
- PersistentVector vector;
- vector.push_back(one);
- vector.push_back(two);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
-
- vector.push_back(three);
- vector.push_back(four);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
- EXPECT_TRUE(vector.Contains(three));
- EXPECT_TRUE(vector.Contains(four));
-
- vector.Shrink(1);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_FALSE(vector.Contains(two));
- EXPECT_FALSE(vector.Contains(three));
- EXPECT_FALSE(vector.Contains(four));
- }
- {
- PersistentVector vector1;
- PersistentVector vector2;
-
- vector1.push_back(one);
- vector2.push_back(two);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(two));
- EXPECT_TRUE(vector2.Contains(one));
- }
- {
- PersistentVector vector1;
- PersistentVector vector2;
-
- vector1.push_back(one);
- vector1.push_back(two);
- vector2.push_back(three);
- vector2.push_back(four);
- vector2.push_back(five);
- vector2.push_back(six);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(three));
- EXPECT_TRUE(vector1.Contains(four));
- EXPECT_TRUE(vector1.Contains(five));
- EXPECT_TRUE(vector1.Contains(six));
- EXPECT_TRUE(vector2.Contains(one));
- EXPECT_TRUE(vector2.Contains(two));
- }
-}
-
-TEST_F(HeapTest, CrossThreadPersistentVector) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef Vector<CrossThreadPersistent<IntWrapper>> CrossThreadPersistentVector;
-
- CrossThreadPersistent<IntWrapper> one(MakeGarbageCollected<IntWrapper>(1));
- CrossThreadPersistent<IntWrapper> two(MakeGarbageCollected<IntWrapper>(2));
- CrossThreadPersistent<IntWrapper> three(MakeGarbageCollected<IntWrapper>(3));
- CrossThreadPersistent<IntWrapper> four(MakeGarbageCollected<IntWrapper>(4));
- CrossThreadPersistent<IntWrapper> five(MakeGarbageCollected<IntWrapper>(5));
- CrossThreadPersistent<IntWrapper> six(MakeGarbageCollected<IntWrapper>(6));
- {
- CrossThreadPersistentVector vector;
- vector.push_back(one);
- vector.push_back(two);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
-
- vector.push_back(three);
- vector.push_back(four);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_TRUE(vector.Contains(two));
- EXPECT_TRUE(vector.Contains(three));
- EXPECT_TRUE(vector.Contains(four));
-
- vector.Shrink(1);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector.Contains(one));
- EXPECT_FALSE(vector.Contains(two));
- EXPECT_FALSE(vector.Contains(three));
- EXPECT_FALSE(vector.Contains(four));
- }
- {
- CrossThreadPersistentVector vector1;
- CrossThreadPersistentVector vector2;
-
- vector1.push_back(one);
- vector2.push_back(two);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(two));
- EXPECT_TRUE(vector2.Contains(one));
- }
- {
- CrossThreadPersistentVector vector1;
- CrossThreadPersistentVector vector2;
-
- vector1.push_back(one);
- vector1.push_back(two);
- vector2.push_back(three);
- vector2.push_back(four);
- vector2.push_back(five);
- vector2.push_back(six);
- vector1.swap(vector2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(vector1.Contains(three));
- EXPECT_TRUE(vector1.Contains(four));
- EXPECT_TRUE(vector1.Contains(five));
- EXPECT_TRUE(vector1.Contains(six));
- EXPECT_TRUE(vector2.Contains(one));
- EXPECT_TRUE(vector2.Contains(two));
- }
-}
-
-TEST_F(HeapTest, PersistentSet) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef HashSet<Persistent<IntWrapper>> PersistentSet;
-
- auto* one_raw = MakeGarbageCollected<IntWrapper>(1);
- Persistent<IntWrapper> one(one_raw);
- Persistent<IntWrapper> one2(one_raw);
- Persistent<IntWrapper> two(MakeGarbageCollected<IntWrapper>(2));
- Persistent<IntWrapper> three(MakeGarbageCollected<IntWrapper>(3));
- Persistent<IntWrapper> four(MakeGarbageCollected<IntWrapper>(4));
- Persistent<IntWrapper> five(MakeGarbageCollected<IntWrapper>(5));
- Persistent<IntWrapper> six(MakeGarbageCollected<IntWrapper>(6));
- {
- PersistentSet set;
- set.insert(one);
- set.insert(two);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set.Contains(one));
- EXPECT_TRUE(set.Contains(one2));
- EXPECT_TRUE(set.Contains(two));
-
- set.insert(three);
- set.insert(four);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set.Contains(one));
- EXPECT_TRUE(set.Contains(two));
- EXPECT_TRUE(set.Contains(three));
- EXPECT_TRUE(set.Contains(four));
-
- set.clear();
- ConservativelyCollectGarbage();
- EXPECT_FALSE(set.Contains(one));
- EXPECT_FALSE(set.Contains(two));
- EXPECT_FALSE(set.Contains(three));
- EXPECT_FALSE(set.Contains(four));
- }
- {
- PersistentSet set1;
- PersistentSet set2;
-
- set1.insert(one);
- set2.insert(two);
- set1.swap(set2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set1.Contains(two));
- EXPECT_TRUE(set2.Contains(one));
- EXPECT_TRUE(set2.Contains(one2));
- }
-}
-
-TEST_F(HeapTest, CrossThreadPersistentSet) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef HashSet<CrossThreadPersistent<IntWrapper>> CrossThreadPersistentSet;
-
- auto* one_raw = MakeGarbageCollected<IntWrapper>(1);
- CrossThreadPersistent<IntWrapper> one(one_raw);
- CrossThreadPersistent<IntWrapper> one2(one_raw);
- CrossThreadPersistent<IntWrapper> two(MakeGarbageCollected<IntWrapper>(2));
- CrossThreadPersistent<IntWrapper> three(MakeGarbageCollected<IntWrapper>(3));
- CrossThreadPersistent<IntWrapper> four(MakeGarbageCollected<IntWrapper>(4));
- CrossThreadPersistent<IntWrapper> five(MakeGarbageCollected<IntWrapper>(5));
- CrossThreadPersistent<IntWrapper> six(MakeGarbageCollected<IntWrapper>(6));
- {
- CrossThreadPersistentSet set;
- set.insert(one);
- set.insert(two);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set.Contains(one));
- EXPECT_TRUE(set.Contains(one2));
- EXPECT_TRUE(set.Contains(two));
-
- set.insert(three);
- set.insert(four);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set.Contains(one));
- EXPECT_TRUE(set.Contains(two));
- EXPECT_TRUE(set.Contains(three));
- EXPECT_TRUE(set.Contains(four));
-
- set.clear();
- ConservativelyCollectGarbage();
- EXPECT_FALSE(set.Contains(one));
- EXPECT_FALSE(set.Contains(two));
- EXPECT_FALSE(set.Contains(three));
- EXPECT_FALSE(set.Contains(four));
- }
- {
- CrossThreadPersistentSet set1;
- CrossThreadPersistentSet set2;
-
- set1.insert(one);
- set2.insert(two);
- set1.swap(set2);
- ConservativelyCollectGarbage();
- EXPECT_TRUE(set1.Contains(two));
- EXPECT_TRUE(set2.Contains(one));
- EXPECT_TRUE(set2.Contains(one2));
- }
-}
-
-class NonTrivialObject final {
- DISALLOW_NEW();
-
- public:
- NonTrivialObject() = default;
- explicit NonTrivialObject(int num) {
- deque_.push_back(MakeGarbageCollected<IntWrapper>(num));
- vector_.push_back(MakeGarbageCollected<IntWrapper>(num));
- }
- void Trace(Visitor* visitor) {
- visitor->Trace(deque_);
- visitor->Trace(vector_);
- }
-
- private:
- HeapDeque<Member<IntWrapper>> deque_;
- HeapVector<Member<IntWrapper>> vector_;
-};
-
-TEST_F(HeapTest, HeapHashMapWithInlinedObject) {
- HeapHashMap<int, NonTrivialObject> map;
- for (int num = 1; num < 1000; num++) {
- NonTrivialObject object(num);
- map.insert(num, object);
- }
-}
-
-template <typename T>
-void MapIteratorCheck(T& it, const T& end, int expected) {
- int found = 0;
- while (it != end) {
- found++;
- int key = it->key->Value();
- int value = it->value->Value();
- EXPECT_TRUE(key >= 0 && key < 1100);
- EXPECT_TRUE(value >= 0 && value < 1100);
- ++it;
- }
- EXPECT_EQ(expected, found);
-}
-
-template <typename T>
-void SetIteratorCheck(T& it, const T& end, int expected) {
- int found = 0;
- while (it != end) {
- found++;
- int value = (*it)->Value();
- EXPECT_TRUE(value >= 0 && value < 1100);
- ++it;
- }
- EXPECT_EQ(expected, found);
-}
-
-TEST_F(HeapTest, HeapWeakCollectionSimple) {
- ClearOutOldGarbage();
- IntWrapper::destructor_calls_ = 0;
-
- Persistent<HeapVector<Member<IntWrapper>>> keep_numbers_alive =
- MakeGarbageCollected<HeapVector<Member<IntWrapper>>>();
-
- typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> WeakStrong;
- typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeak;
- typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper>> WeakWeak;
- typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
- typedef HeapHashCountedSet<WeakMember<IntWrapper>> WeakCountedSet;
-
- Persistent<WeakStrong> weak_strong = MakeGarbageCollected<WeakStrong>();
- Persistent<StrongWeak> strong_weak = MakeGarbageCollected<StrongWeak>();
- Persistent<WeakWeak> weak_weak = MakeGarbageCollected<WeakWeak>();
- Persistent<WeakSet> weak_set = MakeGarbageCollected<WeakSet>();
- Persistent<WeakCountedSet> weak_counted_set =
- MakeGarbageCollected<WeakCountedSet>();
-
- Persistent<IntWrapper> two = MakeGarbageCollected<IntWrapper>(2);
-
- keep_numbers_alive->push_back(MakeGarbageCollected<IntWrapper>(103));
- keep_numbers_alive->push_back(MakeGarbageCollected<IntWrapper>(10));
-
- {
- weak_strong->insert(MakeGarbageCollected<IntWrapper>(1), two);
- strong_weak->insert(two, MakeGarbageCollected<IntWrapper>(1));
- weak_weak->insert(two, MakeGarbageCollected<IntWrapper>(42));
- weak_weak->insert(MakeGarbageCollected<IntWrapper>(42), two);
- weak_set->insert(MakeGarbageCollected<IntWrapper>(0));
- weak_set->insert(two);
- weak_set->insert(keep_numbers_alive->at(0));
- weak_set->insert(keep_numbers_alive->at(1));
- weak_counted_set->insert(MakeGarbageCollected<IntWrapper>(0));
- weak_counted_set->insert(two);
- weak_counted_set->insert(two);
- weak_counted_set->insert(two);
- weak_counted_set->insert(keep_numbers_alive->at(0));
- weak_counted_set->insert(keep_numbers_alive->at(1));
- EXPECT_EQ(1u, weak_strong->size());
- EXPECT_EQ(1u, strong_weak->size());
- EXPECT_EQ(2u, weak_weak->size());
- EXPECT_EQ(4u, weak_set->size());
- EXPECT_EQ(4u, weak_counted_set->size());
- EXPECT_EQ(3u, weak_counted_set->find(two)->value);
- weak_counted_set->erase(two);
- EXPECT_EQ(2u, weak_counted_set->find(two)->value);
- }
-
- keep_numbers_alive->at(0) = nullptr;
-
- PreciselyCollectGarbage();
-
- EXPECT_EQ(0u, weak_strong->size());
- EXPECT_EQ(0u, strong_weak->size());
- EXPECT_EQ(0u, weak_weak->size());
- EXPECT_EQ(2u, weak_set->size());
- EXPECT_EQ(2u, weak_counted_set->size());
-}
-
-template <typename Set>
-void OrderedSetHelper(bool strong) {
- IntWrapper::destructor_calls_ = 0;
-
- Persistent<HeapVector<Member<IntWrapper>>> keep_numbers_alive =
- MakeGarbageCollected<HeapVector<Member<IntWrapper>>>();
-
- Persistent<Set> set1 = MakeGarbageCollected<Set>();
- Persistent<Set> set2 = MakeGarbageCollected<Set>();
-
- const Set& const_set = *set1.Get();
-
- keep_numbers_alive->push_back(MakeGarbageCollected<IntWrapper>(2));
- keep_numbers_alive->push_back(MakeGarbageCollected<IntWrapper>(103));
- keep_numbers_alive->push_back(MakeGarbageCollected<IntWrapper>(10));
-
- set1->insert(MakeGarbageCollected<IntWrapper>(0));
- set1->insert(keep_numbers_alive->at(0));
- set1->insert(keep_numbers_alive->at(1));
- set1->insert(keep_numbers_alive->at(2));
-
- set2->clear();
- set2->insert(MakeGarbageCollected<IntWrapper>(42));
- set2->clear();
-
- EXPECT_EQ(4u, set1->size());
- typename Set::iterator it(set1->begin());
- typename Set::reverse_iterator reverse(set1->rbegin());
- typename Set::const_iterator cit(const_set.begin());
- typename Set::const_reverse_iterator creverse(const_set.rbegin());
-
- EXPECT_EQ(0, (*it)->Value());
- EXPECT_EQ(0, (*cit)->Value());
- ++it;
- ++cit;
- EXPECT_EQ(2, (*it)->Value());
- EXPECT_EQ(2, (*cit)->Value());
- --it;
- --cit;
- EXPECT_EQ(0, (*it)->Value());
- EXPECT_EQ(0, (*cit)->Value());
- ++it;
- ++cit;
- ++it;
- ++cit;
- EXPECT_EQ(103, (*it)->Value());
- EXPECT_EQ(103, (*cit)->Value());
- ++it;
- ++cit;
- EXPECT_EQ(10, (*it)->Value());
- EXPECT_EQ(10, (*cit)->Value());
- ++it;
- ++cit;
-
- EXPECT_EQ(10, (*reverse)->Value());
- EXPECT_EQ(10, (*creverse)->Value());
- ++reverse;
- ++creverse;
- EXPECT_EQ(103, (*reverse)->Value());
- EXPECT_EQ(103, (*creverse)->Value());
- --reverse;
- --creverse;
- EXPECT_EQ(10, (*reverse)->Value());
- EXPECT_EQ(10, (*creverse)->Value());
- ++reverse;
- ++creverse;
- ++reverse;
- ++creverse;
- EXPECT_EQ(2, (*reverse)->Value());
- EXPECT_EQ(2, (*creverse)->Value());
- ++reverse;
- ++creverse;
- EXPECT_EQ(0, (*reverse)->Value());
- EXPECT_EQ(0, (*creverse)->Value());
- ++reverse;
- ++creverse;
-
- EXPECT_EQ(set1->end(), it);
- EXPECT_EQ(const_set.end(), cit);
- EXPECT_EQ(set1->rend(), reverse);
- EXPECT_EQ(const_set.rend(), creverse);
-
- typename Set::iterator i_x(set2->begin());
- EXPECT_EQ(set2->end(), i_x);
-
- if (strong)
- set1->erase(keep_numbers_alive->at(0));
-
- keep_numbers_alive->at(0) = nullptr;
-
- TestSupportingGC::PreciselyCollectGarbage();
-
- EXPECT_EQ(2u + (strong ? 1u : 0u), set1->size());
-
- EXPECT_EQ(2 + (strong ? 0 : 1), IntWrapper::destructor_calls_);
-
- typename Set::iterator i2(set1->begin());
- if (strong) {
- EXPECT_EQ(0, (*i2)->Value());
- ++i2;
- EXPECT_NE(set1->end(), i2);
- }
- EXPECT_EQ(103, (*i2)->Value());
- ++i2;
- EXPECT_NE(set1->end(), i2);
- EXPECT_EQ(10, (*i2)->Value());
- ++i2;
- EXPECT_EQ(set1->end(), i2);
-}
-
-TEST_F(HeapTest, HeapWeakLinkedHashSet) {
- ClearOutOldGarbage();
- OrderedSetHelper<HeapLinkedHashSet<Member<IntWrapper>>>(true);
- ClearOutOldGarbage();
- 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 {
- DISALLOW_NEW();
-
- public:
- ThingWithDestructor() : x_(kEmptyValue) { live_things_with_destructor_++; }
-
- ThingWithDestructor(int x) : x_(x) { live_things_with_destructor_++; }
-
- ThingWithDestructor(const ThingWithDestructor& other) {
- *this = other;
- live_things_with_destructor_++;
- }
-
- ~ThingWithDestructor() { live_things_with_destructor_--; }
-
- int Value() { return x_; }
-
- static int live_things_with_destructor_;
-
- unsigned GetHash() { return IntHash<int>::GetHash(x_); }
-
- private:
- static const int kEmptyValue = 0;
- int x_;
-};
-
-int ThingWithDestructor::live_things_with_destructor_;
-
-static void HeapMapDestructorHelper(bool clear_maps) {
- ThingWithDestructor::live_things_with_destructor_ = 0;
-
- typedef HeapHashMap<WeakMember<IntWrapper>,
- Member<RefCountedAndGarbageCollected>>
- RefMap;
-
- typedef HeapHashMap<WeakMember<IntWrapper>, ThingWithDestructor,
- DefaultHash<WeakMember<IntWrapper>>::Hash,
- HashTraits<WeakMember<IntWrapper>>>
- Map;
-
- Persistent<Map> map(MakeGarbageCollected<Map>());
- Persistent<RefMap> ref_map(MakeGarbageCollected<RefMap>());
-
- Persistent<IntWrapper> luck(MakeGarbageCollected<IntWrapper>(103));
-
- int base_line, ref_base_line;
-
- {
- Map stack_map;
- RefMap stack_ref_map;
-
- TestSupportingGC::PreciselyCollectGarbage();
- TestSupportingGC::PreciselyCollectGarbage();
-
- stack_map.insert(MakeGarbageCollected<IntWrapper>(42),
- ThingWithDestructor(1729));
- stack_map.insert(luck, ThingWithDestructor(8128));
- stack_ref_map.insert(MakeGarbageCollected<IntWrapper>(42),
- MakeGarbageCollected<RefCountedAndGarbageCollected>());
- stack_ref_map.insert(luck,
- MakeGarbageCollected<RefCountedAndGarbageCollected>());
-
- base_line = ThingWithDestructor::live_things_with_destructor_;
- ref_base_line = RefCountedAndGarbageCollected::destructor_calls_;
-
- // Although the heap maps are on-stack, we can't expect prompt
- // finalization of the elements, so when they go out of scope here we
- // will not necessarily have called the relevant destructors.
- }
-
- // The RefCountedAndGarbageCollected things need an extra GC to discover
- // that they are no longer ref counted.
- TestSupportingGC::PreciselyCollectGarbage();
- TestSupportingGC::PreciselyCollectGarbage();
- EXPECT_EQ(base_line - 2, ThingWithDestructor::live_things_with_destructor_);
- EXPECT_EQ(ref_base_line + 2,
- RefCountedAndGarbageCollected::destructor_calls_);
-
- // Now use maps kept alive with persistents. Here we don't expect any
- // destructors to be called before there have been GCs.
-
- map->insert(MakeGarbageCollected<IntWrapper>(42), ThingWithDestructor(1729));
- map->insert(luck, ThingWithDestructor(8128));
- ref_map->insert(MakeGarbageCollected<IntWrapper>(42),
- MakeGarbageCollected<RefCountedAndGarbageCollected>());
- ref_map->insert(luck, MakeGarbageCollected<RefCountedAndGarbageCollected>());
-
- base_line = ThingWithDestructor::live_things_with_destructor_;
- ref_base_line = RefCountedAndGarbageCollected::destructor_calls_;
-
- luck.Clear();
- if (clear_maps) {
- map->clear(); // Clear map.
- ref_map->clear(); // Clear map.
- } else {
- map.Clear(); // Clear Persistent handle, not map.
- ref_map.Clear(); // Clear Persistent handle, not map.
- TestSupportingGC::PreciselyCollectGarbage();
- TestSupportingGC::PreciselyCollectGarbage();
- }
-
- EXPECT_EQ(base_line - 2, ThingWithDestructor::live_things_with_destructor_);
-
- // Need a GC to make sure that the RefCountedAndGarbageCollected thing
- // noticies it's been decremented to zero.
- TestSupportingGC::PreciselyCollectGarbage();
- EXPECT_EQ(ref_base_line + 2,
- RefCountedAndGarbageCollected::destructor_calls_);
-}
-
-TEST_F(HeapTest, HeapMapDestructor) {
- ClearOutOldGarbage();
- HeapMapDestructorHelper(true);
- ClearOutOldGarbage();
- HeapMapDestructorHelper(false);
-}
-
-typedef HeapHashSet<PairWeakStrong> WeakStrongSet;
-typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet;
-typedef HeapHashSet<PairStrongWeak> StrongWeakSet;
-typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet;
-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;
-typedef HeapHashCountedSet<PairUnwrappedWeak> UnwrappedWeakCountedSet;
-
-template <typename T>
-T& IteratorExtractor(WTF::KeyValuePair<T, unsigned>& pair) {
- return pair.key;
-}
-
-template <typename T>
-T& IteratorExtractor(T& not_a_pair) {
- return not_a_pair;
-}
-
-template <typename WSSet, typename SWSet, typename WUSet, typename UWSet>
-void CheckPairSets(Persistent<WSSet>& weak_strong,
- Persistent<SWSet>& strong_weak,
- Persistent<WUSet>& weak_unwrapped,
- Persistent<UWSet>& unwrapped_weak,
- bool ones,
- Persistent<IntWrapper>& two) {
- typename WSSet::iterator it_ws = weak_strong->begin();
- typename SWSet::iterator it_sw = strong_weak->begin();
- typename WUSet::iterator it_wu = weak_unwrapped->begin();
- typename UWSet::iterator it_uw = unwrapped_weak->begin();
-
- EXPECT_EQ(2u, weak_strong->size());
- EXPECT_EQ(2u, strong_weak->size());
- EXPECT_EQ(2u, weak_unwrapped->size());
- EXPECT_EQ(2u, unwrapped_weak->size());
-
- PairWeakStrong p = IteratorExtractor(*it_ws);
- PairStrongWeak p2 = IteratorExtractor(*it_sw);
- PairWeakUnwrapped p3 = IteratorExtractor(*it_wu);
- PairUnwrappedWeak p4 = IteratorExtractor(*it_uw);
- if (p.first == two && p.second == two)
- ++it_ws;
- if (p2.first == two && p2.second == two)
- ++it_sw;
- if (p3.first == two && p3.second == 2)
- ++it_wu;
- if (p4.first == 2 && p4.second == two)
- ++it_uw;
- p = IteratorExtractor(*it_ws);
- p2 = IteratorExtractor(*it_sw);
- p3 = IteratorExtractor(*it_wu);
- p4 = IteratorExtractor(*it_uw);
- IntWrapper* null_wrapper = nullptr;
- if (ones) {
- EXPECT_EQ(p.first->Value(), 1);
- EXPECT_EQ(p2.second->Value(), 1);
- EXPECT_EQ(p3.first->Value(), 1);
- EXPECT_EQ(p4.second->Value(), 1);
- } else {
- EXPECT_EQ(p.first, null_wrapper);
- EXPECT_EQ(p2.second, null_wrapper);
- EXPECT_EQ(p3.first, null_wrapper);
- EXPECT_EQ(p4.second, null_wrapper);
- }
-
- EXPECT_EQ(p.second->Value(), 2);
- EXPECT_EQ(p2.first->Value(), 2);
- EXPECT_EQ(p3.second, 2);
- EXPECT_EQ(p4.first, 2);
-
- EXPECT_TRUE(weak_strong->Contains(PairWeakStrong(&*two, &*two)));
- EXPECT_TRUE(strong_weak->Contains(PairStrongWeak(&*two, &*two)));
- EXPECT_TRUE(weak_unwrapped->Contains(PairWeakUnwrapped(&*two, 2)));
- EXPECT_TRUE(unwrapped_weak->Contains(PairUnwrappedWeak(2, &*two)));
-}
-
-TEST_F(HeapTest, HeapWeakCollectionTypes) {
- IntWrapper::destructor_calls_ = 0;
-
- typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> WeakStrong;
- typedef HeapHashMap<Member<IntWrapper>, WeakMember<IntWrapper>> StrongWeak;
- 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();
-
- const int kWeakStrongIndex = 0;
- const int kStrongWeakIndex = 1;
- const int kWeakWeakIndex = 2;
- const int kNumberOfMapIndices = 3;
- const int kWeakSetIndex = 3;
- const int kWeakOrderedSetIndex = 4;
- const int kNumberOfCollections = 5;
-
- for (int test_run = 0; test_run < 4; test_run++) {
- for (int collection_number = 0; collection_number < kNumberOfCollections;
- collection_number++) {
- bool delete_afterwards = (test_run == 1);
- bool add_afterwards = (test_run == 2);
- bool test_that_iterators_make_strong = (test_run == 3);
-
- // The test doesn't work for strongWeak with deleting because we lost
- // the key from the keepNumbersAlive array, so we can't do the lookup.
- if (delete_afterwards && collection_number == kStrongWeakIndex)
- continue;
-
- unsigned added = add_afterwards ? 100 : 0;
-
- Persistent<WeakStrong> weak_strong = MakeGarbageCollected<WeakStrong>();
- Persistent<StrongWeak> strong_weak = MakeGarbageCollected<StrongWeak>();
- Persistent<WeakWeak> weak_weak = MakeGarbageCollected<WeakWeak>();
-
- Persistent<WeakSet> weak_set = MakeGarbageCollected<WeakSet>();
- Persistent<WeakOrderedSet> weak_ordered_set =
- MakeGarbageCollected<WeakOrderedSet>();
-
- Persistent<HeapVector<Member<IntWrapper>>> keep_numbers_alive =
- MakeGarbageCollected<HeapVector<Member<IntWrapper>>>();
- for (int i = 0; i < 128; i += 2) {
- auto* wrapped = MakeGarbageCollected<IntWrapper>(i);
- auto* wrapped2 = MakeGarbageCollected<IntWrapper>(i + 1);
- keep_numbers_alive->push_back(wrapped);
- keep_numbers_alive->push_back(wrapped2);
- weak_strong->insert(wrapped, wrapped2);
- strong_weak->insert(wrapped2, wrapped);
- weak_weak->insert(wrapped, wrapped2);
- weak_set->insert(wrapped);
- weak_ordered_set->insert(wrapped);
- }
-
- EXPECT_EQ(64u, weak_strong->size());
- EXPECT_EQ(64u, strong_weak->size());
- EXPECT_EQ(64u, weak_weak->size());
- EXPECT_EQ(64u, weak_set->size());
- EXPECT_EQ(64u, weak_ordered_set->size());
-
- // Collect garbage. This should change nothing since we are keeping
- // alive the IntWrapper objects.
- PreciselyCollectGarbage();
-
- EXPECT_EQ(64u, weak_strong->size());
- EXPECT_EQ(64u, strong_weak->size());
- EXPECT_EQ(64u, weak_weak->size());
- EXPECT_EQ(64u, weak_set->size());
- EXPECT_EQ(64u, weak_ordered_set->size());
-
- for (int i = 0; i < 128; i += 2) {
- IntWrapper* wrapped = keep_numbers_alive->at(i);
- IntWrapper* wrapped2 = keep_numbers_alive->at(i + 1);
- EXPECT_EQ(wrapped2, weak_strong->at(wrapped));
- EXPECT_EQ(wrapped, strong_weak->at(wrapped2));
- EXPECT_EQ(wrapped2, weak_weak->at(wrapped));
- EXPECT_TRUE(weak_set->Contains(wrapped));
- EXPECT_TRUE(weak_ordered_set->Contains(wrapped));
- }
-
- for (int i = 0; i < 128; i += 3)
- keep_numbers_alive->at(i) = nullptr;
-
- if (collection_number != kWeakStrongIndex)
- weak_strong->clear();
- if (collection_number != kStrongWeakIndex)
- strong_weak->clear();
- if (collection_number != kWeakWeakIndex)
- weak_weak->clear();
- if (collection_number != kWeakSetIndex)
- weak_set->clear();
- if (collection_number != kWeakOrderedSetIndex)
- weak_ordered_set->clear();
-
- if (test_that_iterators_make_strong) {
- WeakStrong::iterator it1 = weak_strong->begin();
- StrongWeak::iterator it2 = strong_weak->begin();
- WeakWeak::iterator it3 = weak_weak->begin();
- WeakSet::iterator it4 = weak_set->begin();
- WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
- // Collect garbage. This should change nothing since the
- // iterators make the collections strong.
- ConservativelyCollectGarbage();
- if (collection_number == kWeakStrongIndex) {
- EXPECT_EQ(64u, weak_strong->size());
- MapIteratorCheck(it1, weak_strong->end(), 64);
- } else if (collection_number == kStrongWeakIndex) {
- EXPECT_EQ(64u, strong_weak->size());
- MapIteratorCheck(it2, strong_weak->end(), 64);
- } else if (collection_number == kWeakWeakIndex) {
- EXPECT_EQ(64u, weak_weak->size());
- MapIteratorCheck(it3, weak_weak->end(), 64);
- } else if (collection_number == kWeakSetIndex) {
- EXPECT_EQ(64u, weak_set->size());
- SetIteratorCheck(it4, weak_set->end(), 64);
- } else if (collection_number == kWeakOrderedSetIndex) {
- EXPECT_EQ(64u, weak_ordered_set->size());
- SetIteratorCheck(it5, weak_ordered_set->end(), 64);
- }
- } else {
- // Collect garbage. This causes weak processing to remove
- // things from the collections.
- PreciselyCollectGarbage();
- unsigned count = 0;
- for (int i = 0; i < 128; i += 2) {
- bool first_alive = keep_numbers_alive->at(i);
- bool second_alive = keep_numbers_alive->at(i + 1);
- if (first_alive && (collection_number == kWeakStrongIndex ||
- collection_number == kStrongWeakIndex))
- second_alive = true;
- if (first_alive && second_alive &&
- collection_number < kNumberOfMapIndices) {
- if (collection_number == kWeakStrongIndex) {
- if (delete_afterwards) {
- EXPECT_EQ(
- i + 1,
- weak_strong->Take(keep_numbers_alive->at(i))->Value());
- }
- } else if (collection_number == kStrongWeakIndex) {
- if (delete_afterwards) {
- EXPECT_EQ(
- i,
- strong_weak->Take(keep_numbers_alive->at(i + 1))->Value());
- }
- } else if (collection_number == kWeakWeakIndex) {
- if (delete_afterwards) {
- EXPECT_EQ(i + 1,
- weak_weak->Take(keep_numbers_alive->at(i))->Value());
- }
- }
- if (!delete_afterwards)
- count++;
- } else if (collection_number == kWeakSetIndex && first_alive) {
- ASSERT_TRUE(weak_set->Contains(keep_numbers_alive->at(i)));
- if (delete_afterwards)
- weak_set->erase(keep_numbers_alive->at(i));
- else
- count++;
- } else if (collection_number == kWeakOrderedSetIndex && first_alive) {
- ASSERT_TRUE(weak_ordered_set->Contains(keep_numbers_alive->at(i)));
- if (delete_afterwards)
- weak_ordered_set->erase(keep_numbers_alive->at(i));
- else
- count++;
- }
- }
- if (add_afterwards) {
- for (int i = 1000; i < 1100; i++) {
- auto* wrapped = MakeGarbageCollected<IntWrapper>(i);
- keep_numbers_alive->push_back(wrapped);
- weak_strong->insert(wrapped, wrapped);
- strong_weak->insert(wrapped, wrapped);
- weak_weak->insert(wrapped, wrapped);
- weak_set->insert(wrapped);
- weak_ordered_set->insert(wrapped);
- }
- }
- if (collection_number == kWeakStrongIndex)
- EXPECT_EQ(count + added, weak_strong->size());
- else if (collection_number == kStrongWeakIndex)
- EXPECT_EQ(count + added, strong_weak->size());
- else if (collection_number == kWeakWeakIndex)
- EXPECT_EQ(count + added, weak_weak->size());
- else if (collection_number == kWeakSetIndex)
- EXPECT_EQ(count + added, weak_set->size());
- else if (collection_number == kWeakOrderedSetIndex)
- EXPECT_EQ(count + added, weak_ordered_set->size());
- WeakStrong::iterator it1 = weak_strong->begin();
- StrongWeak::iterator it2 = strong_weak->begin();
- WeakWeak::iterator it3 = weak_weak->begin();
- WeakSet::iterator it4 = weak_set->begin();
- WeakOrderedSet::iterator it5 = weak_ordered_set->begin();
- MapIteratorCheck(
- it1, weak_strong->end(),
- (collection_number == kWeakStrongIndex ? count : 0) + added);
- MapIteratorCheck(
- it2, strong_weak->end(),
- (collection_number == kStrongWeakIndex ? count : 0) + added);
- MapIteratorCheck(
- it3, weak_weak->end(),
- (collection_number == kWeakWeakIndex ? count : 0) + added);
- SetIteratorCheck(
- it4, weak_set->end(),
- (collection_number == kWeakSetIndex ? count : 0) + added);
- SetIteratorCheck(
- it5, weak_ordered_set->end(),
- (collection_number == kWeakOrderedSetIndex ? count : 0) + added);
- }
- for (unsigned i = 0; i < 128 + added; i++)
- keep_numbers_alive->at(i) = nullptr;
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, weak_strong->size());
- EXPECT_EQ(0u, strong_weak->size());
- EXPECT_EQ(0u, weak_weak->size());
- EXPECT_EQ(0u, weak_set->size());
- EXPECT_EQ(0u, weak_ordered_set->size());
- }
- }
-}
-
-TEST_F(HeapTest, HeapHashCountedSetToVector) {
- HeapHashCountedSet<Member<IntWrapper>> set;
- HeapVector<Member<IntWrapper>> vector;
- set.insert(MakeGarbageCollected<IntWrapper>(1));
- set.insert(MakeGarbageCollected<IntWrapper>(1));
- set.insert(MakeGarbageCollected<IntWrapper>(2));
-
- CopyToVector(set, vector);
- EXPECT_EQ(3u, vector.size());
-
- Vector<int> int_vector;
- for (const auto& i : vector)
- int_vector.push_back(i->Value());
- std::sort(int_vector.begin(), int_vector.end());
- ASSERT_EQ(3u, int_vector.size());
- EXPECT_EQ(1, int_vector[0]);
- EXPECT_EQ(1, int_vector[1]);
- EXPECT_EQ(2, int_vector[2]);
-}
-
-TEST_F(HeapTest, WeakHeapHashCountedSetToVector) {
- HeapHashCountedSet<WeakMember<IntWrapper>> set;
- HeapVector<Member<IntWrapper>> vector;
- set.insert(MakeGarbageCollected<IntWrapper>(1));
- set.insert(MakeGarbageCollected<IntWrapper>(1));
- set.insert(MakeGarbageCollected<IntWrapper>(2));
-
- CopyToVector(set, vector);
- EXPECT_LE(3u, vector.size());
- for (const auto& i : vector)
- EXPECT_TRUE(i->Value() == 1 || i->Value() == 2);
-}
-
-TEST_F(HeapTest, RefCountedGarbageCollected) {
- RefCountedAndGarbageCollected::destructor_calls_ = 0;
- {
- scoped_refptr<RefCountedAndGarbageCollected> ref_ptr3;
- {
- Persistent<RefCountedAndGarbageCollected> persistent;
- {
- Persistent<RefCountedAndGarbageCollected> ref_ptr1 =
- MakeGarbageCollected<RefCountedAndGarbageCollected>();
- Persistent<RefCountedAndGarbageCollected> ref_ptr2 =
- MakeGarbageCollected<RefCountedAndGarbageCollected>();
- PreciselyCollectGarbage();
- EXPECT_EQ(0, RefCountedAndGarbageCollected::destructor_calls_);
- persistent = ref_ptr1.Get();
- }
- // Reference count is zero for both objects but one of
- // them is kept alive by a persistent handle.
- PreciselyCollectGarbage();
- EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
- ref_ptr3 = persistent.Get();
- }
- // The persistent handle is gone but the ref count has been
- // increased to 1.
- PreciselyCollectGarbage();
- EXPECT_EQ(1, RefCountedAndGarbageCollected::destructor_calls_);
- }
- // Both persistent handle is gone and ref count is zero so the
- // object can be collected.
- PreciselyCollectGarbage();
- EXPECT_EQ(2, RefCountedAndGarbageCollected::destructor_calls_);
-}
-
-TEST_F(HeapTest, WeakMembers) {
- ClearOutOldGarbage();
- Bar::live_ = 0;
- {
- Persistent<Bar> h1 = MakeGarbageCollected<Bar>();
- Persistent<Weak> h4;
- Persistent<WithWeakMember> h5;
- PreciselyCollectGarbage();
- ASSERT_EQ(1u, Bar::live_); // h1 is live.
- {
- auto* h2 = MakeGarbageCollected<Bar>();
- auto* h3 = MakeGarbageCollected<Bar>();
- h4 = MakeGarbageCollected<Weak>(h2, h3);
- h5 = MakeGarbageCollected<WithWeakMember>(h2, h3);
- ConservativelyCollectGarbage();
- EXPECT_EQ(5u, Bar::live_); // The on-stack pointer keeps h3 alive.
- EXPECT_FALSE(h3->HasBeenFinalized());
- EXPECT_TRUE(h4->StrongIsThere());
- EXPECT_TRUE(h4->WeakIsThere());
- EXPECT_TRUE(h5->StrongIsThere());
- EXPECT_TRUE(h5->WeakIsThere());
- }
- // h3 is collected, weak pointers from h4 and h5 don't keep it alive.
- PreciselyCollectGarbage();
- EXPECT_EQ(4u, Bar::live_);
- EXPECT_TRUE(h4->StrongIsThere());
- EXPECT_FALSE(h4->WeakIsThere()); // h3 is gone from weak pointer.
- EXPECT_TRUE(h5->StrongIsThere());
- EXPECT_FALSE(h5->WeakIsThere()); // h3 is gone from weak pointer.
- h1.Release(); // Zero out h1.
- PreciselyCollectGarbage();
- EXPECT_EQ(3u, Bar::live_); // Only h4, h5 and h2 are left.
- EXPECT_TRUE(h4->StrongIsThere()); // h2 is still pointed to from h4.
- EXPECT_TRUE(h5->StrongIsThere()); // h2 is still pointed to from h5.
- }
- // h4 and h5 have gone out of scope now and they were keeping h2 alive.
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_); // All gone.
-}
-
-TEST_F(HeapTest, FinalizationObserver) {
- Persistent<FinalizationObserver<Observable>> o;
- {
- auto* foo = MakeGarbageCollected<Observable>(MakeGarbageCollected<Bar>());
- // |o| observes |foo|.
- o = MakeGarbageCollected<FinalizationObserver<Observable>>(foo);
- }
- // FinalizationObserver doesn't have a strong reference to |foo|. So |foo|
- // and its member will be collected.
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
- EXPECT_TRUE(o->DidCallWillFinalize());
-
- FinalizationObserverWithHashMap::did_call_will_finalize_ = false;
- auto* foo = MakeGarbageCollected<Observable>(MakeGarbageCollected<Bar>());
- FinalizationObserverWithHashMap::ObserverMap& map =
- FinalizationObserverWithHashMap::Observe(*foo);
- EXPECT_EQ(1u, map.size());
- foo = nullptr;
- // FinalizationObserverWithHashMap doesn't have a strong reference to
- // |foo|. So |foo| and its member will be collected.
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, Bar::live_);
- EXPECT_EQ(0u, map.size());
- EXPECT_TRUE(FinalizationObserverWithHashMap::did_call_will_finalize_);
-
- FinalizationObserverWithHashMap::ClearObservers();
-}
-
-TEST_F(HeapTest, PreFinalizer) {
- Observable::will_finalize_was_called_ = false;
- { MakeGarbageCollected<Observable>(MakeGarbageCollected<Bar>()); }
- PreciselyCollectGarbage();
- EXPECT_TRUE(Observable::will_finalize_was_called_);
-}
-
-TEST_F(HeapTest, PreFinalizerUnregistersItself) {
- ObservableWithPreFinalizer::dispose_was_called_ = false;
- MakeGarbageCollected<ObservableWithPreFinalizer>();
- PreciselyCollectGarbage();
- EXPECT_TRUE(ObservableWithPreFinalizer::dispose_was_called_);
- // Don't crash, and assertions don't fail.
-}
-
-TEST_F(HeapTest, NestedPreFinalizer) {
- g_dispose_was_called_for_pre_finalizer_base = false;
- g_dispose_was_called_for_pre_finalizer_sub_class = false;
- g_dispose_was_called_for_pre_finalizer_mixin = false;
- MakeGarbageCollected<PreFinalizerSubClass>();
- PreciselyCollectGarbage();
- EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_base);
- EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class);
- EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_mixin);
- // Don't crash, and assertions don't fail.
-}
-
-TEST_F(HeapTest, Comparisons) {
- Persistent<Bar> bar_persistent = MakeGarbageCollected<Bar>();
- Persistent<Foo> foo_persistent = MakeGarbageCollected<Foo>(bar_persistent);
- EXPECT_TRUE(bar_persistent != foo_persistent);
- bar_persistent = foo_persistent;
- EXPECT_TRUE(bar_persistent == foo_persistent);
-}
-
-namespace {
-
-void ExpectObjectMarkedAndUnmark(MarkingWorklist* worklist, void* expected) {
- MarkingItem item;
- CHECK(worklist->Pop(0, &item));
- CHECK_EQ(expected, item.base_object_payload);
- HeapObjectHeader* header =
- HeapObjectHeader::FromPayload(item.base_object_payload);
- CHECK(header->IsMarked());
- header->Unmark();
- CHECK(worklist->IsGlobalEmpty());
-}
-
-} // namespace
-
-TEST_F(HeapTest, CheckAndMarkPointer) {
- // This test ensures that conservative marking primitives can use any address
- // contained within an object to mark the corresponding object.
-
- ThreadHeap& heap = ThreadState::Current()->Heap();
- ClearOutOldGarbage();
-
- Vector<Address> object_addresses;
- Vector<Address> end_addresses;
- for (int i = 0; i < 10; i++) {
- auto* object = MakeGarbageCollected<SimpleObject>();
- Address object_address = reinterpret_cast<Address>(object);
- object_addresses.push_back(object_address);
- end_addresses.push_back(object_address + sizeof(SimpleObject) - 1);
- }
- Address large_object_address =
- reinterpret_cast<Address>(MakeGarbageCollected<LargeHeapObject>());
- Address large_object_end_address =
- large_object_address + sizeof(LargeHeapObject) - 1;
-
- {
- TestGCScope scope(BlinkGC::kHeapPointersOnStack);
- MarkingVisitor visitor(ThreadState::Current(),
- MarkingVisitor::kGlobalMarking);
- // Record marking speed as counter generation requires valid marking timings
- // for heaps >1MB.
- ThreadHeapStatsCollector::Scope stats_scope(
- heap.stats_collector(),
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure);
-
- // Conservative marker should find the interesting objects by using anything
- // between object start and end.
- MarkingWorklist* worklist = heap.GetMarkingWorklist();
- CHECK(worklist->IsGlobalEmpty());
- for (wtf_size_t i = 0; i < object_addresses.size(); i++) {
- heap.CheckAndMarkPointer(&visitor, object_addresses[i]);
- ExpectObjectMarkedAndUnmark(worklist, object_addresses[i]);
- heap.CheckAndMarkPointer(&visitor, end_addresses[i]);
- ExpectObjectMarkedAndUnmark(worklist, object_addresses[i]);
- }
- heap.CheckAndMarkPointer(&visitor, large_object_address);
- ExpectObjectMarkedAndUnmark(worklist, large_object_address);
- heap.CheckAndMarkPointer(&visitor, large_object_end_address);
- ExpectObjectMarkedAndUnmark(worklist, large_object_address);
- }
-
- // This forces a GC without stack scanning which results in the objects
- // being collected. This will also rebuild the above mentioned freelists,
- // however we don't rely on that below since we don't have any allocations.
- ClearOutOldGarbage();
-
- {
- TestGCScope scope(BlinkGC::kHeapPointersOnStack);
- MarkingVisitor visitor(ThreadState::Current(),
- MarkingVisitor::kGlobalMarking);
- // Record marking speed as counter generation requires valid marking timings
- // for heaps >1MB.
- ThreadHeapStatsCollector::Scope stats_scope(
- heap.stats_collector(),
- ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure);
-
- // After collecting all interesting objects the conservative marker should
- // not find them anymore.
- MarkingWorklist* worklist = heap.GetMarkingWorklist();
- CHECK(worklist->IsGlobalEmpty());
- for (wtf_size_t i = 0; i < object_addresses.size(); i++) {
- heap.CheckAndMarkPointer(&visitor, object_addresses[i]);
- CHECK(worklist->IsGlobalEmpty());
- heap.CheckAndMarkPointer(&visitor, end_addresses[i]);
- CHECK(worklist->IsGlobalEmpty());
- }
- heap.CheckAndMarkPointer(&visitor, large_object_address);
- CHECK(worklist->IsGlobalEmpty());
- heap.CheckAndMarkPointer(&visitor, large_object_end_address);
- CHECK(worklist->IsGlobalEmpty());
- }
-}
-
-TEST_F(HeapTest, CollectionNesting) {
- ClearOutOldGarbage();
- int k;
- int* key = &k;
- IntWrapper::destructor_calls_ = 0;
- typedef HeapVector<Member<IntWrapper>> IntVector;
- typedef HeapDeque<Member<IntWrapper>> IntDeque;
- HeapHashMap<void*, IntVector>* map =
- MakeGarbageCollected<HeapHashMap<void*, IntVector>>();
- HeapHashMap<void*, IntDeque>* map2 =
- MakeGarbageCollected<HeapHashMap<void*, IntDeque>>();
- static_assert(WTF::IsTraceable<IntVector>::value,
- "Failed to recognize HeapVector as traceable");
- static_assert(WTF::IsTraceable<IntDeque>::value,
- "Failed to recognize HeapDeque as traceable");
-
- map->insert(key, IntVector());
- map2->insert(key, IntDeque());
-
- HeapHashMap<void*, IntVector>::iterator it = map->find(key);
- EXPECT_EQ(0u, map->at(key).size());
-
- HeapHashMap<void*, IntDeque>::iterator it2 = map2->find(key);
- EXPECT_EQ(0u, map2->at(key).size());
-
- it->value.push_back(MakeGarbageCollected<IntWrapper>(42));
- EXPECT_EQ(1u, map->at(key).size());
-
- it2->value.push_back(MakeGarbageCollected<IntWrapper>(42));
- EXPECT_EQ(1u, map2->at(key).size());
-
- Persistent<HeapHashMap<void*, IntVector>> keep_alive(map);
- Persistent<HeapHashMap<void*, IntDeque>> keep_alive2(map2);
-
- for (int i = 0; i < 100; i++) {
- map->insert(key + 1 + i, IntVector());
- map2->insert(key + 1 + i, IntDeque());
- }
-
- PreciselyCollectGarbage();
-
- EXPECT_EQ(1u, map->at(key).size());
- EXPECT_EQ(1u, map2->at(key).size());
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
-
- keep_alive = nullptr;
- PreciselyCollectGarbage();
- EXPECT_EQ(1, IntWrapper::destructor_calls_);
-}
-
-TEST_F(HeapTest, GarbageCollectedMixin) {
- ClearOutOldGarbage();
-
- Persistent<UseMixin> usemixin = MakeGarbageCollected<UseMixin>();
- EXPECT_EQ(0, UseMixin::trace_count_);
- PreciselyCollectGarbage();
- EXPECT_EQ(1, UseMixin::trace_count_);
-
- Persistent<Mixin> mixin = usemixin;
- usemixin = nullptr;
- PreciselyCollectGarbage();
- EXPECT_EQ(2, UseMixin::trace_count_);
-
- Persistent<HeapHashSet<WeakMember<Mixin>>> weak_map =
- MakeGarbageCollected<HeapHashSet<WeakMember<Mixin>>>();
- weak_map->insert(MakeGarbageCollected<UseMixin>());
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, weak_map->size());
-}
-
-TEST_F(HeapTest, CollectionNesting2) {
- ClearOutOldGarbage();
- void* key = &IntWrapper::destructor_calls_;
- IntWrapper::destructor_calls_ = 0;
- typedef HeapHashSet<Member<IntWrapper>> IntSet;
- HeapHashMap<void*, IntSet>* map =
- MakeGarbageCollected<HeapHashMap<void*, IntSet>>();
-
- map->insert(key, IntSet());
-
- HeapHashMap<void*, IntSet>::iterator it = map->find(key);
- EXPECT_EQ(0u, map->at(key).size());
-
- it->value.insert(MakeGarbageCollected<IntWrapper>(42));
- EXPECT_EQ(1u, map->at(key).size());
-
- Persistent<HeapHashMap<void*, IntSet>> keep_alive(map);
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, map->at(key).size());
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
-}
-
-TEST_F(HeapTest, CollectionNesting3) {
- ClearOutOldGarbage();
- IntWrapper::destructor_calls_ = 0;
- typedef HeapVector<Member<IntWrapper>> IntVector;
- HeapVector<IntVector>* vector = MakeGarbageCollected<HeapVector<IntVector>>();
-
- vector->push_back(IntVector());
-
- HeapVector<IntVector>::iterator it = vector->begin();
- EXPECT_EQ(0u, it->size());
-
- it->push_back(MakeGarbageCollected<IntWrapper>(42));
- EXPECT_EQ(1u, it->size());
-
- Persistent<HeapVector<IntVector>> keep_alive(vector);
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, it->size());
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
-}
-
-TEST_F(HeapTest, EmbeddedInVector) {
- ClearOutOldGarbage();
- SimpleFinalizedObject::destructor_calls_ = 0;
- {
- Persistent<HeapVector<VectorObject, 2>> inline_vector =
- MakeGarbageCollected<HeapVector<VectorObject, 2>>();
- Persistent<HeapVector<VectorObject>> outline_vector =
- MakeGarbageCollected<HeapVector<VectorObject>>();
- VectorObject i1, i2;
- inline_vector->push_back(i1);
- inline_vector->push_back(i2);
-
- VectorObject o1, o2;
- outline_vector->push_back(o1);
- outline_vector->push_back(o2);
-
- Persistent<HeapVector<VectorObjectInheritedTrace>> vector_inherited_trace =
- MakeGarbageCollected<HeapVector<VectorObjectInheritedTrace>>();
- VectorObjectInheritedTrace it1, it2;
- vector_inherited_trace->push_back(it1);
- vector_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(Visitor* visitor) {}
-
- static int destructor_calls_;
-};
-
-int InlinedVectorObject::destructor_calls_ = 0;
-
-class InlinedVectorObjectWithVtable {
- DISALLOW_NEW();
-
- public:
- InlinedVectorObjectWithVtable() = default;
- virtual ~InlinedVectorObjectWithVtable() { destructor_calls_++; }
- virtual void VirtualMethod() {}
- void Trace(Visitor* visitor) {}
-
- static int destructor_calls_;
-};
-
-int InlinedVectorObjectWithVtable::destructor_calls_ = 0;
-
-} // namespace blink
-
-WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::InlinedVectorObject)
-
-namespace blink {
-
-class InlinedVectorObjectWrapper final
- : public GarbageCollected<InlinedVectorObjectWrapper> {
- public:
- InlinedVectorObjectWrapper() {
- InlinedVectorObject i1, i2;
- vector1_.push_back(i1);
- vector1_.push_back(i2);
- vector2_.push_back(i1);
- vector2_.push_back(i2); // This allocates an out-of-line buffer.
- vector3_.push_back(i1);
- vector3_.push_back(i2);
- }
-
- void Trace(Visitor* visitor) {
- visitor->Trace(vector1_);
- visitor->Trace(vector2_);
- visitor->Trace(vector3_);
- }
-
- private:
- HeapVector<InlinedVectorObject> vector1_;
- HeapVector<InlinedVectorObject, 1> vector2_;
- HeapVector<InlinedVectorObject, 2> vector3_;
-};
-
-class InlinedVectorObjectWithVtableWrapper final
- : public GarbageCollected<InlinedVectorObjectWithVtableWrapper> {
- public:
- InlinedVectorObjectWithVtableWrapper() {
- InlinedVectorObjectWithVtable i1, i2;
- vector1_.push_back(i1);
- vector1_.push_back(i2);
- vector2_.push_back(i1);
- vector2_.push_back(i2); // This allocates an out-of-line buffer.
- vector3_.push_back(i1);
- vector3_.push_back(i2);
- }
-
- void Trace(Visitor* visitor) {
- visitor->Trace(vector1_);
- visitor->Trace(vector2_);
- visitor->Trace(vector3_);
- }
-
- private:
- HeapVector<InlinedVectorObjectWithVtable> vector1_;
- HeapVector<InlinedVectorObjectWithVtable, 1> vector2_;
- HeapVector<InlinedVectorObjectWithVtable, 2> vector3_;
-};
-
-TEST_F(HeapTest, VectorDestructors) {
- ClearOutOldGarbage();
- InlinedVectorObject::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObject> vector;
- InlinedVectorObject i1, i2;
- vector.push_back(i1);
- vector.push_back(i2);
- }
- PreciselyCollectGarbage();
- // This is not EXPECT_EQ but EXPECT_LE because a HeapVectorBacking calls
- // destructors for all elements in (not the size but) the capacity of
- // the vector. Thus the number of destructors called becomes larger
- // than the actual number of objects in the vector.
- EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
-
- InlinedVectorObject::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObject, 1> vector;
- InlinedVectorObject i1, i2;
- vector.push_back(i1);
- vector.push_back(i2); // This allocates an out-of-line buffer.
- }
- PreciselyCollectGarbage();
- EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
-
- InlinedVectorObject::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObject, 2> vector;
- InlinedVectorObject i1, i2;
- vector.push_back(i1);
- vector.push_back(i2);
- }
- PreciselyCollectGarbage();
- EXPECT_LE(4, InlinedVectorObject::destructor_calls_);
-
- InlinedVectorObject::destructor_calls_ = 0;
- {
- Persistent<InlinedVectorObjectWrapper> vector_wrapper =
- MakeGarbageCollected<InlinedVectorObjectWrapper>();
- ConservativelyCollectGarbage();
- EXPECT_EQ(2, InlinedVectorObject::destructor_calls_);
- }
- PreciselyCollectGarbage();
- EXPECT_LE(8, InlinedVectorObject::destructor_calls_);
-}
-
-// TODO(Oilpan): when Vector.h's contiguous container support no longer disables
-// Vector<>s with inline capacity, enable this test.
-#if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
-TEST_F(HeapTest, VectorDestructorsWithVtable) {
- ClearOutOldGarbage();
- InlinedVectorObjectWithVtable::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObjectWithVtable> vector;
- InlinedVectorObjectWithVtable i1, i2;
- vector.push_back(i1);
- vector.push_back(i2);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
-
- InlinedVectorObjectWithVtable::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObjectWithVtable, 1> vector;
- InlinedVectorObjectWithVtable i1, i2;
- vector.push_back(i1);
- vector.push_back(i2); // This allocates an out-of-line buffer.
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(5, InlinedVectorObjectWithVtable::destructor_calls_);
-
- InlinedVectorObjectWithVtable::destructor_calls_ = 0;
- {
- HeapVector<InlinedVectorObjectWithVtable, 2> vector;
- InlinedVectorObjectWithVtable i1, i2;
- vector.push_back(i1);
- vector.push_back(i2);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(4, InlinedVectorObjectWithVtable::destructor_calls_);
-
- InlinedVectorObjectWithVtable::destructor_calls_ = 0;
- {
- Persistent<InlinedVectorObjectWithVtableWrapper> vector_wrapper =
- MakeGarbageCollected<InlinedVectorObjectWithVtableWrapper>();
- ConservativelyCollectGarbage();
- EXPECT_EQ(3, InlinedVectorObjectWithVtable::destructor_calls_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(9, InlinedVectorObjectWithVtable::destructor_calls_);
-}
-#endif
-
-template <typename Set>
-void RawPtrInHashHelper() {
- Set set;
- set.Add(new int(42));
- set.Add(new int(42));
- EXPECT_EQ(2u, set.size());
- for (typename Set::iterator it = set.begin(); it != set.end(); ++it) {
- EXPECT_EQ(42, **it);
- delete *it;
- }
-}
-
-TEST_F(HeapTest, AllocationDuringFinalization) {
- ClearOutOldGarbage();
- IntWrapper::destructor_calls_ = 0;
- OneKiloByteObject::destructor_calls_ = 0;
- LargeHeapObject::destructor_calls_ = 0;
-
- Persistent<IntWrapper> wrapper;
- MakeGarbageCollected<FinalizationAllocator>(&wrapper);
-
- PreciselyCollectGarbage();
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- EXPECT_EQ(0, OneKiloByteObject::destructor_calls_);
- EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
- // Check that the wrapper allocated during finalization is not
- // swept away and zapped later in the same sweeping phase.
- EXPECT_EQ(42, wrapper->Value());
-
- wrapper.Clear();
- PreciselyCollectGarbage();
- // The 42 IntWrappers were the ones allocated in ~FinalizationAllocator
- // and the ones allocated in LargeHeapObject.
- EXPECT_EQ(42, IntWrapper::destructor_calls_);
- EXPECT_EQ(512, OneKiloByteObject::destructor_calls_);
- EXPECT_EQ(32, LargeHeapObject::destructor_calls_);
-}
-
-class SimpleClassWithDestructor {
- public:
- SimpleClassWithDestructor() = default;
- ~SimpleClassWithDestructor() { was_destructed_ = true; }
- static bool was_destructed_;
-};
-
-bool SimpleClassWithDestructor::was_destructed_;
-
-class RefCountedWithDestructor : public RefCounted<RefCountedWithDestructor> {
- public:
- RefCountedWithDestructor() = default;
- ~RefCountedWithDestructor() { was_destructed_ = true; }
- static bool was_destructed_;
-};
-
-bool RefCountedWithDestructor::was_destructed_;
-
-template <typename Set>
-void DestructorsCalledOnGC(bool add_lots) {
- RefCountedWithDestructor::was_destructed_ = false;
- {
- Set set;
- RefCountedWithDestructor* has_destructor = new RefCountedWithDestructor();
- set.Add(base::AdoptRef(has_destructor));
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
-
- if (add_lots) {
- for (int i = 0; i < 1000; i++) {
- set.Add(base::AdoptRef(new RefCountedWithDestructor()));
- }
- }
-
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
- TestSupportingGC::ConservativelyCollectGarbage();
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
- }
- // The destructors of the sets don't call the destructors of the elements
- // in the heap sets. You have to actually remove the elments, call clear()
- // or have a GC to get the destructors called.
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
- TestSupportingGC::PreciselyCollectGarbage();
- EXPECT_TRUE(RefCountedWithDestructor::was_destructed_);
-}
-
-template <typename Set>
-void DestructorsCalledOnClear(bool add_lots) {
- RefCountedWithDestructor::was_destructed_ = false;
- Set set;
- RefCountedWithDestructor* has_destructor = new RefCountedWithDestructor();
- set.Add(base::AdoptRef(has_destructor));
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
-
- if (add_lots) {
- for (int i = 0; i < 1000; i++) {
- set.Add(base::AdoptRef(new RefCountedWithDestructor()));
- }
- }
-
- EXPECT_FALSE(RefCountedWithDestructor::was_destructed_);
- set.Clear();
- EXPECT_TRUE(RefCountedWithDestructor::was_destructed_);
-}
-
-TEST_F(HeapTest, DestructorsCalled) {
- HeapHashMap<Member<IntWrapper>, std::unique_ptr<SimpleClassWithDestructor>>
- map;
- SimpleClassWithDestructor* has_destructor = new SimpleClassWithDestructor();
- map.insert(MakeGarbageCollected<IntWrapper>(1),
- base::WrapUnique(has_destructor));
- SimpleClassWithDestructor::was_destructed_ = false;
- map.clear();
- EXPECT_TRUE(SimpleClassWithDestructor::was_destructed_);
-}
-
-class MixinA : public GarbageCollectedMixin {
- public:
- MixinA() : obj_(MakeGarbageCollected<IntWrapper>(100)) {}
- void Trace(Visitor* visitor) override {
- trace_count_++;
- visitor->Trace(obj_);
- }
-
- static int trace_count_;
-
- Member<IntWrapper> obj_;
-};
-
-int MixinA::trace_count_ = 0;
-
-class MixinB : public GarbageCollectedMixin {
- public:
- MixinB() : obj_(MakeGarbageCollected<IntWrapper>(101)) {}
- void Trace(Visitor* visitor) override { visitor->Trace(obj_); }
- Member<IntWrapper> obj_;
-};
-
-class MultipleMixins : public GarbageCollected<MultipleMixins>,
- public MixinA,
- public MixinB {
- USING_GARBAGE_COLLECTED_MIXIN(MultipleMixins);
-
- public:
- MultipleMixins() : obj_(MakeGarbageCollected<IntWrapper>(102)) {}
- void Trace(Visitor* visitor) override {
- visitor->Trace(obj_);
- MixinA::Trace(visitor);
- MixinB::Trace(visitor);
- }
- Member<IntWrapper> obj_;
-};
-
-class DerivedMultipleMixins : public MultipleMixins {
- public:
- DerivedMultipleMixins() : obj_(MakeGarbageCollected<IntWrapper>(103)) {}
-
- void Trace(Visitor* visitor) override {
- trace_called_++;
- visitor->Trace(obj_);
- MultipleMixins::Trace(visitor);
- }
-
- static int trace_called_;
-
- private:
- Member<IntWrapper> obj_;
-};
-
-int DerivedMultipleMixins::trace_called_ = 0;
-
-static const bool kIsMixinTrue = IsGarbageCollectedMixin<MultipleMixins>::value;
-static const bool kIsMixinFalse = IsGarbageCollectedMixin<IntWrapper>::value;
-
-TEST_F(HeapTest, MultipleMixins) {
- EXPECT_TRUE(kIsMixinTrue);
- EXPECT_FALSE(kIsMixinFalse);
-
- ClearOutOldGarbage();
- IntWrapper::destructor_calls_ = 0;
- MultipleMixins* obj = MakeGarbageCollected<MultipleMixins>();
- {
- Persistent<MixinA> a = obj;
- PreciselyCollectGarbage();
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- }
- {
- Persistent<MixinB> b = obj;
- PreciselyCollectGarbage();
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(3, IntWrapper::destructor_calls_);
-}
-
-TEST_F(HeapTest, DerivedMultipleMixins) {
- ClearOutOldGarbage();
- IntWrapper::destructor_calls_ = 0;
- DerivedMultipleMixins::trace_called_ = 0;
-
- DerivedMultipleMixins* obj = MakeGarbageCollected<DerivedMultipleMixins>();
- {
- Persistent<MixinA> a = obj;
- PreciselyCollectGarbage();
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- EXPECT_LT(0, DerivedMultipleMixins::trace_called_);
- }
- {
- Persistent<MixinB> b = obj;
- PreciselyCollectGarbage();
- EXPECT_EQ(0, IntWrapper::destructor_calls_);
- EXPECT_LT(0, DerivedMultipleMixins::trace_called_);
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(4, IntWrapper::destructor_calls_);
-}
-
-class MixinInstanceWithoutTrace
- : public GarbageCollected<MixinInstanceWithoutTrace>,
- public MixinA {
- USING_GARBAGE_COLLECTED_MIXIN(MixinInstanceWithoutTrace);
-
- public:
- MixinInstanceWithoutTrace() = default;
-};
-
-TEST_F(HeapTest, MixinInstanceWithoutTrace) {
- // Verify that a mixin instance without any traceable
- // references inherits the mixin's trace implementation.
- ClearOutOldGarbage();
- MixinA::trace_count_ = 0;
- MixinInstanceWithoutTrace* obj =
- MakeGarbageCollected<MixinInstanceWithoutTrace>();
- int saved_trace_count = 0;
- {
- Persistent<MixinA> a = obj;
- PreciselyCollectGarbage();
- saved_trace_count = MixinA::trace_count_;
- EXPECT_LT(0, saved_trace_count);
- }
- {
- Persistent<MixinInstanceWithoutTrace> b = obj;
- PreciselyCollectGarbage();
- EXPECT_LT(saved_trace_count, MixinA::trace_count_);
- saved_trace_count = MixinA::trace_count_;
- }
- PreciselyCollectGarbage();
- // Oilpan might still call trace on dead objects for various reasons which is
- // valid before sweeping started.
- EXPECT_LE(saved_trace_count, MixinA::trace_count_);
-}
-
-TEST_F(HeapTest, NeedsAdjustPointer) {
- // class Mixin : public GarbageCollectedMixin {};
- static_assert(NeedsAdjustPointer<Mixin>::value,
- "A Mixin pointer needs adjustment");
- static_assert(NeedsAdjustPointer<const Mixin>::value,
- "A const Mixin pointer needs adjustment");
-
- // class SimpleObject : public GarbageCollected<SimpleObject> {};
- static_assert(!NeedsAdjustPointer<SimpleObject>::value,
- "A SimpleObject pointer does not need adjustment");
- static_assert(!NeedsAdjustPointer<const SimpleObject>::value,
- "A const SimpleObject pointer does not need adjustment");
-
- // class UseMixin : public SimpleObject, public Mixin {};
- static_assert(!NeedsAdjustPointer<UseMixin>::value,
- "A UseMixin pointer does not need adjustment");
- static_assert(!NeedsAdjustPointer<const UseMixin>::value,
- "A const UseMixin pointer does not need adjustment");
-}
-
-static void AddElementsToWeakMap(
- HeapHashMap<int, WeakMember<IntWrapper>>* map) {
- // Key cannot be zero in hashmap.
- for (int i = 1; i < 11; i++)
- map->insert(i, MakeGarbageCollected<IntWrapper>(i));
-}
-
-// crbug.com/402426
-// If it doesn't assert a concurrent modification to the map, then it's passing.
-TEST_F(HeapTest, RegressNullIsStrongified) {
- Persistent<HeapHashMap<int, WeakMember<IntWrapper>>> map =
- MakeGarbageCollected<HeapHashMap<int, WeakMember<IntWrapper>>>();
- AddElementsToWeakMap(map);
- HeapHashMap<int, WeakMember<IntWrapper>>::AddResult result =
- map->insert(800, nullptr);
- ConservativelyCollectGarbage();
- result.stored_value->value = MakeGarbageCollected<IntWrapper>(42);
-}
-
-TEST_F(HeapTest, Bind) {
- base::OnceClosure closure =
- WTF::Bind(static_cast<void (Bar::*)(Visitor*)>(&Bar::Trace),
- WrapPersistent(MakeGarbageCollected<Bar>()), nullptr);
- // OffHeapInt* should not make Persistent.
- base::OnceClosure closure2 =
- WTF::Bind(&OffHeapInt::VoidFunction, OffHeapInt::Create(1));
- PreciselyCollectGarbage();
- // The closure should have a persistent handle to the Bar.
- EXPECT_EQ(1u, Bar::live_);
-
- UseMixin::trace_count_ = 0;
- auto* mixin = MakeGarbageCollected<UseMixin>();
- base::OnceClosure mixin_closure =
- WTF::Bind(static_cast<void (Mixin::*)(Visitor*)>(&Mixin::Trace),
- WrapPersistent(mixin), nullptr);
- PreciselyCollectGarbage();
- // The closure should have a persistent handle to the mixin.
- EXPECT_EQ(1, UseMixin::trace_count_);
-}
-
-typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet;
-
-TEST_F(HeapTest, EphemeronsInEphemerons) {
- typedef HeapHashMap<WeakMember<IntWrapper>, Member<IntWrapper>> InnerMap;
- typedef HeapHashMap<WeakMember<IntWrapper>, InnerMap> OuterMap;
-
- for (int keep_outer_alive = 0; keep_outer_alive <= 1; keep_outer_alive++) {
- for (int keep_inner_alive = 0; keep_inner_alive <= 1; keep_inner_alive++) {
- Persistent<OuterMap> outer = MakeGarbageCollected<OuterMap>();
- Persistent<IntWrapper> one = MakeGarbageCollected<IntWrapper>(1);
- Persistent<IntWrapper> two = MakeGarbageCollected<IntWrapper>(2);
- outer->insert(one, InnerMap());
- outer->begin()->value.insert(two, MakeGarbageCollected<IntWrapper>(3));
- EXPECT_EQ(1u, outer->at(one).size());
- if (!keep_outer_alive)
- one.Clear();
- if (!keep_inner_alive)
- two.Clear();
- PreciselyCollectGarbage();
- if (keep_outer_alive) {
- const InnerMap& inner = outer->at(one);
- if (keep_inner_alive) {
- EXPECT_EQ(1u, inner.size());
- IntWrapper* three = inner.at(two);
- EXPECT_EQ(3, three->Value());
- } else {
- EXPECT_EQ(0u, inner.size());
- }
- } else {
- EXPECT_EQ(0u, outer->size());
- }
- outer->clear();
- Persistent<IntWrapper> deep = MakeGarbageCollected<IntWrapper>(42);
- Persistent<IntWrapper> home = MakeGarbageCollected<IntWrapper>(103);
- Persistent<IntWrapper> composite = MakeGarbageCollected<IntWrapper>(91);
- Persistent<HeapVector<Member<IntWrapper>>> keep_alive =
- MakeGarbageCollected<HeapVector<Member<IntWrapper>>>();
- for (int i = 0; i < 10000; i++) {
- auto* value = MakeGarbageCollected<IntWrapper>(i);
- keep_alive->push_back(value);
- OuterMap::AddResult new_entry = outer->insert(value, InnerMap());
- new_entry.stored_value->value.insert(deep, home);
- new_entry.stored_value->value.insert(composite, home);
- }
- composite.Clear();
- PreciselyCollectGarbage();
- EXPECT_EQ(10000u, outer->size());
- for (int i = 0; i < 10000; i++) {
- IntWrapper* value = keep_alive->at(i);
- EXPECT_EQ(1u,
- outer->at(value)
- .size()); // Other one was deleted by weak handling.
- if (i & 1)
- keep_alive->at(i) = nullptr;
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(5000u, outer->size());
- }
- }
-}
-
-class EphemeronWrapper : public GarbageCollected<EphemeronWrapper> {
- public:
- void Trace(Visitor* visitor) { visitor->Trace(map_); }
-
- typedef HeapHashMap<WeakMember<IntWrapper>, Member<EphemeronWrapper>> Map;
- Map& GetMap() { return map_; }
-
- private:
- Map map_;
-};
-
-TEST_F(HeapTest, EphemeronsPointToEphemerons) {
- Persistent<IntWrapper> key = MakeGarbageCollected<IntWrapper>(42);
- Persistent<IntWrapper> key2 = MakeGarbageCollected<IntWrapper>(103);
-
- Persistent<EphemeronWrapper> chain;
- for (int i = 0; i < 100; i++) {
- EphemeronWrapper* old_head = chain;
- chain = MakeGarbageCollected<EphemeronWrapper>();
- if (i == 50)
- chain->GetMap().insert(key2, old_head);
- else
- chain->GetMap().insert(key, old_head);
- chain->GetMap().insert(MakeGarbageCollected<IntWrapper>(103),
- MakeGarbageCollected<EphemeronWrapper>());
- }
-
- PreciselyCollectGarbage();
-
- EphemeronWrapper* wrapper = chain;
- for (int i = 0; i < 100; i++) {
- EXPECT_EQ(1u, wrapper->GetMap().size());
- if (i == 49)
- wrapper = wrapper->GetMap().at(key2);
- else
- wrapper = wrapper->GetMap().at(key);
- }
- EXPECT_EQ(nullptr, wrapper);
-
- key2.Clear();
- PreciselyCollectGarbage();
-
- wrapper = chain;
- for (int i = 0; i < 50; i++) {
- EXPECT_EQ(i == 49 ? 0u : 1u, wrapper->GetMap().size());
- wrapper = wrapper->GetMap().at(key);
- }
- EXPECT_EQ(nullptr, wrapper);
-
- key.Clear();
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, chain->GetMap().size());
-}
-
-TEST_F(HeapTest, Ephemeron) {
- typedef HeapHashSet<WeakMember<IntWrapper>> Set;
-
- Persistent<Set> set = MakeGarbageCollected<Set>();
-
- Persistent<IntWrapper> wp1 = MakeGarbageCollected<IntWrapper>(1);
- Persistent<IntWrapper> wp2 = MakeGarbageCollected<IntWrapper>(2);
- Persistent<IntWrapper> pw1 = MakeGarbageCollected<IntWrapper>(3);
- Persistent<IntWrapper> pw2 = MakeGarbageCollected<IntWrapper>(4);
-
- set->insert(wp1);
- set->insert(wp2);
- set->insert(pw1);
- set->insert(pw2);
-
- PreciselyCollectGarbage();
-
- EXPECT_EQ(4u, set->size());
-
- wp2.Clear(); // Kills all entries in the weakPairMaps except the first.
- pw2.Clear(); // Kills all entries in the pairWeakMaps except the first.
-
- for (int i = 0; i < 2; i++) {
- PreciselyCollectGarbage();
-
- EXPECT_EQ(2u, set->size()); // wp1 and pw1.
- }
-
- wp1.Clear();
- pw1.Clear();
-
- PreciselyCollectGarbage();
-
- EXPECT_EQ(0u, set->size());
-}
-
-class Link1 : public GarbageCollected<Link1> {
- public:
- Link1(IntWrapper* link) : link_(link) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(link_); }
-
- IntWrapper* Link() { return link_; }
-
- private:
- Member<IntWrapper> link_;
-};
-
-TEST_F(HeapTest, IndirectStrongToWeak) {
- typedef HeapHashMap<WeakMember<IntWrapper>, Member<Link1>> Map;
- Persistent<Map> map = MakeGarbageCollected<Map>();
- Persistent<IntWrapper> dead_object = MakeGarbageCollected<IntWrapper>(
- 100); // Named for "Drowning by Numbers" (1988).
- Persistent<IntWrapper> life_object = MakeGarbageCollected<IntWrapper>(42);
- map->insert(dead_object, MakeGarbageCollected<Link1>(dead_object));
- map->insert(life_object, MakeGarbageCollected<Link1>(life_object));
- EXPECT_EQ(2u, map->size());
- PreciselyCollectGarbage();
- EXPECT_EQ(2u, map->size());
- EXPECT_EQ(dead_object, map->at(dead_object)->Link());
- EXPECT_EQ(life_object, map->at(life_object)->Link());
- dead_object.Clear(); // Now it can live up to its name.
- PreciselyCollectGarbage();
- EXPECT_EQ(1u, map->size());
- EXPECT_EQ(life_object, map->at(life_object)->Link());
- life_object.Clear(); // Despite its name.
- PreciselyCollectGarbage();
- EXPECT_EQ(0u, map->size());
-}
-
-static bool AllocateAndReturnBool() {
- TestSupportingGC::ConservativelyCollectGarbage();
- return true;
-}
-
-template <typename T>
-class TraceIfNeededTester final
- : public GarbageCollected<TraceIfNeededTester<T>> {
- public:
- TraceIfNeededTester() = default;
- explicit TraceIfNeededTester(const T& obj) : obj_(obj) {}
-
- void Trace(Visitor* visitor) { TraceIfNeeded<T>::Trace(visitor, obj_); }
- T& Obj() { return obj_; }
- ~TraceIfNeededTester() = default;
-
- private:
- T obj_;
-};
-
-class PartObject {
- DISALLOW_NEW();
-
- public:
- PartObject() : obj_(MakeGarbageCollected<SimpleObject>()) {}
- void Trace(Visitor* visitor) { visitor->Trace(obj_); }
-
- private:
- Member<SimpleObject> obj_;
-};
-
-class AllocatesOnAssignment {
- public:
- AllocatesOnAssignment(std::nullptr_t) : value_(nullptr) {}
- AllocatesOnAssignment(int x) : value_(MakeGarbageCollected<IntWrapper>(x)) {}
- AllocatesOnAssignment(IntWrapper* x) : value_(x) {}
-
- AllocatesOnAssignment& operator=(const AllocatesOnAssignment x) {
- value_ = x.value_;
- return *this;
- }
-
- enum DeletedMarker { kDeletedValue };
-
- AllocatesOnAssignment(const AllocatesOnAssignment& other) {
- if (!ThreadState::Current()->IsGCForbidden())
- TestSupportingGC::ConservativelyCollectGarbage();
- value_ = MakeGarbageCollected<IntWrapper>(other.value_->Value());
- }
-
- AllocatesOnAssignment(DeletedMarker) : value_(WTF::kHashTableDeletedValue) {}
-
- inline bool IsDeleted() const { return value_.IsHashTableDeletedValue(); }
-
- void Trace(Visitor* visitor) { visitor->Trace(value_); }
-
- int Value() { return value_->Value(); }
-
- private:
- Member<IntWrapper> value_;
-
- friend bool operator==(const AllocatesOnAssignment&,
- const AllocatesOnAssignment&);
- friend void swap(AllocatesOnAssignment&, AllocatesOnAssignment&);
-};
-
-bool operator==(const AllocatesOnAssignment& a,
- const AllocatesOnAssignment& b) {
- if (a.value_)
- return b.value_ && a.value_->Value() == b.value_->Value();
- return !b.value_;
-}
-
-void swap(AllocatesOnAssignment& a, AllocatesOnAssignment& b) {
- std::swap(a.value_, b.value_);
-}
-
-struct DegenerateHash {
- static unsigned GetHash(const AllocatesOnAssignment&) { return 0; }
- static bool Equal(const AllocatesOnAssignment& a,
- const AllocatesOnAssignment& b) {
- return !a.IsDeleted() && a == b;
- }
- static const bool safe_to_compare_to_empty_or_deleted = true;
-};
-
-struct AllocatesOnAssignmentHashTraits
- : WTF::GenericHashTraits<AllocatesOnAssignment> {
- typedef AllocatesOnAssignment T;
- typedef std::nullptr_t EmptyValueType;
- static EmptyValueType EmptyValue() { return nullptr; }
- static const bool kEmptyValueIsZero =
- false; // Can't be zero if it has a vtable.
- static void ConstructDeletedValue(T& slot, bool) {
- slot = T(AllocatesOnAssignment::kDeletedValue);
- }
- static bool IsDeletedValue(const T& value) { return value.IsDeleted(); }
-};
-
-} // namespace blink
-
-namespace WTF {
-
-template <>
-struct DefaultHash<blink::AllocatesOnAssignment> {
- typedef blink::DegenerateHash Hash;
-};
-
-template <>
-struct HashTraits<blink::AllocatesOnAssignment>
- : blink::AllocatesOnAssignmentHashTraits {};
-
-} // namespace WTF
-
-namespace blink {
-
-TEST_F(HeapTest, GCInHashMapOperations) {
- typedef HeapHashMap<AllocatesOnAssignment, AllocatesOnAssignment> Map;
- Map* map = MakeGarbageCollected<Map>();
- IntWrapper* key = MakeGarbageCollected<IntWrapper>(42);
- map->insert(key, AllocatesOnAssignment(103));
- map->erase(key);
- for (int i = 0; i < 10; i++)
- map->insert(AllocatesOnAssignment(i), AllocatesOnAssignment(i));
- for (Map::iterator it = map->begin(); it != map->end(); ++it)
- EXPECT_EQ(it->key.Value(), it->value.Value());
-}
-
-class PartObjectWithVirtualMethod {
- public:
- virtual void Trace(Visitor* visitor) {}
-};
-
-class ObjectWithVirtualPartObject
- : public GarbageCollected<ObjectWithVirtualPartObject> {
- public:
- ObjectWithVirtualPartObject() : dummy_(AllocateAndReturnBool()) {}
- void Trace(Visitor* visitor) { visitor->Trace(part_); }
-
- private:
- bool dummy_;
- PartObjectWithVirtualMethod part_;
-};
-
-TEST_F(HeapTest, PartObjectWithVirtualMethod) {
- ObjectWithVirtualPartObject* object =
- MakeGarbageCollected<ObjectWithVirtualPartObject>();
- EXPECT_TRUE(object);
-}
-
-class AllocInSuperConstructorArgumentSuper
- : public GarbageCollected<AllocInSuperConstructorArgumentSuper> {
- public:
- AllocInSuperConstructorArgumentSuper(bool value) : value_(value) {}
- virtual ~AllocInSuperConstructorArgumentSuper() = default;
- virtual void Trace(Visitor* visitor) {}
- bool Value() { return value_; }
-
- private:
- bool value_;
-};
-
-class AllocInSuperConstructorArgument
- : public AllocInSuperConstructorArgumentSuper {
- public:
- AllocInSuperConstructorArgument()
- : AllocInSuperConstructorArgumentSuper(AllocateAndReturnBool()) {}
-};
-
-// Regression test for crbug.com/404511. Tests conservative marking of
-// an object with an uninitialized vtable.
-TEST_F(HeapTest, AllocationInSuperConstructorArgument) {
- AllocInSuperConstructorArgument* object =
- MakeGarbageCollected<AllocInSuperConstructorArgument>();
- EXPECT_TRUE(object);
- ThreadState::Current()->CollectAllGarbageForTesting();
-}
-
-class NonNodeAllocatingNodeInDestructor final
- : public GarbageCollected<NonNodeAllocatingNodeInDestructor> {
- public:
- ~NonNodeAllocatingNodeInDestructor() {
- node_ = new Persistent<IntNode>(IntNode::Create(10));
- }
-
- void Trace(Visitor* visitor) {}
-
- static Persistent<IntNode>* node_;
-};
-
-Persistent<IntNode>* NonNodeAllocatingNodeInDestructor::node_ = nullptr;
-
-TEST_F(HeapTest, NonNodeAllocatingNodeInDestructor) {
- MakeGarbageCollected<NonNodeAllocatingNodeInDestructor>();
- PreciselyCollectGarbage();
- EXPECT_EQ(10, (*NonNodeAllocatingNodeInDestructor::node_)->Value());
- delete NonNodeAllocatingNodeInDestructor::node_;
- NonNodeAllocatingNodeInDestructor::node_ = nullptr;
-}
-
-class DeepEagerly final : public GarbageCollected<DeepEagerly> {
- public:
- DeepEagerly(DeepEagerly* next) : next_(next) {}
-
- void Trace(Visitor* visitor) {
- int calls = ++s_trace_calls_;
- if (s_trace_lazy_ <= 2)
- visitor->Trace(next_);
- if (s_trace_calls_ == calls)
- s_trace_lazy_++;
- }
-
- Member<DeepEagerly> next_;
-
- static int s_trace_calls_;
- static int s_trace_lazy_;
-};
-
-int DeepEagerly::s_trace_calls_ = 0;
-int DeepEagerly::s_trace_lazy_ = 0;
-
-TEST_F(HeapTest, TraceDeepEagerly) {
-// The allocation & GC overhead is considerable for this test,
-// straining debug builds and lower-end targets too much to be
-// worth running.
-#if !DCHECK_IS_ON() && !defined(OS_ANDROID)
- DeepEagerly* obj = nullptr;
- for (int i = 0; i < 10000000; i++)
- obj = MakeGarbageCollected<DeepEagerly>(obj);
-
- Persistent<DeepEagerly> persistent(obj);
- PreciselyCollectGarbage();
-
- // Verify that the DeepEagerly chain isn't completely unravelled
- // by performing eager trace() calls, but the explicit mark
- // stack is switched once some nesting limit is exceeded.
- EXPECT_GT(DeepEagerly::s_trace_lazy_, 2);
-#endif
-}
-
-TEST_F(HeapTest, DequeExpand) {
- // Test expansion of a HeapDeque<>'s buffer.
-
- typedef HeapDeque<Member<IntWrapper>> IntDeque;
-
- Persistent<IntDeque> deque = MakeGarbageCollected<IntDeque>();
-
- // Append a sequence, bringing about repeated expansions of the
- // deque's buffer.
- int i = 0;
- for (; i < 60; ++i)
- deque->push_back(MakeGarbageCollected<IntWrapper>(i));
-
- EXPECT_EQ(60u, deque->size());
- i = 0;
- for (const auto& int_wrapper : *deque) {
- EXPECT_EQ(i, int_wrapper->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 auto& int_wrapper : *deque) {
- EXPECT_EQ(50 + i, int_wrapper->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(MakeGarbageCollected<IntWrapper>(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 auto& int_wrapper : *deque) {
- EXPECT_EQ(i + 50, int_wrapper->Value());
- i++;
- }
-}
-
-class SimpleRefValue : public RefCounted<SimpleRefValue> {
- public:
- static scoped_refptr<SimpleRefValue> Create(int i) {
- return base::AdoptRef(new SimpleRefValue(i));
- }
-
- int Value() const { return value_; }
-
- private:
- explicit SimpleRefValue(int value) : value_(value) {}
-
- int value_;
-};
-
-class PartObjectWithRef {
- DISALLOW_NEW();
-
- public:
- PartObjectWithRef(int i) : value_(SimpleRefValue::Create(i)) {}
-
- void Trace(Visitor* visitor) {}
-
- int Value() const { return value_->Value(); }
-
- private:
- scoped_refptr<SimpleRefValue> value_;
-};
-
-} // namespace blink
-
-WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::PartObjectWithRef)
-
-namespace blink {
-
-TEST_F(HeapTest, HeapVectorPartObjects) {
- HeapVector<PartObjectWithRef> vector1;
- HeapVector<PartObjectWithRef> vector2;
-
- for (int i = 0; i < 10; ++i) {
- vector1.push_back(PartObjectWithRef(i));
- vector2.push_back(PartObjectWithRef(i));
- }
-
- vector1.ReserveCapacity(150);
- EXPECT_LE(150u, vector1.capacity());
- EXPECT_EQ(10u, vector1.size());
-
- vector2.ReserveCapacity(100);
- EXPECT_LE(100u, vector2.capacity());
- EXPECT_EQ(10u, vector2.size());
-
- for (int i = 0; i < 4; ++i) {
- vector1.push_back(PartObjectWithRef(10 + i));
- vector2.push_back(PartObjectWithRef(10 + i));
- vector2.push_back(PartObjectWithRef(10 + i));
- }
-
- // Shrinking heap vector backing stores always succeeds,
- // so these two will not currently exercise the code path
- // where shrinking causes copying into a new, small buffer.
- vector2.ShrinkToReasonableCapacity();
- EXPECT_EQ(18u, vector2.size());
-
- vector1.ShrinkToReasonableCapacity();
- EXPECT_EQ(14u, vector1.size());
-}
-
-class TestMixinAllocationA : public GarbageCollected<TestMixinAllocationA>,
- public GarbageCollectedMixin {
- USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationA);
-
- public:
- TestMixinAllocationA() = default;
-
- void Trace(Visitor* visitor) override {}
-};
-
-class TestMixinAllocationB : public TestMixinAllocationA {
- USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationB);
-
- public:
- TestMixinAllocationB()
- // Construct object during a mixin construction.
- : a_(MakeGarbageCollected<TestMixinAllocationA>()) {}
-
- void Trace(Visitor* visitor) override {
- visitor->Trace(a_);
- TestMixinAllocationA::Trace(visitor);
- }
-
- private:
- Member<TestMixinAllocationA> a_;
-};
-
-class TestMixinAllocationC final : public TestMixinAllocationB {
- USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationC);
-
- public:
- TestMixinAllocationC() { DCHECK(!ThreadState::Current()->IsGCForbidden()); }
-
- void Trace(Visitor* visitor) override {
- TestMixinAllocationB::Trace(visitor);
- }
-};
-
-TEST_F(HeapTest, NestedMixinConstruction) {
- TestMixinAllocationC* object = MakeGarbageCollected<TestMixinAllocationC>();
- EXPECT_TRUE(object);
-}
-
-class ObjectWithLargeAmountsOfAllocationInConstructor {
- public:
- ObjectWithLargeAmountsOfAllocationInConstructor(
- size_t number_of_large_objects_to_allocate,
- ClassWithMember* member) {
- // Should a constructor allocate plenty in its constructor,
- // and it is a base of GC mixin, GCs will remain locked out
- // regardless, as we cannot safely trace the leftmost GC
- // mixin base.
- DCHECK(ThreadState::Current()->IsGCForbidden());
- for (size_t i = 0; i < number_of_large_objects_to_allocate; i++) {
- auto* large_object = MakeGarbageCollected<LargeHeapObject>();
- EXPECT_TRUE(large_object);
- EXPECT_EQ(0, member->TraceCount());
- }
- }
-};
-
-class WeakPersistentHolder final {
- public:
- explicit WeakPersistentHolder(IntWrapper* object) : object_(object) {}
- IntWrapper* Object() const { return object_; }
-
- private:
- WeakPersistent<IntWrapper> object_;
-};
-
-TEST_F(HeapTest, WeakPersistent) {
- Persistent<IntWrapper> object = MakeGarbageCollected<IntWrapper>(20);
- std::unique_ptr<WeakPersistentHolder> holder =
- std::make_unique<WeakPersistentHolder>(object);
- PreciselyCollectGarbage();
- EXPECT_TRUE(holder->Object());
- object = nullptr;
- PreciselyCollectGarbage();
- EXPECT_FALSE(holder->Object());
-}
-
-namespace {
-
-class ThreadedClearOnShutdownTester : public ThreadedTesterBase {
- public:
- static void Test() {
- IntWrapper::destructor_calls_ = 0;
- ThreadedTesterBase::Test(new ThreadedClearOnShutdownTester);
- EXPECT_EQ(kNumberOfThreads, IntWrapper::destructor_calls_);
- }
-
- private:
- void RunWhileAttached();
-
- void RunThread() override {
- EXPECT_EQ(42, ThreadSpecificIntWrapper().Value());
- RunWhileAttached();
- }
-
- class HeapObject;
- friend class HeapObject;
-
- using WeakHeapObjectSet = HeapHashSet<WeakMember<HeapObject>>;
-
- static WeakHeapObjectSet& GetWeakHeapObjectSet();
-
- using HeapObjectSet = HeapHashSet<Member<HeapObject>>;
- static HeapObjectSet& GetHeapObjectSet();
-
- static IntWrapper& ThreadSpecificIntWrapper() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Persistent<IntWrapper>>,
- int_wrapper, ());
- Persistent<IntWrapper>& handle = *int_wrapper;
- if (!handle) {
- handle = MakeGarbageCollected<IntWrapper>(42);
- handle.RegisterAsStaticReference();
- }
- return *handle;
- }
-};
-
-class ThreadedClearOnShutdownTester::HeapObject final
- : public GarbageCollected<ThreadedClearOnShutdownTester::HeapObject> {
- public:
- explicit HeapObject(bool test_destructor)
- : test_destructor_(test_destructor) {}
- ~HeapObject() {
- if (!test_destructor_)
- return;
-
- // Verify that the weak reference is gone.
- EXPECT_FALSE(GetWeakHeapObjectSet().Contains(this));
-
- // Add a new member to the static singleton; this will
- // re-initializes the persistent node of the collection
- // object. Done while terminating the test thread, so
- // verify that this brings about the release of the
- // persistent also.
- GetHeapObjectSet().insert(MakeGarbageCollected<HeapObject>(false));
- }
-
- void Trace(Visitor* visitor) {}
-
- private:
- bool test_destructor_;
-};
-
-ThreadedClearOnShutdownTester::WeakHeapObjectSet&
-ThreadedClearOnShutdownTester::GetWeakHeapObjectSet() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Persistent<WeakHeapObjectSet>>,
- singleton, ());
- Persistent<WeakHeapObjectSet>& singleton_persistent = *singleton;
- if (!singleton_persistent) {
- singleton_persistent = MakeGarbageCollected<WeakHeapObjectSet>();
- singleton_persistent.RegisterAsStaticReference();
- }
- return *singleton_persistent;
-}
-
-ThreadedClearOnShutdownTester::HeapObjectSet&
-ThreadedClearOnShutdownTester::GetHeapObjectSet() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Persistent<HeapObjectSet>>,
- singleton, ());
- Persistent<HeapObjectSet>& singleton_persistent = *singleton;
- if (!singleton_persistent) {
- singleton_persistent = MakeGarbageCollected<HeapObjectSet>();
- singleton_persistent.RegisterAsStaticReference();
- }
- return *singleton_persistent;
-}
-
-void ThreadedClearOnShutdownTester::RunWhileAttached() {
- EXPECT_EQ(42, ThreadSpecificIntWrapper().Value());
- // Creates a thread-specific singleton to a weakly held object.
- GetWeakHeapObjectSet().insert(MakeGarbageCollected<HeapObject>(true));
-}
-
-} // namespace
-
-TEST_F(HeapTest, TestClearOnShutdown) {
- ThreadedClearOnShutdownTester::Test();
-}
-
-// Verify that WeakMember<const T> compiles and behaves as expected.
-class WithWeakConstObject final : public GarbageCollected<WithWeakConstObject> {
- public:
- WithWeakConstObject(const IntWrapper* int_wrapper) : wrapper_(int_wrapper) {}
-
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
-
- const IntWrapper* Value() const { return wrapper_; }
-
- private:
- WeakMember<const IntWrapper> wrapper_;
-};
-
-TEST_F(HeapTest, TestWeakConstObject) {
- Persistent<WithWeakConstObject> weak_wrapper;
- {
- const auto* wrapper = MakeGarbageCollected<IntWrapper>(42);
- weak_wrapper = MakeGarbageCollected<WithWeakConstObject>(wrapper);
- ConservativelyCollectGarbage();
- EXPECT_EQ(wrapper, weak_wrapper->Value());
- // Stub out any stack reference.
- wrapper = nullptr;
- }
- PreciselyCollectGarbage();
- EXPECT_EQ(nullptr, weak_wrapper->Value());
-}
-
-class EmptyMixin : public GarbageCollectedMixin {};
-class UseMixinFromLeftmostInherited : public UseMixin, public EmptyMixin {
- public:
- ~UseMixinFromLeftmostInherited() = default;
-};
-
-TEST_F(HeapTest, IsGarbageCollected) {
- // Static sanity checks covering the correct operation of
- // IsGarbageCollectedType<>.
-
- static_assert(WTF::IsGarbageCollectedType<SimpleObject>::value,
- "GarbageCollected<>");
- static_assert(WTF::IsGarbageCollectedType<const SimpleObject>::value,
- "const GarbageCollected<>");
- static_assert(WTF::IsGarbageCollectedType<IntWrapper>::value,
- "GarbageCollected<>");
- static_assert(WTF::IsGarbageCollectedType<GarbageCollectedMixin>::value,
- "GarbageCollectedMixin");
- static_assert(WTF::IsGarbageCollectedType<const GarbageCollectedMixin>::value,
- "const GarbageCollectedMixin");
- static_assert(WTF::IsGarbageCollectedType<UseMixin>::value,
- "GarbageCollectedMixin instance");
- static_assert(WTF::IsGarbageCollectedType<const UseMixin>::value,
- "const GarbageCollectedMixin instance");
- static_assert(
- WTF::IsGarbageCollectedType<UseMixinFromLeftmostInherited>::value,
- "GarbageCollectedMixin derived instance");
- static_assert(WTF::IsGarbageCollectedType<MultipleMixins>::value,
- "GarbageCollectedMixin");
-
- static_assert(
- WTF::IsGarbageCollectedType<HeapHashSet<Member<IntWrapper>>>::value,
- "HeapHashSet");
- 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");
- static_assert(WTF::IsGarbageCollectedType<
- HeapHashCountedSet<Member<IntWrapper>>>::value,
- "HeapHashCountedSet");
- static_assert(
- WTF::IsGarbageCollectedType<HeapHashMap<int, Member<IntWrapper>>>::value,
- "HeapHashMap");
- static_assert(
- WTF::IsGarbageCollectedType<HeapVector<Member<IntWrapper>>>::value,
- "HeapVector");
- static_assert(
- WTF::IsGarbageCollectedType<HeapDeque<Member<IntWrapper>>>::value,
- "HeapDeque");
-}
-
-TEST_F(HeapTest, HeapHashMapCallsDestructor) {
- String string = "string";
- EXPECT_TRUE(string.Impl()->HasOneRef());
-
- HeapHashMap<KeyWithCopyingMoveConstructor, Member<IntWrapper>> map;
-
- EXPECT_TRUE(string.Impl()->HasOneRef());
-
- for (int i = 1; i <= 100; ++i) {
- KeyWithCopyingMoveConstructor key(i, string);
- map.insert(key, MakeGarbageCollected<IntWrapper>(i));
- }
-
- EXPECT_FALSE(string.Impl()->HasOneRef());
- map.clear();
-
- EXPECT_TRUE(string.Impl()->HasOneRef());
-}
-
-TEST_F(HeapTest, ShrinkVector) {
- // Regression test: https://crbug.com/823289
-
- HeapVector<Member<IntWrapper>> vector;
- vector.ReserveCapacity(32);
- for (int i = 0; i < 4; i++) {
- vector.push_back(MakeGarbageCollected<IntWrapper>(i));
- }
-
- ConservativelyCollectGarbage(BlinkGC::kConcurrentAndLazySweeping);
-
- // The following call tries to promptly free the left overs. In the buggy
- // scenario that would create a free HeapObjectHeader that is assumed to be
- // black which it is not.
- vector.ShrinkToFit();
-}
-
-TEST_F(HeapTest, GarbageCollectedInConstruction) {
- using O = ObjectWithCallbackBeforeInitializer<IntWrapper>;
- MakeGarbageCollected<O>(base::BindOnce([](O* thiz) {
- CHECK(HeapObjectHeader::FromPayload(thiz)->IsInConstruction());
- }));
-}
-
-TEST_F(HeapTest, GarbageCollectedMixinInConstruction) {
- using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
- MakeGarbageCollected<O>(base::BindOnce([](O::Mixin* thiz) {
- const HeapObjectHeader* const header =
- HeapObjectHeader::FromInnerAddress(reinterpret_cast<Address>(thiz));
- CHECK(header->IsInConstruction());
- }));
-}
-
-TEST_F(HeapTest, GarbageCollectedMixinIsAliveDuringConstruction) {
- using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
- MakeGarbageCollected<O>(base::BindOnce([](O::Mixin* thiz) {
- LivenessBroker broker = internal::LivenessBrokerFactory::Create();
- CHECK(broker.IsHeapObjectAlive(thiz));
- }));
-
- using P = HeapVector<Member<HeapLinkedHashSet<Member<IntWrapper>>>>;
- MakeGarbageCollected<P>();
- using Q = HeapVector<Member<HeapNewLinkedHashSet<Member<IntWrapper>>>>;
- MakeGarbageCollected<Q>();
-}
-
-TEST_F(HeapTest, PersistentAssignsDeletedValue) {
- // Regression test: https://crbug.com/982313
-
- Persistent<IntWrapper> deleted(WTF::kHashTableDeletedValue);
- Persistent<IntWrapper> pre_initialized(MakeGarbageCollected<IntWrapper>(1));
- pre_initialized = deleted;
- PreciselyCollectGarbage();
-}
-
-struct HeapHashMapWrapper final : GarbageCollected<HeapHashMapWrapper> {
- HeapHashMapWrapper() {
- for (int i = 0; i < 100; ++i) {
- map_.insert(MakeGarbageCollected<IntWrapper>(i),
- NonTriviallyDestructible());
- }
- }
- // This should call ~HeapHapMap() -> ~HashMap() -> ~HashTable().
- ~HeapHashMapWrapper() = default;
-
- void Trace(Visitor* visitor) { visitor->Trace(map_); }
-
- private:
- struct NonTriviallyDestructible {
- ~NonTriviallyDestructible() {}
- };
- HeapHashMap<Member<IntWrapper>, NonTriviallyDestructible> map_;
-};
-
-TEST_F(HeapTest, AccessDeletedBackingStore) {
- // Regression test: https://crbug.com/985443
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(
- blink::features::kBlinkHeapConcurrentSweeping);
- ClearOutOldGarbage();
-
- ThreadState* thread_state = ThreadState::Current();
-
- auto* map = MakeGarbageCollected<HeapHashMapWrapper>();
- // Run marking.
- PreciselyCollectGarbage(BlinkGC::kConcurrentAndLazySweeping);
- // Perform complete sweep on hash_arena.
- BaseArena* hash_arena =
- thread_state->Heap().Arena(BlinkGC::kHashTableArenaIndex);
- {
- ThreadState::AtomicPauseScope scope(thread_state);
- ScriptForbiddenScope script_forbidden_scope;
- ThreadState::SweepForbiddenScope sweep_forbidden(thread_state);
- hash_arena->CompleteSweep();
- }
- BaseArena* map_arena = PageFromObject(map)->Arena();
- // Sweep normal arena, but don't call finalizers.
- while (!map_arena->ConcurrentSweepOnePage()) {
- }
- // Now complete sweeping with PerformIdleLazySweep and call finalizers.
- while (thread_state->IsSweepingInProgress()) {
- thread_state->PerformIdleLazySweep(base::TimeTicks::Max());
- }
-}
-
-class GCBase : public GarbageCollected<GCBase> {
- public:
- virtual void Trace(Visitor*) {}
-};
-
-class GCDerived final : public GCBase {
- public:
- static int destructor_called;
- void Trace(Visitor*) override {}
- ~GCDerived() { ++destructor_called; }
-};
-
-int GCDerived::destructor_called = 0;
-
-TEST_F(HeapTest, CallMostDerivedFinalizer) {
- MakeGarbageCollected<GCDerived>();
- PreciselyCollectGarbage();
- 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 8bd251732ff..4bf47d69328 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
@@ -83,7 +83,8 @@ bool IncrementalMarkingTestDriver::SingleConcurrentStep(
CHECK(thread_state_->IsIncrementalMarking());
if (thread_state_->GetGCState() ==
ThreadState::kIncrementalMarkingStepScheduled) {
- thread_state_->IncrementalMarkingStep(stack_state, base::TimeDelta());
+ thread_state_->SkipIncrementalMarkingForTesting();
+ thread_state_->IncrementalMarkingStep(stack_state);
return true;
}
return false;
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 a2a53e2e96e..12a0fccd1f5 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
@@ -56,7 +56,7 @@ class ObjectWithCallbackBeforeInitializer
base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb)
: bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
- virtual void Trace(Visitor* visitor) { visitor->Trace(value_); }
+ virtual void Trace(Visitor* visitor) const { visitor->Trace(value_); }
T* value() const { return value_.Get(); }
@@ -84,7 +84,7 @@ class MixinWithCallbackBeforeInitializer : public GarbageCollectedMixin {
base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb)
: bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
- void Trace(Visitor* visitor) override { visitor->Trace(value_); }
+ void Trace(Visitor* visitor) const override { visitor->Trace(value_); }
T* value() const { return value_.Get(); }
@@ -124,7 +124,7 @@ class ObjectWithMixinWithCallbackBeforeInitializer
base::OnceCallback<void(Mixin*)>&& cb)
: Mixin(std::move(cb)) {}
- void Trace(Visitor* visitor) override { Mixin::Trace(visitor); }
+ void Trace(Visitor* visitor) const override { Mixin::Trace(visitor); }
};
// Simple linked object to be used in tests.
@@ -137,7 +137,7 @@ class LinkedObject : public GarbageCollected<LinkedObject> {
LinkedObject* next() const { return next_; }
Member<LinkedObject>& next_ref() { return next_; }
- void Trace(Visitor* visitor) { visitor->Trace(next_); }
+ virtual void Trace(Visitor* visitor) const { visitor->Trace(next_); }
private:
Member<LinkedObject> next_;
@@ -181,7 +181,7 @@ class IntegerObject : public GarbageCollected<IntegerObject> {
destructor_calls.fetch_add(1, std::memory_order_relaxed);
}
- virtual void Trace(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
int Value() const { return 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
deleted file mode 100644
index 41e2ae291c0..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc
+++ /dev/null
@@ -1,265 +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 "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/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/thread_state.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/wtf/allocator/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-
-namespace blink {
-
-class HeapThreadTest : public TestSupportingGC {};
-
-class HeapThreadDeathTest : public TestSupportingGC {
- public:
- HeapThreadDeathTest() {
- testing::FLAGS_gtest_death_test_style = "threadsafe";
- }
-};
-
-namespace heap_thread_test {
-
-static Mutex& ActiveThreadMutex() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, active_thread_mutex, ());
- return active_thread_mutex;
-}
-
-static ThreadCondition& ActiveThreadCondition() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadCondition, active_thread_condition,
- (ActiveThreadMutex()));
- return active_thread_condition;
-}
-
-enum ActiveThreadState {
- kNoThreadActive,
- kMainThreadActive,
- kWorkerThreadActive,
-};
-
-static ActiveThreadState& ActiveThread() {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(ActiveThreadState, active_thread,
- (kNoThreadActive));
- return active_thread;
-}
-
-static void WakeMainThread() {
- ActiveThread() = kMainThreadActive;
- ActiveThreadCondition().Signal();
-}
-
-static void WakeWorkerThread() {
- ActiveThread() = kWorkerThreadActive;
- ActiveThreadCondition().Signal();
-}
-
-static void ParkMainThread() {
- while (ActiveThread() != kMainThreadActive) {
- ActiveThreadCondition().Wait();
- }
-}
-
-static void ParkWorkerThread() {
- while (ActiveThread() != kWorkerThreadActive) {
- ActiveThreadCondition().Wait();
- }
-}
-
-class Object : public GarbageCollected<Object> {
- public:
- Object() {}
- void Trace(Visitor* visitor) {}
-};
-
-class AlternatingThreadTester {
- STACK_ALLOCATED();
-
- public:
- void Test() {
- MutexLocker locker(ActiveThreadMutex());
- ActiveThread() = kMainThreadActive;
-
- std::unique_ptr<Thread> worker_thread = Platform::Current()->CreateThread(
- ThreadCreationParams(ThreadType::kTestThread)
- .SetThreadNameForTest("Test Worker Thread"));
- PostCrossThreadTask(
- *worker_thread->GetTaskRunner(), FROM_HERE,
- CrossThreadBindOnce(&AlternatingThreadTester::StartWorkerThread,
- CrossThreadUnretained(this)));
-
- MainThreadMain();
- }
-
- void SwitchToWorkerThread() {
- WakeWorkerThread();
- ParkMainThread();
- }
-
- void SwitchToMainThread() {
- WakeMainThread();
- ParkWorkerThread();
- }
-
- protected:
- // Override with code you want to execute on the main thread.
- virtual void MainThreadMain() = 0;
- // Override with code you want to execute on the worker thread. At the end,
- // the ThreadState is detached and we switch back to the main thread
- // automatically.
- virtual void WorkerThreadMain() = 0;
-
- private:
- void StartWorkerThread() {
- ThreadState::AttachCurrentThread();
-
- MutexLocker locker(ActiveThreadMutex());
-
- WorkerThreadMain();
-
- ThreadState::DetachCurrentThread();
- WakeMainThread();
- }
-};
-
-class MemberSameThreadCheckTester : public AlternatingThreadTester {
- private:
- void MainThreadMain() override { SwitchToWorkerThread(); }
-
- void WorkerThreadMain() override {
- // Setting an object created on the worker thread to a Member allocated on
- // the main thread is not allowed.
- object_ = MakeGarbageCollected<Object>();
- }
-
- Member<Object> object_;
-};
-
-#if DCHECK_IS_ON()
-TEST_F(HeapThreadDeathTest, MemberSameThreadCheck) {
- EXPECT_DEATH(MemberSameThreadCheckTester().Test(), "");
-}
-#endif
-
-class PersistentSameThreadCheckTester : public AlternatingThreadTester {
- private:
- void MainThreadMain() override { SwitchToWorkerThread(); }
-
- void WorkerThreadMain() override {
- // Setting an object created on the worker thread to a Persistent allocated
- // on the main thread is not allowed.
- object_ = MakeGarbageCollected<Object>();
- }
-
- Persistent<Object> object_;
-};
-
-#if DCHECK_IS_ON()
-TEST_F(HeapThreadDeathTest, PersistentSameThreadCheck) {
- EXPECT_DEATH(PersistentSameThreadCheckTester().Test(), "");
-}
-#endif
-
-class MarkingSameThreadCheckTester : public AlternatingThreadTester {
- private:
- class MainThreadObject final : public GarbageCollected<MainThreadObject> {
- public:
- void Trace(Visitor* visitor) { visitor->Trace(set_); }
- void AddToSet(Object* object) { set_.insert(42, object); }
-
- private:
- HeapHashMap<int, Member<Object>> set_;
- };
-
- void MainThreadMain() override {
- main_thread_object_ = MakeGarbageCollected<MainThreadObject>();
-
- SwitchToWorkerThread();
-
- // This will try to mark MainThreadObject when it tries to mark Object
- // it should crash.
- TestSupportingGC::PreciselyCollectGarbage();
- }
-
- void WorkerThreadMain() override {
- // Adding a reference to an object created on the worker thread to a
- // HeapHashMap created on the main thread is not allowed.
- main_thread_object_->AddToSet(MakeGarbageCollected<Object>());
- }
-
- CrossThreadPersistent<MainThreadObject> main_thread_object_;
-};
-
-#if DCHECK_IS_ON()
-TEST_F(HeapThreadDeathTest, DISABLED_MarkingSameThreadCheck) {
- // This will crash during marking, at the DCHECK in Visitor::markHeader() or
- // earlier.
- EXPECT_DEATH(MarkingSameThreadCheckTester().Test(), "");
-}
-#endif
-
-class DestructorLockingObject
- : public GarbageCollected<DestructorLockingObject> {
- public:
- DestructorLockingObject() = default;
- virtual ~DestructorLockingObject() { ++destructor_calls_; }
-
- static int destructor_calls_;
- void Trace(Visitor* visitor) {}
-};
-
-int DestructorLockingObject::destructor_calls_ = 0;
-
-class CrossThreadWeakPersistentTester : public AlternatingThreadTester {
- private:
- void MainThreadMain() override {
- // Create an object in the worker thread, have a CrossThreadWeakPersistent
- // pointing to it on the main thread, run a GC in the worker thread, and see
- // if the CrossThreadWeakPersistent is cleared.
-
- DestructorLockingObject::destructor_calls_ = 0;
-
- // Step 1: Initiate a worker thread, and wait for |Object| to get allocated
- // on the worker thread.
- SwitchToWorkerThread();
-
- // Step 3: Set up a CrossThreadWeakPersistent.
- ASSERT_TRUE(object_);
- EXPECT_EQ(0, DestructorLockingObject::destructor_calls_);
-
- // Pretend we have no pointers on stack during the step 4.
- SwitchToWorkerThread();
-
- // Step 5: Make sure the weak persistent is cleared.
- EXPECT_FALSE(object_.Get());
- EXPECT_EQ(1, DestructorLockingObject::destructor_calls_);
-
- SwitchToWorkerThread();
- }
-
- void WorkerThreadMain() override {
- // Step 2: Create an object and store the pointer.
- object_ = MakeGarbageCollected<DestructorLockingObject>();
- SwitchToMainThread();
-
- // Step 4: Run a GC.
- ThreadState::Current()->CollectAllGarbageForTesting(
- BlinkGC::kNoHeapPointersOnStack);
- SwitchToMainThread();
- }
-
- CrossThreadWeakPersistent<DestructorLockingObject> object_;
-};
-
-TEST_F(HeapThreadTest, CrossThreadWeakPersistent) {
- CrossThreadWeakPersistentTester().Test();
-}
-
-} // namespace heap_thread_test
-} // namespace blink
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
deleted file mode 100644
index 2e50a064992..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 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 <type_traits>
-#include <utility>
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/heap/heap_traits.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-// No gtest tests; only static_assert checks.
-
-namespace blink {
-
-class Visitor;
-
-namespace {
-
-struct Empty {};
-
-// Similar to an IDL union or dictionary, which have Trace() methods but are
-// not garbage-collected types themselves.
-struct StructWithTraceMethod {
- void Trace(Visitor*) {}
-};
-
-struct GarbageCollectedStruct
- : public GarbageCollected<GarbageCollectedStruct> {
- void Trace(Visitor*) {}
-};
-
-// AddMemberIfNeeded<T>
-static_assert(std::is_same<AddMemberIfNeeded<double>, double>::value,
- "AddMemberIfNeeded<double> must not add a Member wrapper");
-static_assert(std::is_same<AddMemberIfNeeded<double*>, double*>::value,
- "AddMemberIfNeeded<double*> must not add a Member wrapper");
-
-static_assert(std::is_same<AddMemberIfNeeded<Empty>, Empty>::value,
- "AddMemberIfNeeded<Empty> must not add a Member wrapper");
-
-static_assert(
- std::is_same<AddMemberIfNeeded<StructWithTraceMethod>,
- StructWithTraceMethod>::value,
- "AddMemberIfNeeded<StructWithTraceMethod> must not add a Member wrapper");
-
-static_assert(
- std::is_same<AddMemberIfNeeded<GarbageCollectedStruct>,
- Member<GarbageCollectedStruct>>::value,
- "AddMemberIfNeeded<GarbageCollectedStruct> must not add a Member wrapper");
-
-static_assert(
- std::is_same<AddMemberIfNeeded<HeapVector<Member<GarbageCollectedStruct>>>,
- Member<HeapVector<Member<GarbageCollectedStruct>>>>::value,
- "AddMemberIfNeeded on a HeapVector<Member<T>> must wrap it in a Member<>");
-
-// VectorOf<T>
-static_assert(std::is_same<VectorOf<double>, Vector<double>>::value,
- "VectorOf<double> should use a Vector");
-static_assert(std::is_same<VectorOf<double*>, Vector<double*>>::value,
- "VectorOf<double*> should use a Vector");
-static_assert(std::is_same<VectorOf<Empty>, Vector<Empty>>::value,
- "VectorOf<Empty> should use a Vector");
-
-static_assert(
- std::is_same<VectorOf<StructWithTraceMethod>,
- HeapVector<StructWithTraceMethod>>::value,
- "VectorOf<StructWithTraceMethod> must not add a Member<> wrapper");
-static_assert(std::is_same<VectorOf<GarbageCollectedStruct>,
- HeapVector<Member<GarbageCollectedStruct>>>::value,
- "VectorOf<GarbageCollectedStruct> must add a Member<> wrapper");
-
-static_assert(
- std::is_same<VectorOf<Vector<double>>, Vector<Vector<double>>>::value,
- "Nested Vectors must not add HeapVectors");
-static_assert(
- std::is_same<VectorOf<HeapVector<StructWithTraceMethod>>,
- HeapVector<Member<HeapVector<StructWithTraceMethod>>>>::value,
- "Nested HeapVector<StructWithTraceMethod> must add a HeapVector");
-static_assert(
- std::is_same<
- VectorOf<HeapVector<Member<GarbageCollectedStruct>>>,
- HeapVector<Member<HeapVector<Member<GarbageCollectedStruct>>>>>::value,
- "Nested HeapVectors must not add Vectors");
-
-// VectorOfPairs<T, U>
-static_assert(std::is_same<VectorOfPairs<int, double>,
- Vector<std::pair<int, double>>>::value,
- "POD types must use a regular Vector");
-static_assert(std::is_same<VectorOfPairs<Empty, double>,
- Vector<std::pair<Empty, double>>>::value,
- "POD types must use a regular Vector");
-
-static_assert(
- std::is_same<VectorOfPairs<StructWithTraceMethod, float>,
- HeapVector<std::pair<StructWithTraceMethod, float>>>::value,
- "StructWithTraceMethod causes a HeapVector to be used");
-static_assert(
- std::is_same<VectorOfPairs<float, StructWithTraceMethod>,
- HeapVector<std::pair<float, StructWithTraceMethod>>>::value,
- "StructWithTraceMethod causes a HeapVector to be used");
-static_assert(
- std::is_same<VectorOfPairs<StructWithTraceMethod, StructWithTraceMethod>,
- HeapVector<std::pair<StructWithTraceMethod,
- StructWithTraceMethod>>>::value,
- "StructWithTraceMethod causes a HeapVector to be used");
-
-static_assert(
- std::is_same<
- VectorOfPairs<GarbageCollectedStruct, float>,
- HeapVector<std::pair<Member<GarbageCollectedStruct>, float>>>::value,
- "GarbageCollectedStruct causes a HeapVector to be used");
-static_assert(
- std::is_same<
- VectorOfPairs<float, GarbageCollectedStruct>,
- HeapVector<std::pair<float, Member<GarbageCollectedStruct>>>>::value,
- "GarbageCollectedStruct causes a HeapVector to be used");
-static_assert(
- std::is_same<VectorOfPairs<GarbageCollectedStruct, GarbageCollectedStruct>,
- HeapVector<std::pair<Member<GarbageCollectedStruct>,
- Member<GarbageCollectedStruct>>>>::value,
- "GarbageCollectedStruct causes a HeapVector to be used");
-
-} // namespace
-
-} // namespace blink
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
deleted file mode 100644
index dcd27e6b7fc..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ /dev/null
@@ -1,1889 +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 <initializer_list>
-
-#include "base/bind.h"
-#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/bindings/script_forbidden_scope.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/heap/heap_allocator.h"
-#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
-#include "third_party/blink/renderer/platform/heap/heap_compact.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/heap/member.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/heap/trace_traits.h"
-#include "third_party/blink/renderer/platform/heap/visitor.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class IncrementalMarkingTest : public TestSupportingGC {};
-
-namespace incremental_marking_test {
-
-// Visitor that expects every directly reachable object from a given backing
-// store to be in the set of provided objects.
-class BackingVisitor : public Visitor {
- public:
- BackingVisitor(ThreadState* state, Vector<void*>* objects)
- : Visitor(state), objects_(objects) {}
- ~BackingVisitor() final {}
-
- void ProcessBackingStore(HeapObjectHeader* header) {
- EXPECT_TRUE(header->IsMarked());
- header->Unmark();
-
- GCInfo::From(header->GcInfoIndex()).trace(this, header->Payload());
- }
-
- void Visit(const void* obj, TraceDescriptor desc) final {
- EXPECT_TRUE(obj);
- auto** pos = std::find(objects_->begin(), objects_->end(), obj);
- if (objects_->end() != pos)
- objects_->erase(pos);
- // The garbage collector will find those objects so we can mark them.
- HeapObjectHeader* const header =
- HeapObjectHeader::FromPayload(desc.base_object_payload);
- if (!header->IsMarked())
- EXPECT_TRUE(header->TryMark());
- }
-
- void VisitEphemeron(const void* key,
- const void* value,
- TraceCallback value_trace_callback) final {
- if (!HeapObjectHeader::FromPayload(key)->IsMarked())
- return;
- value_trace_callback(this, value);
- }
-
- private:
- Vector<void*>* objects_;
-};
-
-// Base class for initializing worklists.
-class IncrementalMarkingScopeBase {
- DISALLOW_NEW();
-
- public:
- explicit IncrementalMarkingScopeBase(ThreadState* thread_state)
- : thread_state_(thread_state), heap_(thread_state_->Heap()) {
- if (thread_state_->IsMarkingInProgress() ||
- thread_state_->IsSweepingInProgress()) {
- TestSupportingGC::PreciselyCollectGarbage();
- }
- heap_.SetupWorklists(false);
- }
-
- ~IncrementalMarkingScopeBase() {
- heap_.DestroyMarkingWorklists(BlinkGC::StackState::kNoHeapPointersOnStack);
- heap_.DestroyCompactionWorklists();
- }
-
- ThreadHeap& heap() const { return heap_; }
-
- protected:
- ThreadState* const thread_state_;
- ThreadHeap& heap_;
-};
-
-class IncrementalMarkingScope : public IncrementalMarkingScopeBase {
- public:
- explicit IncrementalMarkingScope(ThreadState* thread_state)
- : IncrementalMarkingScopeBase(thread_state),
- gc_forbidden_scope_(thread_state),
- marking_worklist_(heap_.GetMarkingWorklist()),
- write_barrier_worklist_(heap_.GetWriteBarrierWorklist()),
- not_fully_constructed_worklist_(
- heap_.GetNotFullyConstructedWorklist()) {
- thread_state_->SetGCPhase(ThreadState::GCPhase::kMarking);
- ThreadState::AtomicPauseScope atomic_pause_scope_(thread_state_);
- ScriptForbiddenScope script_forbidden_scope;
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
- thread_state->EnableIncrementalMarkingBarrier();
- thread_state->current_gc_data_.visitor = std::make_unique<MarkingVisitor>(
- thread_state, MarkingVisitor::kGlobalMarking);
- }
-
- ~IncrementalMarkingScope() {
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(not_fully_constructed_worklist_->IsGlobalEmpty());
- thread_state_->DisableIncrementalMarkingBarrier();
- // Need to clear out unused worklists that might have been polluted during
- // test.
- heap_.GetWeakCallbackWorklist()->Clear();
- thread_state_->SetGCPhase(ThreadState::GCPhase::kSweeping);
- thread_state_->SetGCPhase(ThreadState::GCPhase::kNone);
- }
-
- MarkingWorklist* marking_worklist() const { return marking_worklist_; }
- WriteBarrierWorklist* write_barrier_worklist() const {
- return write_barrier_worklist_;
- }
- NotFullyConstructedWorklist* not_fully_constructed_worklist() const {
- return not_fully_constructed_worklist_;
- }
-
- protected:
- ThreadState::GCForbiddenScope gc_forbidden_scope_;
- MarkingWorklist* const marking_worklist_;
- WriteBarrierWorklist* const write_barrier_worklist_;
- NotFullyConstructedWorklist* const not_fully_constructed_worklist_;
-};
-
-// Expects that the write barrier fires for the objects passed to the
-// constructor. This requires that the objects are added to the marking stack
-// as well as headers being marked.
-class ExpectWriteBarrierFires : public IncrementalMarkingScope {
- public:
- ExpectWriteBarrierFires(ThreadState* thread_state,
- std::initializer_list<void*> objects)
- : IncrementalMarkingScope(thread_state),
- objects_(objects),
- backing_visitor_(thread_state_, &objects_) {
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- for (void* object : objects_) {
- // Ensure that the object is in the normal arena so we can ignore backing
- // objects on the marking stack.
- CHECK(ThreadHeap::IsNormalArenaIndex(
- PageFromObject(object)->Arena()->ArenaIndex()));
- headers_.push_back(HeapObjectHeader::FromPayload(object));
- EXPECT_FALSE(headers_.back()->IsMarked());
- }
- EXPECT_FALSE(objects_.IsEmpty());
- }
-
- ~ExpectWriteBarrierFires() {
- // All objects watched should be on the marking or write barrier worklist.
- MarkingItem item;
- while (marking_worklist_->Pop(WorklistTaskId::MutatorThread, &item)) {
- // Inspect backing stores to allow specifying objects that are only
- // reachable through a backing store.
- if (!ThreadHeap::IsNormalArenaIndex(
- PageFromObject(item.base_object_payload)
- ->Arena()
- ->ArenaIndex())) {
- backing_visitor_.ProcessBackingStore(
- HeapObjectHeader::FromPayload(item.base_object_payload));
- continue;
- }
- auto** pos =
- std::find(objects_.begin(), objects_.end(), item.base_object_payload);
- if (objects_.end() != pos)
- objects_.erase(pos);
- }
- HeapObjectHeader* header;
- while (
- write_barrier_worklist_->Pop(WorklistTaskId::MutatorThread, &header)) {
- // Inspect backing stores to allow specifying objects that are only
- // reachable through a backing store.
- if (!ThreadHeap::IsNormalArenaIndex(
- PageFromObject(header->Payload())->Arena()->ArenaIndex())) {
- backing_visitor_.ProcessBackingStore(header);
- continue;
- }
- auto** pos =
- std::find(objects_.begin(), objects_.end(), header->Payload());
- if (objects_.end() != pos)
- objects_.erase(pos);
- }
- EXPECT_TRUE(objects_.IsEmpty());
- // All headers of objects watched should be marked at this point.
- for (HeapObjectHeader* header : headers_) {
- EXPECT_TRUE(header->IsMarked());
- header->Unmark();
- }
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- }
-
- private:
- Vector<void*> objects_;
- Vector<HeapObjectHeader*> headers_;
- BackingVisitor backing_visitor_;
-};
-
-// Expects that no write barrier fires for the objects passed to the
-// constructor. This requires that the marking stack stays empty and the marking
-// state of the object stays the same across the lifetime of the scope.
-class ExpectNoWriteBarrierFires : public IncrementalMarkingScope {
- public:
- ExpectNoWriteBarrierFires(ThreadState* thread_state,
- std::initializer_list<void*> objects)
- : IncrementalMarkingScope(thread_state) {
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- for (void* object : objects_) {
- HeapObjectHeader* header = HeapObjectHeader::FromPayload(object);
- headers_.push_back(std::make_pair(header, header->IsMarked()));
- }
- }
-
- ~ExpectNoWriteBarrierFires() {
- EXPECT_TRUE(marking_worklist_->IsGlobalEmpty());
- EXPECT_TRUE(write_barrier_worklist_->IsGlobalEmpty());
- for (const auto& pair : headers_) {
- EXPECT_EQ(pair.second, pair.first->IsMarked());
- pair.first->Unmark();
- }
- }
-
- private:
- Vector<void*> objects_;
- Vector<std::pair<HeapObjectHeader*, bool /* was marked */>> headers_;
-};
-
-class Object : public LinkedObject {
- public:
- Object() = default;
- explicit Object(Object* next) : LinkedObject(next) {}
-
- bool IsMarked() const {
- return HeapObjectHeader::FromPayload(this)->IsMarked();
- }
-
- 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. ===============================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, EnableDisableBarrier) {
- EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
- ThreadState::Current()->EnableIncrementalMarkingBarrier();
- EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
- EXPECT_TRUE(ThreadState::IsAnyIncrementalMarking());
- ThreadState::Current()->DisableIncrementalMarkingBarrier();
- EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
-}
-
-TEST_F(IncrementalMarkingTest, ManualWriteBarrierTriggersWhenMarkingIsOn) {
- auto* object1 = MakeGarbageCollected<Object>();
- auto* object2 = MakeGarbageCollected<RawPtrObjectWithManualWriteBarrier>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {object1});
- EXPECT_FALSE(object1->IsMarked());
- object2->Set(object1);
- EXPECT_TRUE(object1->IsMarked());
- }
-}
-
-TEST_F(IncrementalMarkingTest, ManualWriteBarrierBailoutWhenMarkingIsOff) {
- auto* object1 = MakeGarbageCollected<Object>();
- auto* object2 = MakeGarbageCollected<RawPtrObjectWithManualWriteBarrier>();
- EXPECT_FALSE(object1->IsMarked());
- object2->Set(object1);
- EXPECT_FALSE(object1->IsMarked());
-}
-
-// =============================================================================
-// Member<T> support. ==========================================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, MemberSetUnmarkedObject) {
- auto* parent = MakeGarbageCollected<Object>();
- auto* child = MakeGarbageCollected<Object>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {child});
- EXPECT_FALSE(child->IsMarked());
- parent->set_next(child);
- EXPECT_TRUE(child->IsMarked());
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberSetMarkedObjectNoBarrier) {
- auto* parent = MakeGarbageCollected<Object>();
- auto* child = MakeGarbageCollected<Object>();
- EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark());
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {child});
- parent->set_next(child);
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberInitializingStoreNoBarrier) {
- auto* object1 = MakeGarbageCollected<Object>();
- HeapObjectHeader* object1_header = HeapObjectHeader::FromPayload(object1);
- {
- IncrementalMarkingScope scope(ThreadState::Current());
- EXPECT_FALSE(object1_header->IsMarked());
- auto* object2 = MakeGarbageCollected<Object>(object1);
- HeapObjectHeader* object2_header = HeapObjectHeader::FromPayload(object2);
- EXPECT_FALSE(object1_header->IsMarked());
- EXPECT_FALSE(object2_header->IsMarked());
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberReferenceAssignMember) {
- 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;
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) {
- auto* obj = MakeGarbageCollected<LinkedObject>();
- Member<LinkedObject>& m = obj->next_ref();
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
- m = WTF::kHashTableDeletedValue;
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberCopyDeletedValueNoBarrier) {
- auto* obj1 = MakeGarbageCollected<LinkedObject>();
- Member<LinkedObject>& m1 = obj1->next_ref();
- m1 = WTF::kHashTableDeletedValue;
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
- auto* obj2 = MakeGarbageCollected<LinkedObject>();
- obj2->next_ref() = m1;
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberHashTraitConstructDeletedValueNoBarrier) {
- auto* obj = MakeGarbageCollected<LinkedObject>();
- Member<LinkedObject>& m = obj->next_ref();
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
- HashTraits<Member<LinkedObject>>::ConstructDeletedValue(m, false);
- }
-}
-
-TEST_F(IncrementalMarkingTest, MemberHashTraitIsDeletedValueNoBarrier) {
- auto* obj =
- MakeGarbageCollected<LinkedObject>(MakeGarbageCollected<LinkedObject>());
- Member<LinkedObject>& m = obj->next_ref();
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {});
- EXPECT_FALSE(HashTraits<Member<LinkedObject>>::IsDeletedValue(m));
- }
-}
-
-// =============================================================================
-// Mixin support. ==============================================================
-// =============================================================================
-
-namespace {
-
-class Mixin : public GarbageCollectedMixin {
- public:
- Mixin() : next_(nullptr) {}
- virtual ~Mixin() {}
-
- void Trace(Visitor* visitor) override { visitor->Trace(next_); }
-
- virtual void Bar() {}
-
- protected:
- Member<Object> next_;
-};
-
-class ClassWithVirtual {
- protected:
- virtual void Foo() {}
-};
-
-class Child : public GarbageCollected<Child>,
- public ClassWithVirtual,
- public Mixin {
- USING_GARBAGE_COLLECTED_MIXIN(Child);
-
- public:
- Child() : ClassWithVirtual(), Mixin() {}
- ~Child() override {}
-
- void Trace(Visitor* visitor) override { Mixin::Trace(visitor); }
-
- void Foo() override {}
- void Bar() override {}
-};
-
-class ParentWithMixinPointer : public GarbageCollected<ParentWithMixinPointer> {
- public:
- ParentWithMixinPointer() : mixin_(nullptr) {}
-
- void set_mixin(Mixin* mixin) { mixin_ = mixin; }
-
- virtual void Trace(Visitor* visitor) { visitor->Trace(mixin_); }
-
- protected:
- Member<Mixin> mixin_;
-};
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, WriteBarrierOnUnmarkedMixinApplication) {
- ParentWithMixinPointer* parent =
- MakeGarbageCollected<ParentWithMixinPointer>();
- auto* child = MakeGarbageCollected<Child>();
- Mixin* mixin = static_cast<Mixin*>(child);
- EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {child});
- parent->set_mixin(mixin);
- }
-}
-
-TEST_F(IncrementalMarkingTest, NoWriteBarrierOnMarkedMixinApplication) {
- ParentWithMixinPointer* parent =
- MakeGarbageCollected<ParentWithMixinPointer>();
- auto* child = MakeGarbageCollected<Child>();
- EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark());
- Mixin* mixin = static_cast<Mixin*>(child);
- EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin));
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {child});
- parent->set_mixin(mixin);
- }
-}
-
-// =============================================================================
-// HeapVector support. =========================================================
-// =============================================================================
-
-namespace {
-
-// HeapVector allows for insertion of container objects that can be traced but
-// are themselves non-garbage collected.
-class NonGarbageCollectedContainer {
- DISALLOW_NEW();
-
- public:
- NonGarbageCollectedContainer(Object* obj, int y) : obj_(obj), y_(y) {}
-
- virtual ~NonGarbageCollectedContainer() {}
- virtual void Trace(Visitor* visitor) { visitor->Trace(obj_); }
-
- private:
- Member<Object> obj_;
- int y_;
-};
-
-class NonGarbageCollectedContainerRoot {
- DISALLOW_NEW();
-
- public:
- NonGarbageCollectedContainerRoot(Object* obj1, Object* obj2, int y)
- : next_(obj1, y), obj_(obj2) {}
- virtual ~NonGarbageCollectedContainerRoot() {}
-
- virtual void Trace(Visitor* visitor) {
- visitor->Trace(next_);
- visitor->Trace(obj_);
- }
-
- private:
- NonGarbageCollectedContainer next_;
- Member<Object> obj_;
-};
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, HeapVectorPushBackMember) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- vec->push_back(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorPushBackNonGCedContainer) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- vec->push_back(NonGarbageCollectedContainer(obj, 1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorPushBackStdPair) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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)));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackMember) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- vec->emplace_back(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackNonGCedContainer) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- vec->emplace_back(obj, 1);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackStdPair) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* vec = MakeGarbageCollected<
- HeapVector<std::pair<Member<Object>, Member<Object>>>>();
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- vec->emplace_back(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorCopyMember) {
- auto* object = MakeGarbageCollected<Object>();
- auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>();
- vec1->push_back(object);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
- MakeGarbageCollected<HeapVector<Member<Object>>>(*vec1);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorCopyNonGCedContainer) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
- vec1->emplace_back(obj, 1);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(*vec1);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorCopyStdPair) {
- using ValueType = std::pair<Member<Object>, Member<Object>>;
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* vec1 = MakeGarbageCollected<HeapVector<ValueType>>();
- vec1->emplace_back(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- MakeGarbageCollected<HeapVector<ValueType>>(*vec1);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorMoveMember) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>();
- vec1->push_back(obj);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- MakeGarbageCollected<HeapVector<Member<Object>>>(std::move(*vec1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorMoveNonGCedContainer) {
- auto* obj = MakeGarbageCollected<Object>();
- auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>();
- vec1->emplace_back(obj, 1);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- 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>();
- auto* vec1 = MakeGarbageCollected<VectorType>();
- vec1->emplace_back(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- MakeGarbageCollected<VectorType>(std::move(*vec1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorSwapMember) {
- using VectorType = HeapVector<Member<Object>>;
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorSwapNonGCedContainer) {
- using VectorType = HeapVector<NonGarbageCollectedContainer>;
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorSwapStdPair) {
- using ValueType = std::pair<Member<Object>, Member<Object>>;
- using VectorType = HeapVector<ValueType>;
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorSubscriptOperator) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapVector<Member<Object>> vec;
- vec.push_back(obj1);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj2});
- EXPECT_EQ(1u, vec.size());
- EXPECT_EQ(obj1, vec[0]);
- vec[0] = obj2;
- EXPECT_EQ(obj2, vec[0]);
- EXPECT_FALSE(obj1->IsMarked());
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapVectorEagerTracingStopsAtMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- obj1->set_next(obj3);
- HeapVector<NonGarbageCollectedContainerRoot> vec;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- vec.emplace_back(obj1, obj2, 3);
- // |obj3| is only reachable from |obj1| which is not eagerly traced. Only
- // objects without object headers are eagerly traced.
- EXPECT_FALSE(obj3->IsMarked());
- }
-}
-
-// =============================================================================
-// HeapDeque support. ==========================================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, HeapDequePushBackMember) {
- auto* obj = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- deq.push_back(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequePushFrontMember) {
- auto* obj = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- deq.push_front(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequeEmplaceBackMember) {
- auto* obj = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- deq.emplace_back(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequeEmplaceFrontMember) {
- auto* obj = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- deq.emplace_front(obj);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequeCopyMember) {
- auto* object = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq1;
- deq1.push_back(object);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
- HeapDeque<Member<Object>> deq2(deq1);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequeMoveMember) {
- auto* object = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq1;
- deq1.push_back(object);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {object});
- HeapDeque<Member<Object>> deq2(std::move(deq1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapDequeSwapMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapDeque<Member<Object>> deq1;
- deq1.push_back(obj1);
- HeapDeque<Member<Object>> deq2;
- deq2.push_back(obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- std::swap(deq1, deq2);
- }
-}
-
-// =============================================================================
-// HeapHashSet support. ========================================================
-// =============================================================================
-
-namespace {
-
-template <typename Container>
-void Insert() {
- auto* obj = MakeGarbageCollected<Object>();
- Container container;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- container.insert(obj);
- }
-}
-
-template <typename Container>
-void InsertNoBarrier() {
- auto* obj = MakeGarbageCollected<Object>();
- Container container;
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
- container.insert(obj);
- }
-}
-
-template <typename Container>
-void Copy() {
- auto* obj = MakeGarbageCollected<Object>();
- Container container1;
- container1.insert(obj);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- Container container2(container1);
- EXPECT_TRUE(container1.Contains(obj));
- EXPECT_TRUE(container2.Contains(obj));
- }
-}
-
-template <typename Container>
-void CopyNoBarrier() {
- auto* obj = MakeGarbageCollected<Object>();
- Container container1;
- container1.insert(obj);
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
- Container container2(container1);
- EXPECT_TRUE(container1.Contains(obj));
- EXPECT_TRUE(container2.Contains(obj));
- }
-}
-
-template <typename Container>
-void Move() {
- auto* obj = MakeGarbageCollected<Object>();
- auto* container1 = MakeGarbageCollected<Container>();
- auto* container2 = MakeGarbageCollected<Container>();
- container1->insert(obj);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj});
- *container2 = std::move(*container1);
- }
-}
-
-template <typename Container>
-void MoveNoBarrier() {
- auto* obj = MakeGarbageCollected<Object>();
- auto* container1 = MakeGarbageCollected<Container>();
- container1->insert(obj);
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj});
- auto* container2 = MakeGarbageCollected<Container>(std::move(*container1));
- }
-}
-
-template <typename Container>
-void Swap() {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* container1 = MakeGarbageCollected<Container>();
- container1->insert(obj1);
- auto* container2 = MakeGarbageCollected<Container>();
- container2->insert(obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- std::swap(*container1, *container2);
- }
-}
-
-template <typename Container>
-void SwapNoBarrier() {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* container1 = MakeGarbageCollected<Container>();
- container1->insert(obj1);
- auto* container2 = MakeGarbageCollected<Container>();
- container2->insert(obj2);
- {
- ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- std::swap(*container1, *container2);
- }
-}
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, HeapHashSetInsert) {
- Insert<HeapHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Insert<HeapHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashSetCopy) {
- Copy<HeapHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Copy<HeapHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashSetMove) {
- Move<HeapHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Move<HeapHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashSetSwap) {
- Swap<HeapHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Swap<HeapHashSet<WeakMember<Object>>>();
-}
-
-// =============================================================================
-// HeapLinkedHashSet support. ==================================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, HeapLinkedHashSetInsert) {
- Insert<HeapLinkedHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Insert<HeapLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLinkedHashSetCopy) {
- Copy<HeapLinkedHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Copy<HeapLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLinkedHashSetMove) {
- Move<HeapLinkedHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Move<HeapLinkedHashSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapLinkedHashSetSwap) {
- Swap<HeapLinkedHashSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Swap<HeapLinkedHashSet<WeakMember<Object>>>();
-}
-
-// TODO(keinakashima): add tests for NewLinkedHashSet after supporting
-// WeakMember
-
-// =============================================================================
-// HeapHashCountedSet support. =================================================
-// =============================================================================
-
-// HeapHashCountedSet does not support copy or move.
-
-TEST_F(IncrementalMarkingTest, HeapHashCountedSetInsert) {
- Insert<HeapHashCountedSet<Member<Object>>>();
- // Weak references are strongified for the current cycle.
- Insert<HeapHashCountedSet<WeakMember<Object>>>();
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashCountedSetSwap) {
- // HeapHashCountedSet is not move constructible so we cannot use std::swap.
- {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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);
- }
- }
- {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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);
- }
- }
-}
-
-// =============================================================================
-// HeapHashMap support. ========================================================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, HeapHashMapInsertMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- map.insert(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapInsertWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<WeakMember<Object>, WeakMember<Object>> map;
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- map.insert(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapInsertMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, WeakMember<Object>> map;
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- map.insert(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapInsertWeakMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<WeakMember<Object>, Member<Object>> map;
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- map.insert(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSetMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- map.Set(obj1, obj2);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSetMemberUpdateValue) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- map.insert(obj1, obj2);
- {
- // Only |obj3| is newly added to |map|, so we only expect the barrier to
- // fire on this one.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
- map.Set(obj1, obj3);
- EXPECT_FALSE(HeapObjectHeader::FromPayload(obj1)->IsMarked());
- EXPECT_FALSE(HeapObjectHeader::FromPayload(obj2)->IsMarked());
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapIteratorChangeKey) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- map.insert(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
- auto it = map.find(obj1);
- EXPECT_NE(map.end(), it);
- it->key = obj3;
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapIteratorChangeValue) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- map.insert(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj3});
- auto it = map.find(obj1);
- EXPECT_NE(map.end(), it);
- it->value = obj3;
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map1;
- map1.insert(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- EXPECT_TRUE(map1.Contains(obj1));
- HeapHashMap<Member<Object>, Member<Object>> map2(map1);
- EXPECT_TRUE(map1.Contains(obj1));
- EXPECT_TRUE(map2.Contains(obj1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyWeakMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1;
- map1.insert(obj1, obj2);
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- EXPECT_TRUE(map1.Contains(obj1));
- HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(map1);
- EXPECT_TRUE(map1.Contains(obj1));
- EXPECT_TRUE(map2.Contains(obj1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, WeakMember<Object>> map1;
- map1.insert(obj1, obj2);
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- EXPECT_TRUE(map1.Contains(obj1));
- HeapHashMap<Member<Object>, WeakMember<Object>> map2(map1);
- EXPECT_TRUE(map1.Contains(obj1));
- EXPECT_TRUE(map2.Contains(obj1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyWeakMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<WeakMember<Object>, Member<Object>> map1;
- map1.insert(obj1, obj2);
- {
- // Weak references are strongified for the current cycle.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- EXPECT_TRUE(map1.Contains(obj1));
- HeapHashMap<WeakMember<Object>, Member<Object>> map2(map1);
- EXPECT_TRUE(map1.Contains(obj1));
- EXPECT_TRUE(map2.Contains(obj1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapMoveMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* map1 =
- MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>();
- map1->insert(obj1, obj2);
- {
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2});
- MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>(
- std::move(*map1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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});
- MakeGarbageCollected<HeapHashMap<WeakMember<Object>, WeakMember<Object>>>(
- std::move(*map1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapMoveMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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});
- MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>(
- std::move(*map1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- 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});
- MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>(
- std::move(*map1));
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- auto* obj4 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- auto* obj4 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberWeakMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- auto* obj4 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- auto* obj3 = MakeGarbageCollected<Object>();
- auto* obj4 = MakeGarbageCollected<Object>();
- 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);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyKeysToVectorMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- map.insert(obj1, obj2);
- HeapVector<Member<Object>> vec;
- {
- // Only key should have its write barrier fired. A write barrier call for
- // value hints to an inefficient implementation.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1});
- CopyKeysToVector(map, vec);
- }
-}
-
-TEST_F(IncrementalMarkingTest, HeapHashMapCopyValuesToVectorMember) {
- auto* obj1 = MakeGarbageCollected<Object>();
- auto* obj2 = MakeGarbageCollected<Object>();
- HeapHashMap<Member<Object>, Member<Object>> map;
- map.insert(obj1, obj2);
- HeapVector<Member<Object>> vec;
- {
- // Only value should have its write barrier fired. A write barrier call for
- // key hints to an inefficient implementation.
- ExpectWriteBarrierFires scope(ThreadState::Current(), {obj2});
- CopyValuesToVector(map, vec);
- }
-}
-
-// TODO(keishi) Non-weak hash table backings should be promptly freed but they
-// are currently not because we emit write barriers for the backings, and we
-// don't free marked backings.
-TEST_F(IncrementalMarkingTest, DISABLED_WeakHashMapPromptlyFreeDisabled) {
- ThreadState* state = ThreadState::Current();
- state->SetGCState(ThreadState::kIncrementalMarkingStepScheduled);
- Persistent<Object> obj1 = MakeGarbageCollected<Object>();
- NormalPageArena* arena = static_cast<NormalPageArena*>(
- ThreadState::Current()->Heap().Arena(BlinkGC::kHashTableArenaIndex));
- CHECK(arena);
- {
- size_t before = arena->promptly_freed_size();
- // Create two maps so we don't promptly free at the allocation point.
- HeapHashMap<WeakMember<Object>, Member<Object>> weak_map1;
- HeapHashMap<WeakMember<Object>, Member<Object>> weak_map2;
- weak_map1.insert(obj1, obj1);
- weak_map2.insert(obj1, obj1);
- weak_map1.clear();
- size_t after = arena->promptly_freed_size();
- // Weak hash table backings should not be promptly freed.
- EXPECT_EQ(after, before);
- }
- {
- size_t before = arena->promptly_freed_size();
- // Create two maps so we don't promptly free at the allocation point.
- HeapHashMap<Member<Object>, Member<Object>> map1;
- HeapHashMap<Member<Object>, Member<Object>> map2;
- map1.insert(obj1, obj1);
- map2.insert(obj1, obj1);
- map1.clear();
- size_t after = arena->promptly_freed_size();
- // Non-weak hash table backings should be promptly freed.
- EXPECT_GT(after, before);
- }
- state->SetGCState(ThreadState::kIncrementalMarkingFinalizeScheduled);
- state->SetGCState(ThreadState::kNoGCScheduled);
-}
-
-namespace {
-
-class RegisteringMixin;
-using ObjectRegistry = HeapHashMap<void*, Member<RegisteringMixin>>;
-
-class RegisteringMixin : public GarbageCollectedMixin {
- public:
- explicit RegisteringMixin(ObjectRegistry* registry) {
- HeapObjectHeader* header = HeapObjectHeader::FromTraceDescriptor(
- TraceTrait<RegisteringMixin>::GetTraceDescriptor(this));
- const void* uninitialized_value = BlinkGC::kNotFullyConstructedObject;
- EXPECT_EQ(uninitialized_value, header);
- registry->insert(reinterpret_cast<void*>(this), this);
- }
-};
-
-class RegisteringObject : public GarbageCollected<RegisteringObject>,
- public RegisteringMixin {
- USING_GARBAGE_COLLECTED_MIXIN(RegisteringObject);
-
- public:
- explicit RegisteringObject(ObjectRegistry* registry)
- : RegisteringMixin(registry) {}
-};
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, WriteBarrierDuringMixinConstruction) {
- IncrementalMarkingScope scope(ThreadState::Current());
- ObjectRegistry registry;
- RegisteringObject* object =
- MakeGarbageCollected<RegisteringObject>(&registry);
-
- // Clear any objects that have been added to the regular marking worklist in
- // the process of calling the constructor.
- MarkingItem marking_item;
- while (scope.marking_worklist()->Pop(WorklistTaskId::MutatorThread,
- &marking_item)) {
- HeapObjectHeader* header =
- HeapObjectHeader::FromPayload(marking_item.base_object_payload);
- if (header->IsMarked())
- header->Unmark();
- }
- EXPECT_TRUE(scope.marking_worklist()->IsGlobalEmpty());
- // Clear any write barriers so far.
- HeapObjectHeader* header;
- while (scope.write_barrier_worklist()->Pop(WorklistTaskId::MutatorThread,
- &header)) {
- if (header->IsMarked())
- header->Unmark();
- }
- EXPECT_TRUE(scope.write_barrier_worklist()->IsGlobalEmpty());
-
- EXPECT_FALSE(scope.not_fully_constructed_worklist()->IsGlobalEmpty());
- NotFullyConstructedItem partial_item;
- bool found_mixin_object = false;
- // The same object may be on the marking work list because of expanding
- // and rehashing of the backing store in the registry.
- while (scope.not_fully_constructed_worklist()->Pop(
- WorklistTaskId::MutatorThread, &partial_item)) {
- if (object == partial_item)
- found_mixin_object = true;
- HeapObjectHeader* header = HeapObjectHeader::FromPayload(partial_item);
- if (header->IsMarked())
- header->Unmark();
- }
- EXPECT_TRUE(found_mixin_object);
- EXPECT_TRUE(scope.not_fully_constructed_worklist()->IsGlobalEmpty());
-}
-
-TEST_F(IncrementalMarkingTest, OverrideAfterMixinConstruction) {
- ObjectRegistry registry;
- RegisteringMixin* mixin = MakeGarbageCollected<RegisteringObject>(&registry);
- HeapObjectHeader* header = HeapObjectHeader::FromTraceDescriptor(
- TraceTrait<RegisteringMixin>::GetTraceDescriptor(mixin));
-
- const void* uninitialized_value = BlinkGC::kNotFullyConstructedObject;
- EXPECT_NE(uninitialized_value, header);
-}
-
-// =============================================================================
-// Tests that execute complete incremental garbage collections. ================
-// =============================================================================
-
-TEST_F(IncrementalMarkingTest, TestDriver) {
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
- driver.SingleStep();
- EXPECT_TRUE(ThreadState::Current()->IsIncrementalMarking());
- driver.FinishGC();
- EXPECT_FALSE(ThreadState::Current()->IsIncrementalMarking());
-}
-
-TEST_F(IncrementalMarkingTest, DropBackingStore) {
- // Regression test: https://crbug.com/828537
- using WeakStore = HeapHashCountedSet<WeakMember<Object>>;
-
- Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>());
- persistent->insert(MakeGarbageCollected<Object>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- driver.FinishSteps();
- persistent->clear();
- // Marking verifier should not crash on a black backing store with all
- // black->white edges.
- driver.FinishGC();
-}
-
-TEST_F(IncrementalMarkingTest, NoBackingFreeDuringIncrementalMarking) {
- // Regression test: https://crbug.com/870306
- // Only reproduces in ASAN configurations.
- using WeakStore = HeapHashCountedSet<WeakMember<Object>>;
-
- Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>());
- // 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>());
- }
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- persistent->insert(MakeGarbageCollected<Object>());
- // Is not allowed to free the backing store as the previous insert may have
- // registered a slot.
- persistent->clear();
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-TEST_F(IncrementalMarkingTest, DropReferenceWithHeapCompaction) {
- using Store = HeapHashCountedSet<Member<Object>>;
-
- Persistent<Store> persistent(MakeGarbageCollected<Store>());
- persistent->insert(MakeGarbageCollected<Object>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- driver.Start();
- driver.FinishSteps();
- persistent->clear();
- // Registration of movable and updatable references should not crash because
- // if a slot have nullptr reference, it doesn't call registeration method.
- driver.FinishGC();
-}
-
-TEST_F(IncrementalMarkingTest, HasInlineCapacityCollectionWithHeapCompaction) {
- using Store = HeapVector<Member<Object>, 2>;
-
- Persistent<Store> persistent(MakeGarbageCollected<Store>());
- Persistent<Store> persistent2(MakeGarbageCollected<Store>());
-
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- persistent->push_back(MakeGarbageCollected<Object>());
- driver.Start();
- driver.FinishGC();
-
- // Should collect also slots that has only inline buffer and nullptr
- // references.
-#if defined(ANNOTATE_CONTIGUOUS_CONTAINER)
- // When ANNOTATE_CONTIGUOUS_CONTAINER is defined, inline capacity is ignored.
- EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 1u);
-#else
- EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 2u);
-#endif
-}
-
-TEST_F(IncrementalMarkingTest, WeakHashMapHeapCompaction) {
- using Store = HeapHashCountedSet<WeakMember<Object>>;
-
- Persistent<Store> persistent(MakeGarbageCollected<Store>());
-
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- driver.Start();
- driver.FinishSteps();
- persistent->insert(MakeGarbageCollected<Object>());
- driver.FinishGC();
-
- // Weak callback should register the slot.
- EXPECT_EQ(1u, driver.GetHeapCompactLastFixupCount());
-}
-
-TEST_F(IncrementalMarkingTest, ConservativeGCWhileCompactionScheduled) {
- using Store = HeapVector<Member<Object>>;
- Persistent<Store> persistent(MakeGarbageCollected<Store>());
- persistent->push_back(MakeGarbageCollected<Object>());
-
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- driver.Start();
- driver.FinishSteps();
- ThreadState::Current()->CollectGarbageForTesting(
- BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack,
- BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping,
- BlinkGC::GCReason::kForcedGCForTesting);
-
- // Heap compaction should be canceled if incremental marking finishes with a
- // conservative GC.
- EXPECT_EQ(driver.GetHeapCompactLastFixupCount(), 0u);
-}
-
-namespace {
-
-class ObjectWithWeakMember : public GarbageCollected<ObjectWithWeakMember> {
- public:
- ObjectWithWeakMember() = default;
-
- void set_object(Object* object) { object_ = object; }
-
- void Trace(Visitor* visitor) { visitor->Trace(object_); }
-
- private:
- WeakMember<Object> object_ = nullptr;
-};
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, WeakMember) {
- // Regression test: https://crbug.com/913431
-
- Persistent<ObjectWithWeakMember> persistent(
- MakeGarbageCollected<ObjectWithWeakMember>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- driver.FinishSteps();
- persistent->set_object(MakeGarbageCollected<Object>());
- driver.FinishGC();
- ConservativelyCollectGarbage();
-}
-
-TEST_F(IncrementalMarkingTest, MemberSwap) {
- // Regression test: https://crbug.com/913431
- //
- // MemberBase::Swap may be used to swap in a not-yet-processed member into an
- // already-processed member. This leads to a stale pointer that is not marked.
-
- Persistent<Object> object1(MakeGarbageCollected<Object>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- // The repro leverages the fact that initializing stores do not emit a barrier
- // (because they are still reachable from stack) to simulate the problematic
- // interleaving.
- driver.FinishSteps();
- Object* object2 =
- MakeGarbageCollected<Object>(MakeGarbageCollected<Object>());
- object2->next_ref().Swap(object1->next_ref());
- driver.FinishGC();
- ConservativelyCollectGarbage();
-}
-
-namespace {
-
-template <typename T>
-class ObjectHolder : public GarbageCollected<ObjectHolder<T>> {
- public:
- ObjectHolder() = default;
-
- virtual void Trace(Visitor* visitor) { visitor->Trace(holder_); }
-
- void set_value(T* value) { holder_ = value; }
- T* value() const { return holder_.Get(); }
-
- private:
- Member<T> holder_;
-};
-
-} // namespace
-
-TEST_F(IncrementalMarkingTest, StepDuringObjectConstruction) {
- // Test ensures that objects in construction are delayed for processing to
- // allow omitting write barriers on initializing stores.
-
- using O = ObjectWithCallbackBeforeInitializer<Object>;
- using Holder = ObjectHolder<O>;
- Persistent<Holder> holder(MakeGarbageCollected<Holder>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- MakeGarbageCollected<O>(
- base::BindOnce(
- [](IncrementalMarkingTestDriver* driver, Holder* holder, O* thiz) {
- // Publish not-fully-constructed object |thiz| by triggering write
- // barrier for the object.
- holder->set_value(thiz);
- // Finish call incremental steps.
- driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
- },
- &driver, holder.Get()),
- MakeGarbageCollected<Object>());
- driver.FinishGC();
- PreciselyCollectGarbage();
-}
-
-TEST_F(IncrementalMarkingTest, StepDuringMixinObjectConstruction) {
- // Test ensures that mixin objects in construction are delayed for processing
- // to allow omitting write barriers on initializing stores.
-
- using Parent = ObjectWithMixinWithCallbackBeforeInitializer<Object>;
- using Mixin = MixinWithCallbackBeforeInitializer<Object>;
- using Holder = ObjectHolder<Mixin>;
- Persistent<Holder> holder(MakeGarbageCollected<Holder>());
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- MakeGarbageCollected<Parent>(
- base::BindOnce(
- [](IncrementalMarkingTestDriver* driver, Holder* holder,
- Mixin* thiz) {
- // Publish not-fully-constructed object
- // |thiz| by triggering write barrier for
- // the object.
- holder->set_value(thiz);
- // Finish call incremental steps.
- driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
- },
- &driver, holder.Get()),
- MakeGarbageCollected<Object>());
- driver.FinishGC();
- PreciselyCollectGarbage();
-}
-
-TEST_F(IncrementalMarkingTest, IncrementalMarkingShrinkingBackingCompaction) {
- // Regression test: https://crbug.com/918064
-
- using Nested = HeapVector<HeapVector<Member<Object>>>;
- // The following setup will ensure that the outer HeapVector's backing store
- // contains slots to other to-be-compacted backings.
- Persistent<Nested> holder(MakeGarbageCollected<Nested>());
- for (int i = 0; i < 32; i++) {
- holder->emplace_back();
- holder->at(i).emplace_back(MakeGarbageCollected<Object>());
- }
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- driver.Start();
- driver.FinishSteps();
- // Reduce size of the outer backing store.
- for (int i = 0; i < 16; i++) {
- holder->pop_back();
- }
- // Ensure that shrinking the backing does not crash in compaction as there may
- // be registered slots left in the area that is already freed.
- holder->ShrinkToFit();
- driver.FinishGC();
-}
-
-TEST_F(IncrementalMarkingTest,
- InPayloadWriteBarrierRegistersInvalidSlotForCompaction) {
- // Regression test: https://crbug.com/918064
-
- using Nested = HeapVector<HeapVector<Member<Object>>>;
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- // Allocate a vector and reserve a buffer to avoid triggering the write
- // barrier during incremental marking.
- Nested* nested = MakeGarbageCollected<Nested>();
- nested->ReserveCapacity(32);
- driver.Start();
- // Initialize the inner vector, triggering tracing and slots registration.
- // This could be an object using DISALLOW_NEW() but HeapVector is easier to
- // test.
- nested->emplace_back(1);
- // Use the inner vector as otherwise the slot would not be registered due to
- // not having a backing store itself.
- nested->at(0).emplace_back(MakeGarbageCollected<Object>());
- driver.FinishSteps();
- // GCs here are without stack. This is just to show that we don't want this
- // object marked.
- CHECK(!HeapObjectHeader::FromPayload(nested)
- ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>());
- nested = nullptr;
- driver.FinishGC();
-}
-
-TEST_F(IncrementalMarkingTest, AdjustMarkedBytesOnMarkedBackingStore) {
- // Regression test: https://crbug.com/966456
- //
- // Test ensures that backing expansion does not crash in trying to adjust
- // marked bytes when the page is actually about to be swept and marking is not
- // in progress.
-
- // Disable concurrent sweeping to check that sweeping is not in progress after
- // the FinishGC call.
- base::test::ScopedFeatureList scoped_feature_list;
- scoped_feature_list.InitAndDisableFeature(
- blink::features::kBlinkHeapConcurrentSweeping);
- using Container = HeapVector<Member<Object>>;
- Persistent<Container> holder(MakeGarbageCollected<Container>());
- holder->push_back(MakeGarbageCollected<Object>());
- holder->Grow(16);
- ThreadState::Current()->Heap().ResetAllocationPointForTesting();
- // Slowly shrink down the backing, only adjusting capacity without performing
- // free as the resulting memory block is too small for a free list entry.
- for (int i = 15; i > 0; i--) {
- holder->Shrink(i);
- holder->ShrinkToFit();
- }
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- driver.FinishSteps();
- // The object is marked at this point.
- CHECK(HeapObjectHeader::FromPayload(holder.Get())
- ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>());
- driver.FinishGC(false);
- // The object is still marked as sweeping did not make any progress.
- CHECK(HeapObjectHeader::FromPayload(holder.Get())->IsMarked());
- // Re-grow to some size within the initial payload size (capacity=16).
- holder->Grow(8);
-}
-
-TEST_F(IncrementalMarkingTest, HeapCompactWithStaleSlotInNestedContainer) {
- // Regression test: https://crbug.com/980962
- //
- // Test ensures that interior pointers are updated even if the backing store
- // itself is not referenced anymore. Consider the case where a |B| is
- // references a value |V| through slot |B.x|. Even if |B| is not referred to
- // from an actual object any more, the slot |B.x| needs to be in valid state
- // when |V| is moved.
-
- using Nested = HeapVector<HeapVector<Member<Object>>>;
-
- // Allocate dummy storage so that other vector backings are actually moved.
- MakeGarbageCollected<HeapVector<Member<Object>>>()->push_back(
- MakeGarbageCollected<Object>());
-
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- ThreadState::Current()->EnableCompactionForNextGCForTesting();
- driver.Start();
- Nested* outer = MakeGarbageCollected<Nested>();
- outer->push_back(HeapVector<Member<Object>>());
- outer->at(0).push_back(MakeGarbageCollected<Object>());
- // The outer HeapVector object is not marked, which leaves the backing store
- // as marked with a valid slot inside. Now, if the outer backing store moves
- // first and its page is freed, then referring to the slot when the inner
- // backing store is moved may crash.
- outer = nullptr;
- driver.FinishSteps();
- driver.FinishGC();
-}
-
-class Destructed final : public GarbageCollected<Destructed> {
- public:
- ~Destructed() { n_destructed++; }
-
- void Trace(Visitor*) {}
-
- static size_t n_destructed;
-};
-
-size_t Destructed::n_destructed = 0;
-
-class LinkedHashSetWrapper final
- : public GarbageCollected<LinkedHashSetWrapper> {
- public:
- using HashType = HeapLinkedHashSet<Member<Destructed>>;
-
- LinkedHashSetWrapper() {
- 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, LinkedHashSetMovingCallback) {
- ClearOutOldGarbage();
-
- Destructed::n_destructed = 0;
- {
- HeapHashSet<Member<Destructed>> to_be_destroyed;
- to_be_destroyed.ReserveCapacityForSize(100);
- }
- 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 backing
- // store.
- wrapper->Swap();
- DCHECK(wrapper->hash_set_.IsEmpty());
-
- PreciselyCollectGarbage();
-
- 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_scheduling_oracle.cc b/chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.cc
new file mode 100644
index 00000000000..f17cea8db7b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.cc
@@ -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.
+
+#include "third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h"
+
+namespace blink {
+
+constexpr double MarkingSchedulingOracle::kEstimatedMarkingTimeMs;
+constexpr base::TimeDelta
+ MarkingSchedulingOracle::kDefaultIncrementalMarkingStepDuration;
+constexpr size_t MarkingSchedulingOracle::kMinimumMarkedBytesInStep;
+constexpr base::TimeDelta
+ MarkingSchedulingOracle::kMaximumIncrementalMarkingStepDuration;
+
+MarkingSchedulingOracle::MarkingSchedulingOracle()
+ : incremental_marking_start_time_(base::TimeTicks::Now()) {}
+
+void MarkingSchedulingOracle::UpdateIncrementalMarkingStats(
+ size_t overall_marked_bytes,
+ base::TimeDelta overall_marking_time) {
+ incrementally_marked_bytes_ = overall_marked_bytes;
+ incremental_marking_time_so_far_ = overall_marking_time;
+}
+
+void MarkingSchedulingOracle::AddConcurrentlyMarkedBytes(size_t marked_bytes) {
+ base::AutoLock lock(concurrently_marked_bytes_lock_);
+ concurrently_marked_bytes_ += marked_bytes;
+}
+
+size_t MarkingSchedulingOracle::GetOverallMarkedBytes() {
+ base::AutoLock lock(concurrently_marked_bytes_lock_);
+ return incrementally_marked_bytes_ + concurrently_marked_bytes_;
+}
+
+double MarkingSchedulingOracle::GetElapsedTimeInMs(base::TimeTicks start_time) {
+ if (elapsed_time_for_testing_ != kNoSetElapsedTimeForTesting) {
+ double elapsed_time = elapsed_time_for_testing_;
+ elapsed_time_for_testing_ = kNoSetElapsedTimeForTesting;
+ return elapsed_time;
+ }
+ return (base::TimeTicks::Now() - start_time).InMillisecondsF();
+}
+
+base::TimeDelta MarkingSchedulingOracle::GetMinimumStepDuration() {
+ DCHECK_LT(0u, incrementally_marked_bytes_);
+ DCHECK(!incremental_marking_time_so_far_.is_zero());
+ base::TimeDelta minimum_duration = incremental_marking_time_so_far_ *
+ kMinimumMarkedBytesInStep /
+ incrementally_marked_bytes_;
+ return minimum_duration;
+}
+
+base::TimeDelta MarkingSchedulingOracle::GetNextIncrementalStepDurationForTask(
+ size_t estimated_live_bytes) {
+ if ((incrementally_marked_bytes_ == 0) ||
+ incremental_marking_time_so_far_.is_zero()) {
+ // Impossible to estimate marking speed. Fallback to default duration.
+ return kDefaultIncrementalMarkingStepDuration;
+ }
+ double elapsed_time_in_ms =
+ GetElapsedTimeInMs(incremental_marking_start_time_);
+ size_t actual_marked_bytes = GetOverallMarkedBytes();
+ double expected_marked_bytes =
+ estimated_live_bytes * elapsed_time_in_ms / kEstimatedMarkingTimeMs;
+ base::TimeDelta minimum_duration = GetMinimumStepDuration();
+ if (expected_marked_bytes < actual_marked_bytes) {
+ // Marking is ahead of schedule, incremental marking doesn't need to
+ // do anything.
+ return std::min(minimum_duration, kMaximumIncrementalMarkingStepDuration);
+ }
+ // Assuming marking will take |kEstimatedMarkingTime|, overall there will
+ // be |estimated_live_bytes| live bytes to mark, and that marking speed is
+ // constant, after |elapsed_time| the number of marked_bytes should be
+ // |estimated_live_bytes| * (|elapsed_time| / |kEstimatedMarkingTime|),
+ // denoted as |expected_marked_bytes|. If |actual_marked_bytes| is less,
+ // i.e. marking is behind schedule, incremental marking should help "catch
+ // up" by marking (|expected_marked_bytes| - |actual_marked_bytes|).
+ // Assuming constant marking speed, duration of the next incremental step
+ // should be as follows:
+ double marking_time_to_catch_up_in_ms =
+ (expected_marked_bytes - actual_marked_bytes) *
+ incremental_marking_time_so_far_.InMillisecondsF() /
+ incrementally_marked_bytes_;
+ return std::min(
+ kMaximumIncrementalMarkingStepDuration,
+ std::max(minimum_duration, base::TimeDelta::FromMillisecondsD(
+ marking_time_to_catch_up_in_ms)));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h b/chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h
new file mode 100644
index 00000000000..642304cb2f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h
@@ -0,0 +1,64 @@
+// 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_MARKING_SCHEDULING_ORACLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_SCHEDULING_ORACLE_H_
+
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MarkingSchedulingOracle {
+ public:
+ // Estimated duration of GC cycle in milliseconds.
+ static constexpr double kEstimatedMarkingTimeMs = 500.0;
+
+ // 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 =
+ base::TimeDelta::FromMillisecondsD(0.5);
+
+ // Minimum number of bytes that should be marked during an incremental
+ // marking step.
+ static constexpr size_t kMinimumMarkedBytesInStep = 64 * 1024;
+
+ // Maximum 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 kMaximumIncrementalMarkingStepDuration =
+ base::TimeDelta::FromMillisecondsD(2.0);
+
+ explicit MarkingSchedulingOracle();
+
+ void UpdateIncrementalMarkingStats(size_t, base::TimeDelta);
+ void AddConcurrentlyMarkedBytes(size_t);
+
+ size_t GetOverallMarkedBytes();
+
+ base::TimeDelta GetNextIncrementalStepDurationForTask(size_t);
+
+ void SetElapsedTimeForTesting(double elapsed_time) {
+ elapsed_time_for_testing_ = elapsed_time;
+ }
+
+ private:
+ double GetElapsedTimeInMs(base::TimeTicks);
+ base::TimeDelta GetMinimumStepDuration();
+
+ base::TimeTicks incremental_marking_start_time_;
+ base::TimeDelta incremental_marking_time_so_far_;
+
+ size_t incrementally_marked_bytes_ = 0;
+ size_t concurrently_marked_bytes_ = 0;
+ base::Lock concurrently_marked_bytes_lock_;
+
+ // Using -1 as sentinel to denote
+ static constexpr double kNoSetElapsedTimeForTesting = -1;
+ double elapsed_time_for_testing_ = kNoSetElapsedTimeForTesting;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_SCHEDULING_ORACLE_H_
diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_verifier_test.cc b/chromium/third_party/blink/renderer/platform/heap/marking_verifier_test.cc
deleted file mode 100644
index 3c97206c0a6..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/marking_verifier_test.cc
+++ /dev/null
@@ -1,98 +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/heap/marking_verifier.h"
-#include "testing/gtest/include/gtest/gtest.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"
-
-namespace blink {
-
-#if DCHECK_IS_ON()
-class MarkingVerifierDeathTest : public TestSupportingGC {};
-
-namespace {
-
-class ResurrectingPreFinalizer
- : public GarbageCollected<ResurrectingPreFinalizer> {
- USING_PRE_FINALIZER(ResurrectingPreFinalizer, Dispose);
-
- public:
- enum TestType {
- kMember,
- kWeakMember,
- };
-
- class GlobalStorage : public GarbageCollected<GlobalStorage> {
- public:
- void Trace(Visitor* visitor) {
- visitor->Trace(strong);
- visitor->Trace(weak);
- }
-
- Member<LinkedObject> strong;
- WeakMember<LinkedObject> weak;
- };
-
- ResurrectingPreFinalizer(TestType test_type,
- GlobalStorage* storage,
- LinkedObject* object_that_dies)
- : test_type_(test_type),
- storage_(storage),
- object_that_dies_(object_that_dies) {}
-
- void Trace(Visitor* visitor) {
- visitor->Trace(storage_);
- visitor->Trace(object_that_dies_);
- }
-
- private:
- void Dispose() {
- switch (test_type_) {
- case TestType::kMember:
- storage_->strong = object_that_dies_;
- break;
- case TestType::kWeakMember:
- storage_->weak = object_that_dies_;
- break;
- }
- }
-
- TestType test_type_;
- Member<GlobalStorage> storage_;
- Member<LinkedObject> object_that_dies_;
-};
-
-} // namespace
-
-TEST_F(MarkingVerifierDeathTest, DiesOnResurrectedMember) {
- if (!ThreadState::Current()->IsVerifyMarkingEnabled())
- return;
-
- Persistent<ResurrectingPreFinalizer::GlobalStorage> storage(
- MakeGarbageCollected<ResurrectingPreFinalizer::GlobalStorage>());
- MakeGarbageCollected<ResurrectingPreFinalizer>(
- ResurrectingPreFinalizer::kMember, storage.Get(),
- MakeGarbageCollected<LinkedObject>());
- ASSERT_DEATH_IF_SUPPORTED(PreciselyCollectGarbage(),
- "MarkingVerifier: Encountered unmarked object.");
-}
-
-TEST_F(MarkingVerifierDeathTest, DiesOnResurrectedWeakMember) {
- if (!ThreadState::Current()->IsVerifyMarkingEnabled())
- return;
-
- Persistent<ResurrectingPreFinalizer::GlobalStorage> storage(
- MakeGarbageCollected<ResurrectingPreFinalizer::GlobalStorage>());
- MakeGarbageCollected<ResurrectingPreFinalizer>(
- ResurrectingPreFinalizer::kWeakMember, storage.Get(),
- MakeGarbageCollected<LinkedObject>());
- ASSERT_DEATH_IF_SUPPORTED(PreciselyCollectGarbage(),
- "MarkingVerifier: Encountered unmarked object.");
-}
-#endif // DCHECK_IS_ON()
-
-} // namespace blink
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 fef919acdc4..a4567f5a9a9 100644
--- a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -11,9 +11,9 @@
namespace blink {
-MarkingVisitorCommon::MarkingVisitorCommon(ThreadState* state,
- MarkingMode marking_mode,
- int task_id)
+MarkingVisitorBase::MarkingVisitorBase(ThreadState* state,
+ MarkingMode marking_mode,
+ int task_id)
: Visitor(state),
marking_worklist_(Heap().GetMarkingWorklist(), task_id),
write_barrier_worklist_(Heap().GetWriteBarrierWorklist(), task_id),
@@ -22,25 +22,30 @@ MarkingVisitorCommon::MarkingVisitorCommon(ThreadState* state,
weak_callback_worklist_(Heap().GetWeakCallbackWorklist(), task_id),
movable_reference_worklist_(Heap().GetMovableReferenceWorklist(),
task_id),
- weak_table_worklist_(Heap().GetWeakTableWorklist(), task_id),
+ discovered_ephemeron_pairs_worklist_(
+ Heap().GetDiscoveredEphemeronPairsWorklist(),
+ task_id),
+ ephemeron_pairs_to_process_worklist_(
+ Heap().GetEphemeronPairsToProcessWorklist(),
+ task_id),
backing_store_callback_worklist_(Heap().GetBackingStoreCallbackWorklist(),
task_id),
marking_mode_(marking_mode),
task_id_(task_id) {}
-void MarkingVisitorCommon::FlushCompactionWorklists() {
+void MarkingVisitorBase::FlushCompactionWorklists() {
if (marking_mode_ != kGlobalMarkingWithCompaction)
return;
movable_reference_worklist_.FlushToGlobal();
backing_store_callback_worklist_.FlushToGlobal();
}
-void MarkingVisitorCommon::RegisterWeakCallback(WeakCallback callback,
- const void* object) {
+void MarkingVisitorBase::RegisterWeakCallback(WeakCallback callback,
+ const void* object) {
weak_callback_worklist_.Push({callback, object});
}
-void MarkingVisitorCommon::RegisterBackingStoreCallback(
+void MarkingVisitorBase::RegisterBackingStoreCallback(
const void* backing,
MovingObjectCallback callback) {
if (marking_mode_ != kGlobalMarkingWithCompaction)
@@ -50,7 +55,7 @@ void MarkingVisitorCommon::RegisterBackingStoreCallback(
}
}
-void MarkingVisitorCommon::RegisterMovableSlot(const void* const* slot) {
+void MarkingVisitorBase::RegisterMovableSlot(const void* const* slot) {
if (marking_mode_ != kGlobalMarkingWithCompaction)
return;
if (Heap().ShouldRegisterMovingAddress()) {
@@ -58,30 +63,33 @@ void MarkingVisitorCommon::RegisterMovableSlot(const void* const* slot) {
}
}
-void MarkingVisitorCommon::VisitWeak(const void* object,
- const void* object_weak_ref,
- TraceDescriptor desc,
- WeakCallback callback) {
+void MarkingVisitorBase::VisitWeak(const void* object,
+ const void* object_weak_ref,
+ TraceDescriptor desc,
+ WeakCallback callback) {
// Filter out already marked values. The write barrier for WeakMember
// ensures that any newly set value after this point is kept alive and does
// not require the callback.
- if (desc.base_object_payload != BlinkGC::kNotFullyConstructedObject &&
- HeapObjectHeader::FromPayload(desc.base_object_payload)
- ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>())
+ HeapObjectHeader* header =
+ HeapObjectHeader::FromPayload(desc.base_object_payload);
+ if (header->IsMarked<HeapObjectHeader::AccessMode::kAtomic>())
return;
RegisterWeakCallback(callback, object_weak_ref);
}
-void MarkingVisitorCommon::VisitEphemeron(const void* key,
- const void* value,
- TraceCallback value_trace_callback) {
+void MarkingVisitorBase::VisitEphemeron(const void* key,
+ const void* value,
+ TraceCallback value_trace_callback) {
if (!HeapObjectHeader::FromPayload(key)
- ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>())
+ ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>()) {
+ discovered_ephemeron_pairs_worklist_.Push(
+ {key, value, value_trace_callback});
return;
+ }
value_trace_callback(this, value);
}
-void MarkingVisitorCommon::VisitWeakContainer(
+void MarkingVisitorBase::VisitWeakContainer(
const void* object,
const void* const*,
TraceDescriptor,
@@ -111,7 +119,20 @@ void MarkingVisitorCommon::VisitWeakContainer(
RegisterWeakCallback(weak_callback, weak_callback_parameter);
// Register ephemeron callbacks if necessary.
if (weak_desc.callback)
- weak_table_worklist_.Push(weak_desc);
+ weak_desc.callback(this, weak_desc.base_object_payload);
+}
+
+void MarkingVisitorBase::DynamicallyMarkAddress(ConstAddress address) {
+ constexpr HeapObjectHeader::AccessMode mode =
+ HeapObjectHeader::AccessMode::kAtomic;
+ HeapObjectHeader* const header =
+ HeapObjectHeader::FromInnerAddress<mode>(address);
+ DCHECK(header);
+ DCHECK(!header->IsInConstruction<mode>());
+ if (MarkHeaderNoTracing(header)) {
+ marking_worklist_.Push({reinterpret_cast<void*>(header->Payload()),
+ GCInfo::From(header->GcInfoIndex<mode>()).trace});
+ }
}
// static
@@ -121,9 +142,8 @@ bool MarkingVisitor::MarkValue(void* value,
HeapObjectHeader* header;
if (LIKELY(!base_page->IsLargeObjectPage())) {
header = reinterpret_cast<HeapObjectHeader*>(
- static_cast<NormalPage*>(base_page)
- ->FindHeaderFromAddress<HeapObjectHeader::AccessMode::kAtomic>(
- reinterpret_cast<Address>(value)));
+ static_cast<NormalPage*>(base_page)->FindHeaderFromAddress(
+ reinterpret_cast<Address>(value)));
} else {
LargeObjectPage* large_page = static_cast<LargeObjectPage*>(base_page);
header = large_page->ObjectHeader();
@@ -133,10 +153,11 @@ bool MarkingVisitor::MarkValue(void* value,
return false;
MarkingVisitor* visitor = thread_state->CurrentVisitor();
- if (UNLIKELY(IsInConstruction(header))) {
+ if (UNLIKELY(
+ header->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>())) {
// It is assumed that objects on not_fully_constructed_worklist_ are not
// marked.
- header->Unmark();
+ header->Unmark<HeapObjectHeader::AccessMode::kAtomic>();
visitor->not_fully_constructed_worklist_.Push(header->Payload());
return true;
}
@@ -210,18 +231,6 @@ MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode)
DCHECK(state->CheckThread());
}
-void MarkingVisitor::DynamicallyMarkAddress(ConstAddress address) {
- HeapObjectHeader* const header =
- HeapObjectHeader::FromInnerAddress<HeapObjectHeader::AccessMode::kAtomic>(
- address);
- DCHECK(header);
- DCHECK(!IsInConstruction(header));
- if (MarkHeaderNoTracing(header)) {
- marking_worklist_.Push({reinterpret_cast<void*>(header->Payload()),
- GCInfo::From(header->GcInfoIndex()).trace});
- }
-}
-
void MarkingVisitor::ConservativelyMarkAddress(BasePage* page,
ConstAddress address) {
#if DCHECK_IS_ON()
@@ -237,7 +246,7 @@ void MarkingVisitor::ConservativelyMarkAddress(BasePage* page,
// Simple case for fully constructed objects. This just adds the object to the
// regular marking worklist.
- if (!IsInConstruction(header)) {
+ if (!header->IsInConstruction()) {
MarkHeader(header,
{header->Payload(), GCInfo::From(header->GcInfoIndex()).trace});
return;
@@ -284,6 +293,9 @@ ConcurrentMarkingVisitor::ConcurrentMarkingVisitor(ThreadState* state,
: MarkingVisitorBase(state, marking_mode, task_id),
not_safe_to_concurrently_trace_worklist_(
Heap().GetNotSafeToConcurrentlyTraceWorklist(),
+ task_id),
+ previously_not_fully_constructed_worklist_(
+ Heap().GetPreviouslyNotFullyConstructedWorklist(),
task_id) {
DCHECK(!state->CheckThread());
DCHECK_NE(WorklistTaskId::MutatorThread, task_id);
@@ -294,8 +306,10 @@ void ConcurrentMarkingVisitor::FlushWorklists() {
marking_worklist_.FlushToGlobal();
write_barrier_worklist_.FlushToGlobal();
not_fully_constructed_worklist_.FlushToGlobal();
+ previously_not_fully_constructed_worklist_.FlushToGlobal();
weak_callback_worklist_.FlushToGlobal();
- weak_table_worklist_.FlushToGlobal();
+ discovered_ephemeron_pairs_worklist_.FlushToGlobal();
+ ephemeron_pairs_to_process_worklist_.FlushToGlobal();
not_safe_to_concurrently_trace_worklist_.FlushToGlobal();
// Flush compaction worklists.
if (marking_mode_ == kGlobalMarkingWithCompaction) {
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 60f312e4471..069c4a4cc0f 100644
--- a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -21,9 +21,13 @@ ALWAYS_INLINE bool IsHashTableDeleteValue(const void* value) {
} // namespace
class BasePage;
+class HeapAllocator;
+enum class TracenessMemberConfiguration;
+template <typename T, TracenessMemberConfiguration tracenessConfiguration>
+class MemberBase;
// Base visitor used to mark Oilpan objects on any thread.
-class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor {
+class PLATFORM_EXPORT MarkingVisitorBase : public Visitor {
public:
enum MarkingMode {
// Default visitor mode used for regular marking.
@@ -32,7 +36,6 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor {
kGlobalMarkingWithCompaction,
};
- void VisitWeak(const void*, const void*, TraceDescriptor, WeakCallback) final;
void VisitWeakContainer(const void*,
const void* const*,
TraceDescriptor,
@@ -41,6 +44,11 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor {
const void*) final;
void VisitEphemeron(const void*, const void*, TraceCallback) final;
+ // 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(ConstAddress);
+
// This callback mechanism is needed to account for backing store objects
// containing intra-object pointers, all of which must be relocated/rebased
// with respect to the moved-to location.
@@ -66,9 +74,14 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor {
ALWAYS_INLINE void AccountMarkedBytes(HeapObjectHeader*);
protected:
- MarkingVisitorCommon(ThreadState*, MarkingMode, int task_id);
- ~MarkingVisitorCommon() override = default;
+ MarkingVisitorBase(ThreadState*, MarkingMode, int task_id);
+ ~MarkingVisitorBase() override = default;
+
+ void Visit(const void* object, TraceDescriptor desc) final;
+ void VisitWeak(const void*, const void*, TraceDescriptor, WeakCallback) final;
+ // Marks an object and adds a tracing callback for processing of the object.
+ void MarkHeader(HeapObjectHeader*, const TraceDescriptor&);
// Try to mark an object without tracing. Returns true when the object was not
// marked upon calling.
bool MarkHeaderNoTracing(HeapObjectHeader*);
@@ -78,23 +91,23 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor {
NotFullyConstructedWorklist::View not_fully_constructed_worklist_;
WeakCallbackWorklist::View weak_callback_worklist_;
MovableReferenceWorklist::View movable_reference_worklist_;
- WeakTableWorklist::View weak_table_worklist_;
+ EphemeronPairsWorklist::View discovered_ephemeron_pairs_worklist_;
+ EphemeronPairsWorklist::View ephemeron_pairs_to_process_worklist_;
BackingStoreCallbackWorklist::View backing_store_callback_worklist_;
size_t marked_bytes_ = 0;
const MarkingMode marking_mode_;
int task_id_;
};
-ALWAYS_INLINE void MarkingVisitorCommon::AccountMarkedBytes(
+ALWAYS_INLINE void MarkingVisitorBase::AccountMarkedBytes(
HeapObjectHeader* header) {
marked_bytes_ +=
- header->IsLargeObject()
- ? reinterpret_cast<LargeObjectPage*>(PageFromObject(header))
- ->ObjectSize()
- : header->size();
+ header->IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>()
+ ? static_cast<LargeObjectPage*>(PageFromObject(header))->ObjectSize()
+ : header->size<HeapObjectHeader::AccessMode::kAtomic>();
}
-ALWAYS_INLINE bool MarkingVisitorCommon::MarkHeaderNoTracing(
+ALWAYS_INLINE bool MarkingVisitorBase::MarkHeaderNoTracing(
HeapObjectHeader* header) {
DCHECK(header);
DCHECK(State()->IsIncrementalMarking() || State()->InAtomicMarkingPause());
@@ -108,42 +121,19 @@ ALWAYS_INLINE bool MarkingVisitorCommon::MarkHeaderNoTracing(
return header->TryMark<HeapObjectHeader::AccessMode::kAtomic>();
}
-// Base visitor used to mark Oilpan objects on any thread.
-template <class Specialized>
-class PLATFORM_EXPORT MarkingVisitorBase : public MarkingVisitorCommon {
- protected:
- MarkingVisitorBase(ThreadState* state, MarkingMode marking_mode, int task_id)
- : MarkingVisitorCommon(state, marking_mode, task_id) {}
- ~MarkingVisitorBase() override = default;
-
- void Visit(const void* object, TraceDescriptor desc) final;
-
- // Marks an object and adds a tracing callback for processing of the object.
- void MarkHeader(HeapObjectHeader*, const TraceDescriptor&);
-};
-
-template <class Specialized>
-inline void MarkingVisitorBase<Specialized>::Visit(const void* object,
- TraceDescriptor desc) {
+inline void MarkingVisitorBase::Visit(const void* object,
+ TraceDescriptor desc) {
DCHECK(object);
- if (desc.base_object_payload == BlinkGC::kNotFullyConstructedObject) {
- // This means that the objects are not-yet-fully-constructed. See comments
- // on GarbageCollectedMixin for how those objects are handled.
- not_fully_constructed_worklist_.Push(object);
- return;
- }
MarkHeader(HeapObjectHeader::FromPayload(desc.base_object_payload), desc);
}
// Marks an object and adds a tracing callback for processing of the object.
-template <class Specialized>
-ALWAYS_INLINE void MarkingVisitorBase<Specialized>::MarkHeader(
- HeapObjectHeader* header,
- const TraceDescriptor& desc) {
+ALWAYS_INLINE void MarkingVisitorBase::MarkHeader(HeapObjectHeader* header,
+ const TraceDescriptor& desc) {
DCHECK(header);
DCHECK(desc.callback);
- if (Specialized::IsInConstruction(header)) {
+ if (header->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>()) {
not_fully_constructed_worklist_.Push(header->Payload());
} else if (MarkHeaderNoTracing(header)) {
marking_worklist_.Push(desc);
@@ -153,18 +143,8 @@ ALWAYS_INLINE void MarkingVisitorBase<Specialized>::MarkHeader(
// Visitor used to mark Oilpan objects on the main thread. Also implements
// various sorts of write barriers that should only be called from the main
// thread.
-class PLATFORM_EXPORT MarkingVisitor
- : public MarkingVisitorBase<MarkingVisitor> {
+class PLATFORM_EXPORT MarkingVisitor : public MarkingVisitorBase {
public:
- // Returns whether an object is in construction.
- static bool IsInConstruction(HeapObjectHeader* header);
-
- // 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
@@ -182,27 +162,25 @@ class PLATFORM_EXPORT MarkingVisitor
// Trace method.
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(ConstAddress);
-
void FlushMarkingWorklists();
private:
+ // 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);
+
// Exact version of the marking and generational write barriers.
static bool WriteBarrierSlow(void*);
static void GenerationalBarrierSlow(Address, ThreadState*);
static bool MarkValue(void*, BasePage*, ThreadState*);
static void TraceMarkedBackingStoreSlow(const void*);
-};
-// static
-ALWAYS_INLINE bool MarkingVisitor::IsInConstruction(HeapObjectHeader* header) {
- // No need for atomics when operating on the mutator thread where
- // construction happens.
- return header->IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>();
-}
+ friend class HeapAllocator;
+ template <typename T, TracenessMemberConfiguration tracenessConfiguration>
+ friend class MemberBase;
+};
// static
template <typename T>
@@ -253,20 +231,13 @@ ALWAYS_INLINE void MarkingVisitor::TraceMarkedBackingStore(const void* value) {
}
// Visitor used to mark Oilpan objects on concurrent threads.
-class PLATFORM_EXPORT ConcurrentMarkingVisitor
- : public MarkingVisitorBase<ConcurrentMarkingVisitor> {
+class PLATFORM_EXPORT ConcurrentMarkingVisitor : public MarkingVisitorBase {
public:
- // Returns whether an object is in construction.
- static bool IsInConstruction(HeapObjectHeader* header);
-
ConcurrentMarkingVisitor(ThreadState*, MarkingMode, int);
~ConcurrentMarkingVisitor() override = default;
virtual void FlushWorklists();
- // Concurrent variant of MarkingVisitorCommon::AccountMarkedBytes.
- void AccountMarkedBytesSafe(HeapObjectHeader*);
-
bool IsConcurrent() const override { return true; }
bool DeferredTraceIfConcurrent(TraceDescriptor desc) override {
@@ -277,22 +248,9 @@ class PLATFORM_EXPORT ConcurrentMarkingVisitor
private:
NotSafeToConcurrentlyTraceWorklist::View
not_safe_to_concurrently_trace_worklist_;
+ NotFullyConstructedWorklist::View previously_not_fully_constructed_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) {
- return header->IsInConstruction<HeapObjectHeader::AccessMode::kAtomic>();
-}
-
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_MARKING_VISITOR_H_
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
deleted file mode 100644
index c30e976d294..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/minor_gc_test.cc
+++ /dev/null
@@ -1,295 +0,0 @@
-// 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/name_trait_test.cc b/chromium/third_party/blink/renderer/platform/heap/name_trait_test.cc
deleted file mode 100644
index d83ab7012fc..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/name_trait_test.cc
+++ /dev/null
@@ -1,68 +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 <string.h>
-
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/bindings/name_client.h"
-#include "third_party/blink/renderer/platform/heap/name_traits.h"
-
-namespace blink {
-
-namespace {
-
-class ClassWithoutName final {
- public:
- ClassWithoutName() = default;
-};
-
-class ClassWithName final : public NameClient {
- public:
- explicit ClassWithName(const char* name) : name_(name) {}
-
- const char* NameInHeapSnapshot() const final { return name_; }
-
- private:
- const char* name_;
-};
-
-} // namespace
-
-TEST(NameTraitTest, InternalNamesHiddenInOfficialBuild) {
- // Use a test instead of static_assert to allow local builds but block
- // enabling the feature accidentally through the waterfall.
- //
- // Do not include such type information in official builds to
- // (a) safe binary size on string literals, and
- // (b) avoid exposing internal types until it has been clarified whether
- // exposing internals in DevTools is fine.
-#if defined(OFFICIAL_BUILD)
- EXPECT_TRUE(NameClient::HideInternalName());
-#endif
-}
-
-TEST(NameTraitTest, InternalNamesHiddenWhenFlagIsTurnedOff) {
-#if !BUILDFLAG(RAW_HEAP_SNAPSHOTS)
- EXPECT_TRUE(NameClient::HideInternalName());
-#endif // BUILDFLAG(RAW_HEAP_SNAPSHOTS)
-}
-
-TEST(NameTraitTest, DefaultName) {
- ClassWithoutName no_name;
- const char* name = NameTrait<ClassWithoutName>::GetName(&no_name).value;
- if (NameClient::HideInternalName()) {
- EXPECT_EQ(0, strcmp(name, "InternalNode"));
- } else {
- EXPECT_NE(nullptr, strstr(name, "ClassWithoutName"));
- }
-}
-
-TEST(NameTraitTest, CustomName) {
- ClassWithName with_name("CustomName");
- const char* name = NameTrait<ClassWithName>::GetName(&with_name).value;
- EXPECT_EQ(0, strcmp(name, "CustomName"));
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc b/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
deleted file mode 100644
index af1db1625ee..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/object_start_bitmap_test.cc
+++ /dev/null
@@ -1,172 +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/heap/heap_page.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-namespace {
-
-bool IsEmpty(const ObjectStartBitmap& bitmap) {
- size_t count = 0;
- bitmap.Iterate([&count](Address) { count++; });
- return count == 0;
-}
-
-// Abstraction for objects that hides ObjectStartBitmap::kGranularity and
-// the base address as getting either of it wrong will result in failed DCHECKs.
-class Object {
- STACK_ALLOCATED();
-
- public:
- static Address kBaseOffset;
-
- Object(size_t number) : number_(number) {
- const size_t max_entries = ObjectStartBitmap::MaxEntries();
- EXPECT_GE(max_entries, number_);
- }
-
- Address address() const {
- return kBaseOffset + ObjectStartBitmap::Granularity() * number_;
- }
-
- // Allow implicitly converting Object to Address.
- operator Address() const { return address(); }
-
- private:
- const size_t number_;
-};
-
-Address Object::kBaseOffset = reinterpret_cast<Address>(0x4000);
-
-} // namespace
-
-TEST(ObjectStartBitmapTest, MoreThanZeroEntriesPossible) {
- const size_t max_entries = ObjectStartBitmap::MaxEntries();
- EXPECT_LT(0u, max_entries);
-}
-
-TEST(ObjectStartBitmapTest, InitialEmpty) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- EXPECT_TRUE(IsEmpty(bitmap));
-}
-
-TEST(ObjectStartBitmapTest, SetBitImpliesNonEmpty) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- bitmap.SetBit(Object(0));
- EXPECT_FALSE(IsEmpty(bitmap));
-}
-
-TEST(ObjectStartBitmapTest, SetBitCheckBit) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object(7);
- bitmap.SetBit(object);
- EXPECT_TRUE(bitmap.CheckBit(object));
-}
-
-TEST(ObjectStartBitmapTest, SetBitClearbitCheckBit) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object(77);
- bitmap.SetBit(object);
- bitmap.ClearBit(object);
- EXPECT_FALSE(bitmap.CheckBit(object));
-}
-
-TEST(ObjectStartBitmapTest, SetBitClearBitImpliesEmpty) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object(123);
- bitmap.SetBit(object);
- bitmap.ClearBit(object);
- EXPECT_TRUE(IsEmpty(bitmap));
-}
-
-TEST(ObjectStartBitmapTest, AdjacentObjectsAtBegin) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object0(0);
- Object object1(1);
- bitmap.SetBit(object0);
- bitmap.SetBit(object1);
- EXPECT_FALSE(bitmap.CheckBit(Object(3)));
- size_t count = 0;
- bitmap.Iterate([&count, object0, object1](Address current) {
- if (count == 0) {
- EXPECT_EQ(object0.address(), current);
- } else if (count == 1) {
- EXPECT_EQ(object1.address(), current);
- }
- count++;
- });
- EXPECT_EQ(2u, count);
-}
-
-TEST(ObjectStartBitmapTest, AdjacentObjectsAtEnd) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- const size_t last_entry_index = ObjectStartBitmap::MaxEntries() - 1;
- Object object0(last_entry_index - 1);
- Object object1(last_entry_index);
- bitmap.SetBit(object0);
- bitmap.SetBit(object1);
- EXPECT_FALSE(bitmap.CheckBit(Object(last_entry_index - 2)));
- size_t count = 0;
- bitmap.Iterate([&count, object0, object1](Address current) {
- if (count == 0) {
- EXPECT_EQ(object0.address(), current);
- } else if (count == 1) {
- EXPECT_EQ(object1.address(), current);
- }
- count++;
- });
- EXPECT_EQ(2u, count);
-}
-
-TEST(ObjectStartBitmapTest, FindHeaderExact) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object(654);
- bitmap.SetBit(object);
- EXPECT_EQ(object.address(), bitmap.FindHeader(object.address()));
-}
-
-TEST(ObjectStartBitmapTest, FindHeaderApproximate) {
- static const size_t kInternalDelta = 37;
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object(654);
- bitmap.SetBit(object);
- EXPECT_EQ(object.address(),
- bitmap.FindHeader(object.address() + kInternalDelta));
-}
-
-TEST(ObjectStartBitmapTest, FindHeaderIteratingWholeBitmap) {
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object_to_find(Object(0));
- Address hint_index = Object(ObjectStartBitmap::MaxEntries() - 1);
- bitmap.SetBit(object_to_find);
- EXPECT_EQ(object_to_find.address(), bitmap.FindHeader(hint_index));
-}
-
-TEST(ObjectStartBitmapTest, FindHeaderNextCell) {
- // This white box test makes use of the fact that cells are of type uint8_t.
- const size_t kCellSize = sizeof(uint8_t);
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object_to_find(Object(kCellSize - 1));
- Address hint = Object(kCellSize);
- bitmap.SetBit(Object(0));
- bitmap.SetBit(object_to_find);
- EXPECT_EQ(object_to_find.address(), bitmap.FindHeader(hint));
-}
-
-TEST(ObjectStartBitmapTest, FindHeaderSameCell) {
- // This white box test makes use of the fact that cells are of type uint8_t.
- const size_t kCellSize = sizeof(uint8_t);
- ObjectStartBitmap bitmap(Object::kBaseOffset);
- Object object_to_find(Object(kCellSize - 1));
- bitmap.SetBit(Object(0));
- bitmap.SetBit(object_to_find);
- EXPECT_EQ(object_to_find.address(),
- bitmap.FindHeader(object_to_find.address()));
-}
-
-} // namespace blink
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 1449205eeb5..0dd7b23ae4c 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
};
}
diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc b/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc
deleted file mode 100644
index 474509cd185..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc
+++ /dev/null
@@ -1,57 +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/heap/persistent.h"
-
-#include <memory>
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-class PersistentTest : public TestSupportingGC {};
-
-namespace {
-
-class Receiver : public GarbageCollected<Receiver> {
- public:
- void Increment(int* counter) { ++*counter; }
-
- void Trace(Visitor* visitor) {}
-};
-
-TEST_F(PersistentTest, BindCancellation) {
- Receiver* receiver = MakeGarbageCollected<Receiver>();
- int counter = 0;
- base::RepeatingClosure function =
- WTF::BindRepeating(&Receiver::Increment, WrapWeakPersistent(receiver),
- WTF::Unretained(&counter));
-
- function.Run();
- EXPECT_EQ(1, counter);
-
- receiver = nullptr;
- PreciselyCollectGarbage();
- function.Run();
- EXPECT_EQ(1, counter);
-}
-
-TEST_F(PersistentTest, CrossThreadBindCancellation) {
- Receiver* receiver = MakeGarbageCollected<Receiver>();
- int counter = 0;
- CrossThreadOnceClosure function = CrossThreadBindOnce(
- &Receiver::Increment, WrapCrossThreadWeakPersistent(receiver),
- WTF::CrossThreadUnretained(&counter));
-
- receiver = nullptr;
- PreciselyCollectGarbage();
- std::move(function).Run();
- EXPECT_EQ(0, counter);
-}
-
-} // namespace
-} // namespace blink
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 ca5058b4f5f..76a8faf64f4 100644
--- a/chromium/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -56,6 +56,7 @@
#include "third_party/blink/renderer/platform/heap/heap_buildflags.h"
#include "third_party/blink/renderer/platform/heap/heap_compact.h"
#include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
+#include "third_party/blink/renderer/platform/heap/marking_scheduling_oracle.h"
#include "third_party/blink/renderer/platform/heap/marking_visitor.h"
#include "third_party/blink/renderer/platform/heap/page_pool.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -132,8 +133,6 @@ class WorkerPoolTaskRunner : public base::TaskRunner {
} // namespace
-constexpr base::TimeDelta ThreadState::kDefaultIncrementalMarkingStepDuration;
-
class ThreadState::IncrementalMarkingScheduler {
public:
explicit IncrementalMarkingScheduler(ThreadState* thread_state)
@@ -158,9 +157,6 @@ class ThreadState::IncrementalMarkingScheduler {
void Init(BlinkGC::GCReason reason) {
DCHECK(!task_.IsActive());
reason_ = reason;
- next_incremental_marking_step_duration_ =
- kDefaultIncrementalMarkingStepDuration;
- previous_incremental_marking_time_left_ = base::TimeDelta::Max();
}
void ScheduleTask() {
@@ -174,10 +170,7 @@ class ThreadState::IncrementalMarkingScheduler {
void Dispatch() {
switch (thread_state_->GetGCState()) {
case ThreadState::kIncrementalMarkingStepScheduled:
- thread_state_->IncrementalMarkingStep(
- BlinkGC::kNoHeapPointersOnStack,
- next_incremental_marking_step_duration_);
- UpdateIncrementalMarkingStepDuration();
+ thread_state_->IncrementalMarkingStep(BlinkGC::kNoHeapPointersOnStack);
if (thread_state_->GetGCState() !=
ThreadState::kIncrementalMarkingStepPaused) {
ScheduleTask();
@@ -191,25 +184,8 @@ class ThreadState::IncrementalMarkingScheduler {
}
}
- void UpdateIncrementalMarkingStepDuration() {
- const ThreadHeap& heap = thread_state_->Heap();
- base::TimeDelta time_left =
- heap.stats_collector()->estimated_marking_time() -
- heap.stats_collector()->marking_time_so_far();
- // Increase step size if estimated time left is increasing.
- if (previous_incremental_marking_time_left_ < time_left) {
- constexpr double ratio = 2.0;
- next_incremental_marking_step_duration_ *= ratio;
- }
- previous_incremental_marking_time_left_ = time_left;
- }
-
ThreadState* thread_state_;
BlinkGC::GCReason reason_;
- base::TimeDelta next_incremental_marking_step_duration_ =
- kDefaultIncrementalMarkingStepDuration;
- base::TimeDelta previous_incremental_marking_time_left_ =
- base::TimeDelta::Max();
TaskHandle task_;
};
@@ -1157,9 +1133,6 @@ void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) {
EnableIncrementalMarkingBarrier();
if (base::FeatureList::IsEnabled(
blink::features::kBlinkHeapConcurrentMarking)) {
- // 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->FlushMarkingWorklists();
// Check that the marking worklist has enough private segments for all
// concurrent marking tasks.
@@ -1185,8 +1158,7 @@ void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) {
}
}
-void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state,
- base::TimeDelta duration) {
+void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state) {
DCHECK(IsMarkingInProgress());
DCHECK_EQ(kIncrementalMarkingStepScheduled, GetGCState());
@@ -1202,11 +1174,15 @@ void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state,
Heap().FlushNotFullyConstructedObjects();
}
- bool complete = true;
- // If duration is 0, should skip incremental marking.
- if (!duration.is_zero()) {
- complete =
- complete && MarkPhaseAdvanceMarking(base::TimeTicks::Now() + duration);
+ bool complete;
+ if (skip_incremental_marking_for_testing_) {
+ complete = true;
+ skip_incremental_marking_for_testing_ = false;
+ } else {
+ complete = MarkPhaseAdvanceMarking(
+ base::TimeTicks::Now() +
+ marking_scheduling_->GetNextIncrementalStepDurationForTask(
+ Heap().stats_collector()->object_size_in_bytes()));
}
if (base::FeatureList::IsEnabled(
@@ -1582,6 +1558,8 @@ void ThreadState::MarkPhasePrologue(BlinkGC::CollectionType collection_type,
this, GetMarkingMode(compaction_enabled));
current_gc_data_.stack_state = stack_state;
current_gc_data_.marking_type = marking_type;
+
+ marking_scheduling_ = std::make_unique<MarkingSchedulingOracle>();
}
void ThreadState::MarkPhaseVisitRoots() {
@@ -1604,17 +1582,34 @@ void ThreadState::MarkPhaseVisitRoots() {
}
}
+bool ThreadState::MarkPhaseAdvanceMarkingBasedOnSchedule(
+ base::TimeDelta max_deadline) {
+ return MarkPhaseAdvanceMarking(
+ base::TimeTicks::Now() +
+ std::min(max_deadline,
+ marking_scheduling_->GetNextIncrementalStepDurationForTask(
+ Heap().stats_collector()->object_size_in_bytes())));
+}
+
bool ThreadState::MarkPhaseAdvanceMarking(base::TimeTicks deadline) {
- return Heap().AdvanceMarking(
- reinterpret_cast<MarkingVisitor*>(current_gc_data_.visitor.get()),
- deadline);
+ MarkingVisitor* visitor = current_gc_data_.visitor.get();
+ const bool finished = Heap().AdvanceMarking(
+ reinterpret_cast<MarkingVisitor*>(visitor), deadline);
+ // visitor->marked_bytes() can also include bytes marked during roots
+ // visitation which is not counted in worklist_processing_time_foreground.
+ // Since the size of the roots is usually small relative to the size of
+ // the object graph, this is fine.
+ marking_scheduling_->UpdateIncrementalMarkingStats(
+ visitor->marked_bytes(),
+ Heap().stats_collector()->worklist_processing_time_foreground());
+ return finished;
}
bool ThreadState::IsVerifyMarkingEnabled() const {
bool should_verify_marking = base::FeatureList::IsEnabled(
blink::features::kBlinkHeapIncrementalMarkingStress);
#if BUILDFLAG(BLINK_HEAP_VERIFICATION)
- should_verify_marking = true;
+ should_verify_marking = (disable_heap_verification_scope_ == 0);
#endif // BLINK_HEAP_VERIFICATION
return should_verify_marking;
}
@@ -1635,13 +1630,13 @@ void ThreadState::MarkPhaseEpilogue(BlinkGC::MarkingType marking_type) {
incremental_marking_scheduler_->Cancel();
- size_t marked_bytes = concurrently_marked_bytes_;
current_gc_data_.visitor->FlushCompactionWorklists();
- marked_bytes += current_gc_data_.visitor->marked_bytes();
current_gc_data_.visitor.reset();
- Heap().stats_collector()->NotifyMarkingCompleted(marked_bytes);
+ Heap().stats_collector()->NotifyMarkingCompleted(
+ marking_scheduling_->GetOverallMarkedBytes());
+ marking_scheduling_.reset();
DEFINE_THREAD_SAFE_STATIC_LOCAL(
CustomCountHistogram, total_object_space_histogram,
@@ -1738,12 +1733,14 @@ void ThreadState::PerformConcurrentMark() {
concurrent_visitor.get(),
base::TimeTicks::Now() + kConcurrentMarkingStepDuration);
+ marking_scheduling_->AddConcurrentlyMarkedBytes(
+ concurrent_visitor->marked_bytes());
+
concurrent_visitor->FlushWorklists();
{
base::AutoLock lock(concurrent_marker_bootstrapping_lock_);
// When marking is done, flush visitor worklists and decrement number of
// active markers so we know how many markers are left
- concurrently_marked_bytes_ += concurrent_visitor->marked_bytes();
available_concurrent_marking_task_ids_.push_back(task_id);
if (finished) {
--active_markers_;
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 fc3f53d1e64..33d35193f71 100644
--- a/chromium/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.h
@@ -65,6 +65,7 @@ class IncrementalMarkingScope;
class CancelableTaskScheduler;
class MarkingVisitor;
+class MarkingSchedulingOracle;
class PersistentNode;
class PersistentRegion;
class ThreadHeap;
@@ -371,6 +372,10 @@ class PLATFORM_EXPORT ThreadState final {
// Returns true if the marking verifier is enabled, false otherwise.
bool IsVerifyMarkingEnabled() const;
+ void SkipIncrementalMarkingForTesting() {
+ skip_incremental_marking_for_testing_ = true;
+ }
+
// Performs stand-alone garbage collections considering only C++ objects for
// testing.
//
@@ -400,14 +405,16 @@ class PLATFORM_EXPORT ThreadState final {
!forced_scheduled_gc_for_testing_;
}
+ void EnterNoHeapVerificationScopeForTesting() {
+ ++disable_heap_verification_scope_;
+ }
+ void LeaveNoHeapVerificationScopeForTesting() {
+ --disable_heap_verification_scope_;
+ }
+
private:
class IncrementalMarkingScheduler;
- // 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 =
- base::TimeDelta::FromMilliseconds(2);
-
// Stores whether some ThreadState is currently in incremental marking.
static AtomicEntryFlag incremental_marking_flag_;
@@ -500,6 +507,7 @@ class PLATFORM_EXPORT ThreadState final {
void MarkPhaseEpilogue(BlinkGC::MarkingType);
void MarkPhaseVisitRoots();
void MarkPhaseVisitNotFullyConstructedObjects();
+ bool MarkPhaseAdvanceMarkingBasedOnSchedule(base::TimeDelta max_deadline);
bool MarkPhaseAdvanceMarking(base::TimeTicks deadline);
void VerifyMarking(BlinkGC::MarkingType);
@@ -535,9 +543,7 @@ class PLATFORM_EXPORT ThreadState final {
// Incremental marking step advance marking on the mutator thread. This method
// also reschedules concurrent marking tasks if needed. The duration parameter
// applies only to incremental marking steps on the mutator thread.
- void IncrementalMarkingStep(
- BlinkGC::StackState,
- base::TimeDelta duration = kDefaultIncrementalMarkingStepDuration);
+ void IncrementalMarkingStep(BlinkGC::StackState);
void IncrementalMarkingFinalize();
// Returns true if concurrent marking is finished (i.e. all current threads
@@ -650,16 +656,20 @@ class PLATFORM_EXPORT ThreadState final {
GCData current_gc_data_;
std::unique_ptr<IncrementalMarkingScheduler> incremental_marking_scheduler_;
+ std::unique_ptr<MarkingSchedulingOracle> marking_scheduling_;
std::unique_ptr<CancelableTaskScheduler> marker_scheduler_;
Vector<uint8_t> available_concurrent_marking_task_ids_;
uint8_t active_markers_ = 0;
base::Lock concurrent_marker_bootstrapping_lock_;
- size_t concurrently_marked_bytes_ = 0;
base::JobHandle sweeper_handle_;
std::atomic_bool has_unswept_pages_{false};
+ size_t disable_heap_verification_scope_ = 0;
+
+ bool skip_incremental_marking_for_testing_ = false;
+
friend class BlinkGCObserver;
friend class incremental_marking_test::IncrementalMarkingScope;
friend class IncrementalMarkingTestDriver;
diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc b/chromium/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
deleted file mode 100644
index 4f5bec849d4..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/run_loop.h"
-#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/heap_test_utilities.h"
-#include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
-#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-
-namespace blink {
-
-namespace {
-
-void RunLoop() {
- base::RunLoop rl;
- // Push quit task.
- ThreadScheduler::Current()->V8TaskRunner()->PostNonNestableTask(
- FROM_HERE, WTF::Bind(rl.QuitWhenIdleClosure()));
- rl.Run();
-}
-
-} // namespace
-
-class ThreadStateSchedulingTest : public TestSupportingGC {
- public:
- void SetUp() override {
- state_ = ThreadState::Current();
- ClearOutOldGarbage();
- initial_gc_age_ = state_->GcAge();
- }
-
- void TearDown() override {
- PreciselyCollectGarbage();
- EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
- EXPECT_FALSE(state_->IsMarkingInProgress());
- EXPECT_FALSE(state_->IsSweepingInProgress());
- }
-
- BlinkGC::GCReason LastReason() const {
- return state_->reason_for_scheduled_gc_;
- }
-
- void RunScheduledGC(BlinkGC::StackState stack_state) {
- state_->RunScheduledGC(stack_state);
- }
-
- // Counter that is incremented when sweep finishes.
- int GCCount() { return state_->GcAge() - initial_gc_age_; }
-
- ThreadState* state() { return state_; }
-
- private:
- ThreadState* state_;
- int initial_gc_age_;
-};
-
-TEST_F(ThreadStateSchedulingTest, RunIncrementalGCForTesting) {
- ThreadStateSchedulingTest* test = this;
-
- EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
- test->state()->StartIncrementalMarking(
- BlinkGC::GCReason::kForcedGCForTesting);
- EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
- test->state()->GetGCState());
-
- RunLoop();
- EXPECT_EQ(ThreadState::kNoGCScheduled, test->state()->GetGCState());
-}
-
-} // namespace blink
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 b415aa40838..8fd7f90bae6 100644
--- a/chromium/third_party/blink/renderer/platform/heap/trace_traits.h
+++ b/chromium/third_party/blink/renderer/platform/heap/trace_traits.h
@@ -8,6 +8,7 @@
#include "base/optional.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/heap_page.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"
@@ -47,7 +48,17 @@ struct AdjustPointerTrait<T, true> {
STATIC_ONLY(AdjustPointerTrait);
static TraceDescriptor GetTraceDescriptor(const void* self) {
- return static_cast<const T*>(self)->GetTraceDescriptor();
+ // Tracing an object, and more specifically GetTraceDescriptor for an
+ // object, implies having a reference which means the object is at least in
+ // construction. Therefore it is guaranteed that the ObjectStartBitmap was
+ // already updated to include the object, and its HeapObjectHeader was
+ // already created.
+ HeapObjectHeader* const header = HeapObjectHeader::FromInnerAddress<
+ HeapObjectHeader::AccessMode::kAtomic>(self);
+ return {header->Payload(),
+ GCInfo::From(
+ header->GcInfoIndex<HeapObjectHeader::AccessMode::kAtomic>())
+ .trace};
}
};
@@ -167,7 +178,7 @@ struct TraceTrait<const T> : public TraceTrait<T> {};
template <typename T>
void TraceTrait<T>::Trace(Visitor* visitor, const void* self) {
static_assert(WTF::IsTraceable<T>::value, "T should be traceable");
- static_cast<T*>(const_cast<void*>(self))->Trace(visitor);
+ static_cast<const T*>(self)->Trace(visitor);
}
// This trace trait for std::pair will null weak members if their referent is
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 319e82a486f..6244e43f985 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
@@ -138,17 +138,15 @@ bool UnifiedHeapController::AdvanceTracing(double deadline_in_ms) {
// progress. Oilpan will additionally schedule marking steps.
ThreadState::AtomicPauseScope atomic_pause_scope(thread_state_);
ScriptForbiddenScope script_forbidden_scope;
- base::TimeTicks deadline =
- base::TimeTicks() + base::TimeDelta::FromMillisecondsD(deadline_in_ms);
- is_tracing_done_ = thread_state_->MarkPhaseAdvanceMarking(deadline);
+ is_tracing_done_ = thread_state_->MarkPhaseAdvanceMarkingBasedOnSchedule(
+ base::TimeDelta::FromMillisecondsD(deadline_in_ms));
if (!is_tracing_done_) {
+ if (base::FeatureList::IsEnabled(
+ blink::features::kBlinkHeapConcurrentMarking)) {
+ thread_state_->ConcurrentMarkingStep();
+ }
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/unsanitized_atomic.cc b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc
index a33e2b21ee0..c175fdb0a07 100644
--- a/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc
+++ b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc
@@ -15,21 +15,42 @@
namespace blink {
namespace internal {
+namespace {
+constexpr int ToGCCMemoryOrder(std::memory_order order) {
+ switch (order) {
+ case std::memory_order_seq_cst:
+ return __ATOMIC_SEQ_CST;
+ case std::memory_order_relaxed:
+ return __ATOMIC_RELAXED;
+ case std::memory_order_acquire:
+ return __ATOMIC_ACQUIRE;
+ case std::memory_order_release:
+ return __ATOMIC_RELEASE;
+ case std::memory_order_acq_rel:
+ return __ATOMIC_ACQ_REL;
+ case std::memory_order_consume:
+ return __ATOMIC_CONSUME;
+ }
+}
+} // namespace
+
template <typename T>
-void UnsanitizedAtomic<T>::store(T value, std::memory_order order) {
- Base::store(value, order);
+void UnsanitizedAtomic<T>::store(T desired, std::memory_order order) {
+ __atomic_store(&value_, &desired, ToGCCMemoryOrder(order));
}
template <typename T>
T UnsanitizedAtomic<T>::load(std::memory_order order) const {
- return Base::load(order);
+ T result;
+ __atomic_load(&value_, &result, ToGCCMemoryOrder(order));
+ return result;
}
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);
+ return compare_exchange_strong(expected, desired, order, order);
}
template <typename T>
@@ -38,15 +59,16 @@ bool UnsanitizedAtomic<T>::compare_exchange_strong(
T desired,
std::memory_order succ_order,
std::memory_order fail_order) {
- return Base::compare_exchange_strong(expected, desired, succ_order,
- fail_order);
+ return __atomic_compare_exchange(&value_, &expected, &desired, false,
+ ToGCCMemoryOrder(succ_order),
+ ToGCCMemoryOrder(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);
+ return compare_exchange_weak(expected, desired, order, order);
}
template <typename T>
@@ -54,7 +76,9 @@ 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);
+ return __atomic_compare_exchange(&value_, &expected, &desired, true,
+ ToGCCMemoryOrder(succ_order),
+ ToGCCMemoryOrder(fail_order));
}
template class PLATFORM_EXPORT UnsanitizedAtomic<uint16_t>;
diff --git a/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h
index fc93c9b4dd8..9242e9263a0 100644
--- a/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h
+++ b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h
@@ -19,10 +19,11 @@ namespace internal {
// 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>;
-
+class PLATFORM_EXPORT UnsanitizedAtomic final {
public:
+ UnsanitizedAtomic() = default;
+ explicit UnsanitizedAtomic(T value) : value_(value) {}
+
void store(T, std::memory_order = std::memory_order_seq_cst);
T load(std::memory_order = std::memory_order_seq_cst) const;
@@ -35,6 +36,9 @@ class PLATFORM_EXPORT UnsanitizedAtomic final : private std::atomic<T> {
T,
std::memory_order = std::memory_order_seq_cst);
bool compare_exchange_weak(T&, T, std::memory_order, std::memory_order);
+
+ private:
+ T value_;
};
template <typename T>
diff --git a/chromium/third_party/blink/renderer/platform/heap/visitor.h b/chromium/third_party/blink/renderer/platform/heap/visitor.h
index b2880df6f4f..15ca5dbdbf5 100644
--- a/chromium/third_party/blink/renderer/platform/heap/visitor.h
+++ b/chromium/third_party/blink/renderer/platform/heap/visitor.h
@@ -74,7 +74,7 @@ template <typename T, void (T::*method)(Visitor*) const>
struct TraceMethodDelegate {
STATIC_ONLY(TraceMethodDelegate);
static void Trampoline(Visitor* visitor, const void* self) {
- (reinterpret_cast<T*>(const_cast<void*>(self))->*method)(visitor);
+ (reinterpret_cast<const T*>(self)->*method)(visitor);
}
};
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
deleted file mode 100644
index aaa2140c7b5..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc
+++ /dev/null
@@ -1,247 +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 <atomic>
-#include <iostream>
-#include <memory>
-
-#include "testing/gtest/include/gtest/gtest.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/visitor.h"
-#include "third_party/blink/renderer/platform/wtf/hash_traits.h"
-
-namespace blink {
-
-namespace {
-
-class WeaknessMarkingTest : public TestSupportingGC {};
-
-} // namespace
-
-enum class ObjectLiveness { Alive = 0, Dead };
-
-template <typename Map,
- template <typename T>
- class KeyHolder,
- template <typename T>
- class ValueHolder>
-void TestMapImpl(ObjectLiveness expected_key_liveness,
- ObjectLiveness expected_value_liveness) {
- Persistent<Map> map = MakeGarbageCollected<Map>();
- KeyHolder<IntegerObject> int_key = MakeGarbageCollected<IntegerObject>(1);
- ValueHolder<IntegerObject> int_value = MakeGarbageCollected<IntegerObject>(2);
- map->insert(int_key.Get(), int_value.Get());
- TestSupportingGC::PreciselyCollectGarbage();
- if (expected_key_liveness == ObjectLiveness::Alive) {
- EXPECT_TRUE(int_key.Get());
- } else {
- EXPECT_FALSE(int_key.Get());
- }
- if (expected_value_liveness == ObjectLiveness::Alive) {
- EXPECT_TRUE(int_value.Get());
- } else {
- EXPECT_FALSE(int_value.Get());
- }
- EXPECT_EQ(((expected_key_liveness == ObjectLiveness::Alive) &&
- (expected_value_liveness == ObjectLiveness::Alive))
- ? 1u
- : 0u,
- map->size());
-}
-
-TEST_F(WeaknessMarkingTest, WeakToWeakMap) {
- using Map = HeapHashMap<WeakMember<IntegerObject>, WeakMember<IntegerObject>>;
- TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
- ObjectLiveness::Alive);
- TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
- ObjectLiveness::Dead);
- TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
- ObjectLiveness::Dead);
-}
-
-TEST_F(WeaknessMarkingTest, WeakToStrongMap) {
- using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
- TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Dead,
- ObjectLiveness::Alive);
- TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
- ObjectLiveness::Dead);
-}
-
-TEST_F(WeaknessMarkingTest, StrongToWeakMap) {
- using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
- TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
- ObjectLiveness::Dead);
- TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Dead,
- ObjectLiveness::Dead);
-}
-
-TEST_F(WeaknessMarkingTest, StrongToStrongMap) {
- using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>;
- TestMapImpl<Map, Persistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, Persistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, Persistent, WeakPersistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
- TestMapImpl<Map, WeakPersistent, WeakPersistent>(ObjectLiveness::Alive,
- ObjectLiveness::Alive);
-}
-
-template <typename Set, template <typename T> class Type>
-void TestSetImpl(ObjectLiveness object_liveness) {
- Persistent<Set> set = MakeGarbageCollected<Set>();
- Type<IntegerObject> object = MakeGarbageCollected<IntegerObject>(1);
- set->insert(object.Get());
- TestSupportingGC::PreciselyCollectGarbage();
- if (object_liveness == ObjectLiveness::Alive) {
- EXPECT_TRUE(object.Get());
- } else {
- EXPECT_FALSE(object.Get());
- }
- EXPECT_EQ((object_liveness == ObjectLiveness::Alive) ? 1u : 0u, set->size());
-}
-
-TEST_F(WeaknessMarkingTest, WeakSet) {
- using Set = HeapHashSet<WeakMember<IntegerObject>>;
- TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
- TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Dead);
-}
-
-TEST_F(WeaknessMarkingTest, StrongSet) {
- using Set = HeapHashSet<Member<IntegerObject>>;
- TestSetImpl<Set, Persistent>(ObjectLiveness::Alive);
- TestSetImpl<Set, WeakPersistent>(ObjectLiveness::Alive);
-}
-
-TEST_F(WeaknessMarkingTest, DeadValueInReverseEphemeron) {
- using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
- Persistent<Map> map = MakeGarbageCollected<Map>();
- Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
- map->insert(key.Get(), MakeGarbageCollected<IntegerObject>(2));
- EXPECT_EQ(1u, map->size());
- TestSupportingGC::PreciselyCollectGarbage();
- // Entries with dead values are removed.
- EXPECT_EQ(0u, map->size());
-}
-
-TEST_F(WeaknessMarkingTest, NullValueInReverseEphemeron) {
- using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>;
- Persistent<Map> map = MakeGarbageCollected<Map>();
- Persistent<IntegerObject> key = MakeGarbageCollected<IntegerObject>(1);
- map->insert(key.Get(), nullptr);
- EXPECT_EQ(1u, map->size());
- TestSupportingGC::PreciselyCollectGarbage();
- // Entries with null values are kept.
- EXPECT_EQ(1u, map->size());
-}
-
-namespace weakness_marking_test {
-
-class EphemeronCallbacksCounter
- : public GarbageCollected<EphemeronCallbacksCounter> {
- public:
- EphemeronCallbacksCounter(size_t* count_holder)
- : count_holder_(count_holder) {}
-
- void Trace(Visitor* visitor) {
- visitor->RegisterWeakCallbackMethod<EphemeronCallbacksCounter,
- &EphemeronCallbacksCounter::Callback>(
- this);
- }
-
- void Callback(const LivenessBroker& info) {
- *count_holder_ = ThreadState::Current()->Heap().ephemeron_callbacks_.size();
- }
-
- private:
- size_t* count_holder_;
-};
-
-TEST_F(WeaknessMarkingTest, UntracableEphemeronIsNotRegsitered) {
- size_t ephemeron_count;
- Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
- MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
- TestSupportingGC::PreciselyCollectGarbage();
- size_t old_ephemeron_count = ephemeron_count;
- using Map = HeapHashMap<WeakMember<IntegerObject>, int>;
- Persistent<Map> map = MakeGarbageCollected<Map>();
- map->insert(MakeGarbageCollected<IntegerObject>(1), 2);
- TestSupportingGC::PreciselyCollectGarbage();
- // Ephemeron value is not traceable, thus the map shouldn't be treated as an
- // ephemeron.
- EXPECT_EQ(old_ephemeron_count, ephemeron_count);
-}
-
-TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) {
- size_t ephemeron_count;
- Persistent<EphemeronCallbacksCounter> ephemeron_callbacks_counter =
- MakeGarbageCollected<EphemeronCallbacksCounter>(&ephemeron_count);
- TestSupportingGC::PreciselyCollectGarbage();
- size_t old_ephemeron_count = ephemeron_count;
- using Map = HeapHashMap<WeakMember<IntegerObject>, Member<IntegerObject>>;
- Persistent<Map> map = MakeGarbageCollected<Map>();
- map->insert(MakeGarbageCollected<IntegerObject>(1),
- MakeGarbageCollected<IntegerObject>(2));
- TestSupportingGC::PreciselyCollectGarbage();
- 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 b3f341077ae..6bce8f5808a 100644
--- a/chromium/third_party/blink/renderer/platform/heap/worklist.h
+++ b/chromium/third_party/blink/renderer/platform/heap/worklist.h
@@ -16,9 +16,9 @@
#include <utility>
#include "base/atomicops.h"
+#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
-#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -202,6 +202,14 @@ class Worklist {
global_pool_.Merge(&other->global_pool_);
}
+ size_t SizeForTesting() {
+ size_t size = global_pool_.SizeForTesting();
+ for (int i = 0; i < kNumTasks; i++) {
+ size += private_pop_segment(i)->Size() + private_push_segment(i)->Size();
+ }
+ return size;
+ }
+
private:
FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentCreate);
FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentPush);
@@ -383,6 +391,14 @@ class Worklist {
}
}
+ size_t SizeForTesting() {
+ size_t size = 0;
+ base::AutoLock guard(lock_);
+ for (Segment* current = top_; current; current = current->next())
+ size += current->Size();
+ return size;
+ }
+
private:
void set_top(Segment* segment) {
return base::subtle::NoBarrier_Store(
diff --git a/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc b/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc
deleted file mode 100644
index 1030cbab9ac..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc
+++ /dev/null
@@ -1,352 +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.
-//
-// Copied and adopted from V8.
-//
-// Copyright 2017 the V8 project authors. 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/worklist.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-class SomeObject {};
-} // namespace
-
-using TestWorklist = Worklist<SomeObject*, 64 /* entries */, 8 /* tasks */>;
-
-TEST(WorklistTest, SegmentCreate) {
- TestWorklist::Segment segment;
- EXPECT_TRUE(segment.IsEmpty());
- EXPECT_EQ(0u, segment.Size());
- EXPECT_FALSE(segment.IsFull());
-}
-
-TEST(WorklistTest, SegmentPush) {
- TestWorklist::Segment segment;
- EXPECT_EQ(0u, segment.Size());
- EXPECT_TRUE(segment.Push(nullptr));
- EXPECT_EQ(1u, segment.Size());
-}
-
-TEST(WorklistTest, SegmentPushPop) {
- TestWorklist::Segment segment;
- EXPECT_TRUE(segment.Push(nullptr));
- EXPECT_EQ(1u, segment.Size());
- SomeObject dummy;
- SomeObject* object = &dummy;
- EXPECT_TRUE(segment.Pop(&object));
- EXPECT_EQ(0u, segment.Size());
- EXPECT_EQ(nullptr, object);
-}
-
-TEST(WorklistTest, SegmentIsEmpty) {
- TestWorklist::Segment segment;
- EXPECT_TRUE(segment.IsEmpty());
- EXPECT_TRUE(segment.Push(nullptr));
- EXPECT_FALSE(segment.IsEmpty());
-}
-
-TEST(WorklistTest, SegmentIsFull) {
- TestWorklist::Segment segment;
- EXPECT_FALSE(segment.IsFull());
- for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
- EXPECT_TRUE(segment.Push(nullptr));
- }
- EXPECT_TRUE(segment.IsFull());
-}
-
-TEST(WorklistTest, SegmentClear) {
- TestWorklist::Segment segment;
- EXPECT_TRUE(segment.Push(nullptr));
- EXPECT_FALSE(segment.IsEmpty());
- segment.Clear();
- EXPECT_TRUE(segment.IsEmpty());
- for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
- EXPECT_TRUE(segment.Push(nullptr));
- }
-}
-
-TEST(WorklistTest, SegmentFullPushFails) {
- TestWorklist::Segment segment;
- EXPECT_FALSE(segment.IsFull());
- for (size_t i = 0; i < TestWorklist::Segment::kCapacity; i++) {
- EXPECT_TRUE(segment.Push(nullptr));
- }
- EXPECT_TRUE(segment.IsFull());
- EXPECT_FALSE(segment.Push(nullptr));
-}
-
-TEST(WorklistTest, SegmentEmptyPopFails) {
- TestWorklist::Segment segment;
- EXPECT_TRUE(segment.IsEmpty());
- SomeObject* object;
- EXPECT_FALSE(segment.Pop(&object));
-}
-
-TEST(WorklistTest, SegmentUpdateFalse) {
- TestWorklist::Segment segment;
- SomeObject* object;
- object = reinterpret_cast<SomeObject*>(&object);
- EXPECT_TRUE(segment.Push(object));
- segment.Update([](SomeObject* object, SomeObject** out) { return false; });
- EXPECT_TRUE(segment.IsEmpty());
-}
-
-TEST(WorklistTest, SegmentUpdate) {
- TestWorklist::Segment segment;
- SomeObject* objectA;
- objectA = reinterpret_cast<SomeObject*>(&objectA);
- SomeObject* objectB;
- objectB = reinterpret_cast<SomeObject*>(&objectB);
- EXPECT_TRUE(segment.Push(objectA));
- segment.Update([objectB](SomeObject* object, SomeObject** out) {
- *out = objectB;
- return true;
- });
- SomeObject* object;
- EXPECT_TRUE(segment.Pop(&object));
- EXPECT_EQ(object, objectB);
-}
-
-TEST(WorklistTest, CreateEmpty) {
- TestWorklist worklist;
- TestWorklist::View worklist_view(&worklist, 0);
- EXPECT_TRUE(worklist_view.IsLocalEmpty());
- EXPECT_TRUE(worklist.IsGlobalEmpty());
-}
-
-TEST(WorklistTest, LocalPushPop) {
- TestWorklist worklist;
- TestWorklist::View worklist_view(&worklist, 0);
- SomeObject dummy;
- SomeObject* retrieved = nullptr;
- EXPECT_TRUE(worklist_view.Push(&dummy));
- EXPECT_FALSE(worklist_view.IsLocalEmpty());
- EXPECT_TRUE(worklist_view.Pop(&retrieved));
- EXPECT_EQ(&dummy, retrieved);
-}
-
-TEST(WorklistTest, LocalIsBasedOnId) {
- TestWorklist worklist;
- // Use the same id.
- TestWorklist::View worklist_view1(&worklist, 0);
- TestWorklist::View worklist_view2(&worklist, 0);
- SomeObject dummy;
- SomeObject* retrieved = nullptr;
- EXPECT_TRUE(worklist_view1.Push(&dummy));
- EXPECT_FALSE(worklist_view1.IsLocalEmpty());
- EXPECT_FALSE(worklist_view2.IsLocalEmpty());
- EXPECT_TRUE(worklist_view2.Pop(&retrieved));
- EXPECT_EQ(&dummy, retrieved);
- EXPECT_TRUE(worklist_view1.IsLocalEmpty());
- EXPECT_TRUE(worklist_view2.IsLocalEmpty());
-}
-
-TEST(WorklistTest, LocalPushStaysPrivate) {
- TestWorklist worklist;
- TestWorklist::View worklist_view1(&worklist, 0);
- TestWorklist::View worklist_view2(&worklist, 1);
- 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) {
- TestWorklist worklist;
- TestWorklist::View worklist_view(&worklist, 0);
- SomeObject* object;
- object = reinterpret_cast<SomeObject*>(&object);
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view.Push(object));
- }
- 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) {
- TestWorklist worklist;
- TestWorklist::View worklist_view(&worklist, 0);
- SomeObject* objectA = nullptr;
- objectA = reinterpret_cast<SomeObject*>(&objectA);
- SomeObject* objectB = nullptr;
- objectB = reinterpret_cast<SomeObject*>(&objectB);
- SomeObject* objectC = nullptr;
- objectC = reinterpret_cast<SomeObject*>(&objectC);
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view.Push(objectA));
- }
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view.Push(objectB));
- }
- EXPECT_TRUE(worklist_view.Push(objectA));
- worklist.Update([objectA, objectC](SomeObject* object, SomeObject** out) {
- if (object != objectA) {
- *out = objectC;
- return true;
- }
- return false;
- });
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- SomeObject* object;
- EXPECT_TRUE(worklist_view.Pop(&object));
- EXPECT_EQ(object, objectC);
- }
-}
-
-TEST(WorklistTest, FlushToGlobalPushSegment) {
- TestWorklist worklist;
- TestWorklist::View worklist_view0(&worklist, 0);
- TestWorklist::View worklist_view1(&worklist, 1);
- SomeObject* object = nullptr;
- SomeObject* objectA = nullptr;
- 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));
-}
-
-TEST(WorklistTest, FlushToGlobalPopSegment) {
- TestWorklist worklist;
- TestWorklist::View worklist_view0(&worklist, 0);
- TestWorklist::View worklist_view1(&worklist, 1);
- SomeObject* object = nullptr;
- SomeObject* objectA = nullptr;
- objectA = reinterpret_cast<SomeObject*>(&objectA);
- EXPECT_TRUE(worklist_view0.Push(objectA));
- 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));
-}
-
-TEST(WorklistTest, Clear) {
- TestWorklist worklist;
- TestWorklist::View worklist_view(&worklist, 0);
- SomeObject* object;
- object = reinterpret_cast<SomeObject*>(&object);
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- 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) {
- TestWorklist worklist;
- TestWorklist::View worklist_view1(&worklist, 0);
- TestWorklist::View worklist_view2(&worklist, 1);
- SomeObject dummy;
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view1.Push(&dummy));
- }
- SomeObject* retrieved = nullptr;
- // One more push/pop to publish the full segment.
- 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));
- EXPECT_EQ(&dummy, retrieved);
- EXPECT_FALSE(worklist_view1.Pop(&retrieved));
- }
- EXPECT_TRUE(worklist.IsGlobalEmpty());
- EXPECT_EQ(0U, worklist.GlobalPoolSize());
-}
-
-TEST(WorklistTest, MultipleSegmentsStolen) {
- TestWorklist worklist;
- TestWorklist::View worklist_view1(&worklist, 0);
- TestWorklist::View worklist_view2(&worklist, 1);
- TestWorklist::View worklist_view3(&worklist, 2);
- SomeObject dummy1;
- SomeObject dummy2;
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view1.Push(&dummy1));
- }
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view1.Push(&dummy2));
- }
- SomeObject* retrieved = nullptr;
- SomeObject dummy3;
- // One more push/pop to publish the full segment.
- 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);
- for (size_t i = 1; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view2.Pop(&retrieved));
- EXPECT_EQ(expect_bag2, retrieved);
- EXPECT_FALSE(worklist_view1.Pop(&retrieved));
- }
- for (size_t i = 1; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view3.Pop(&retrieved));
- EXPECT_EQ(expect_bag3, retrieved);
- EXPECT_FALSE(worklist_view1.Pop(&retrieved));
- }
- EXPECT_TRUE(worklist.IsGlobalEmpty());
-}
-
-TEST(WorklistTest, MergeGlobalPool) {
- TestWorklist worklist1;
- TestWorklist::View worklist_view1(&worklist1, 0);
- SomeObject dummy;
- for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) {
- EXPECT_TRUE(worklist_view1.Push(&dummy));
- }
- SomeObject* retrieved = nullptr;
- // One more push/pop to publish the full segment.
- 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));
- EXPECT_EQ(&dummy, retrieved);
- EXPECT_FALSE(worklist_view1.Pop(&retrieved));
- }
- EXPECT_TRUE(worklist1.IsGlobalEmpty());
- EXPECT_TRUE(worklist2.IsGlobalEmpty());
-}
-
-} // namespace blink
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
deleted file mode 100644
index 92bf0523d47..00000000000
--- a/chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (c) 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 "base/callback.h"
-#include "testing/gtest/include/gtest/gtest.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"
-
-namespace blink {
-
-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;
- virtual void Trace(Visitor*) {}
-};
-
-base::TimeDelta TimedRun(base::RepeatingCallback<void()> callback) {
- const base::TimeTicks start = base::TimeTicks::Now();
- callback.Run();
- return base::TimeTicks::Now() - start;
-}
-
-} // namespace
-
-TEST_F(WriteBarrierPerfTest, MemberWritePerformance) {
- // Setup.
- constexpr wtf_size_t kNumElements = 100000;
- Persistent<HeapVector<Member<PerfDummyObject>>> holder(
- MakeGarbageCollected<HeapVector<Member<PerfDummyObject>>>());
- for (wtf_size_t i = 0; i < kNumElements; ++i) {
- holder->push_back(MakeGarbageCollected<PerfDummyObject>());
- }
- PreciselyCollectGarbage();
- // Benchmark.
- base::RepeatingCallback<void()> benchmark = base::BindRepeating(
- [](const Persistent<HeapVector<Member<PerfDummyObject>>>& holder) {
- for (wtf_size_t i = 0; i < kNumElements / 2; ++i) {
- (*holder)[i].Swap((*holder)[kNumElements / 2 + i]);
- }
- },
- holder);
-
- // During GC.
- IncrementalMarkingTestDriver driver(ThreadState::Current());
- driver.Start();
- base::TimeDelta during_gc_duration = TimedRun(benchmark);
- driver.FinishSteps();
- PreciselyCollectGarbage();
-
- // Outside GC.
- base::TimeDelta outside_gc_duration = TimedRun(benchmark);
-
- // Cleanup.
- holder.Clear();
- PreciselyCollectGarbage();
-
- // Reporting.
- 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
index 1c439a68ae8..ac3baf9dc3d 100644
--- a/chromium/third_party/blink/renderer/platform/heap_observer_list.h
+++ b/chromium/third_party/blink/renderer/platform/heap_observer_list.h
@@ -66,7 +66,7 @@ class PLATFORM_EXPORT HeapObserverList {
}
}
- void Trace(Visitor* visitor) { visitor->Trace(observers_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(observers_); }
private:
using ObserverSet = HeapLinkedHashSet<WeakMember<ObserverType>>;
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
index 1797cf0482f..225ac271de3 100644
--- a/chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc
+++ b/chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc
@@ -39,7 +39,7 @@ class TestingNotifier final : public GarbageCollected<TestingNotifier> {
HeapObserverList<TestingObserver>& ObserverList() { return observer_list_; }
- void Trace(Visitor* visitor) { visitor->Trace(observer_list_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(observer_list_); }
private:
HeapObserverList<TestingObserver> observer_list_;
@@ -50,7 +50,7 @@ class TestingObserver final : public GarbageCollected<TestingObserver> {
TestingObserver() = default;
void OnNotification() { count_++; }
int Count() { return count_; }
- void Trace(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
private:
int count_ = 0;
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index 0f10eb02ebd..8f3a4b42133 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -4,13 +4,17 @@
#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h"
+#include <cstring>
#include <memory>
+#include "base/bits.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/ranges.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
#include "media/base/video_color_space.h"
@@ -36,31 +40,105 @@
namespace {
+// Builds a gfx::ColorSpace from the ITU-T H.273 (CICP) color description in the
+// image. This color space is used to create the gfx::ColorTransform for the
+// YUV-to-RGB conversion. If the image does not have an ICC profile, this color
+// space is also used to create the embedded color profile.
gfx::ColorSpace GetColorSpace(const avifImage* image) {
- if (image->icc.size) {
- auto iccp = gfx::ICCProfile::FromData(image->icc.data, image->icc.size);
- if (iccp.IsValid())
- return iccp.GetColorSpace();
-
- // TODO(dalecurtis): Do we need to reparse this per frame when dealing
- // with animated AVIF files? Or is it only for still picture?
-
- // TODO(wtc): We need to set the color profile using
- // SetEmbeddedColorProfile() rather than handling all the color space
- // conversion during decode.
- }
- media::VideoColorSpace color_space(
- image->colorPrimaries, image->transferCharacteristics,
- image->matrixCoefficients,
- image->yuvRange == AVIF_RANGE_FULL ? gfx::ColorSpace::RangeID::FULL
- : gfx::ColorSpace::RangeID::LIMITED);
+ // MIAF Section 7.3.6.4 says:
+ // If a coded image has no associated colour property, the default property
+ // is defined as having colour_type equal to 'nclx' with properties as
+ // follows:
+ // – For YCbCr encoding, sYCC should be assumed as indicated by
+ // colour_primaries equal to 1, transfer_characteristics equal to 13,
+ // matrix_coefficients equal to 1, and full_range_flag equal to 1.
+ // ...
+ // Note that this only specifies the default color property when the color
+ // property is absent. It does not really specify the default values for
+ // colour_primaries, transfer_characteristics, and matrix_coefficients when
+ // they are equal to 2 (unspecified). But we will interpret it as specifying
+ // the default values for these variables because we must choose some defaults
+ // and these are the most reasonable defaults to choose. We also advocate that
+ // all AVIF decoders choose these defaults:
+ // https://github.com/AOMediaCodec/av1-avif/issues/84
+ const auto primaries =
+ image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED
+ ? AVIF_COLOR_PRIMARIES_BT709
+ : image->colorPrimaries;
+ const auto transfer = image->transferCharacteristics ==
+ AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED
+ ? AVIF_TRANSFER_CHARACTERISTICS_SRGB
+ : image->transferCharacteristics;
+ const auto matrix =
+ image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED
+ ? AVIF_MATRIX_COEFFICIENTS_BT709
+ : image->matrixCoefficients;
+ const auto range = image->yuvRange == AVIF_RANGE_FULL
+ ? gfx::ColorSpace::RangeID::FULL
+ : gfx::ColorSpace::RangeID::LIMITED;
+ media::VideoColorSpace color_space(primaries, transfer, matrix, range);
if (color_space.IsSpecified())
return color_space.ToGfxColorSpace();
+ // media::VideoColorSpace and gfx::ColorSpace do not support CICP
+ // MatrixCoefficients 12, 13, 14.
+ DCHECK_GE(matrix, 12);
+ DCHECK_LE(matrix, 14);
if (image->yuvRange == AVIF_RANGE_FULL)
return gfx::ColorSpace::CreateJpeg();
return gfx::ColorSpace::CreateREC709();
}
+// Returns the SkYUVColorSpace that matches |image|->matrixCoefficients and
+// |image|->yuvRange.
+base::Optional<SkYUVColorSpace> GetSkYUVColorSpace(const avifImage* image) {
+ const auto matrix =
+ image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED
+ ? AVIF_MATRIX_COEFFICIENTS_BT709
+ : image->matrixCoefficients;
+ if (image->yuvRange == AVIF_RANGE_FULL) {
+ if (matrix == AVIF_MATRIX_COEFFICIENTS_BT470BG ||
+ matrix == AVIF_MATRIX_COEFFICIENTS_BT601) {
+ return kJPEG_SkYUVColorSpace;
+ }
+ return base::nullopt;
+ }
+
+ if (matrix == AVIF_MATRIX_COEFFICIENTS_BT470BG ||
+ matrix == AVIF_MATRIX_COEFFICIENTS_BT601) {
+ return kRec601_SkYUVColorSpace;
+ }
+ if (matrix == AVIF_MATRIX_COEFFICIENTS_BT709) {
+ return kRec709_SkYUVColorSpace;
+ }
+ if (matrix == AVIF_MATRIX_COEFFICIENTS_BT2020_NCL) {
+ return kBT2020_SkYUVColorSpace;
+ }
+ return base::nullopt;
+}
+
+// Returns whether media::PaintCanvasVideoRenderer (PCVR) can convert the YUV
+// color space of |image| to RGB.
+// media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels() uses libyuv
+// for the YUV-to-RGB conversion.
+//
+// NOTE: Ideally, this function should be a static method of
+// media::PaintCanvasVideoRenderer. We did not do that because
+// media::PaintCanvasVideoRenderer uses the JPEG matrix coefficients for all
+// full-range YUV color spaces, but we want to use the JPEG matrix coefficients
+// only for full-range BT.601 YUV.
+bool IsColorSpaceSupportedByPCVR(const avifImage* image) {
+ base::Optional<SkYUVColorSpace> yuv_color_space = GetSkYUVColorSpace(image);
+ if (!yuv_color_space)
+ return false;
+ if (!image->alphaPlane)
+ return true;
+ // libyuv supports the alpha channel only with the I420 pixel format, which is
+ // 8-bit YUV 4:2:0 with kRec601_SkYUVColorSpace.
+ return image->depth == 8 && image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 &&
+ *yuv_color_space == kRec601_SkYUVColorSpace &&
+ image->alphaRange == AVIF_RANGE_FULL;
+}
+
media::VideoPixelFormat AvifToVideoPixelFormat(avifPixelFormat fmt, int depth) {
if (depth != 8 && depth != 10 && depth != 12) {
// Unsupported bit depth.
@@ -79,40 +157,59 @@ media::VideoPixelFormat AvifToVideoPixelFormat(avifPixelFormat fmt, int depth) {
media::PIXEL_FORMAT_YUV444P12};
switch (fmt) {
case AVIF_PIXEL_FORMAT_YUV420:
+ case AVIF_PIXEL_FORMAT_YUV400:
return kYUV420Formats[index];
case AVIF_PIXEL_FORMAT_YUV422:
return kYUV422Formats[index];
case AVIF_PIXEL_FORMAT_YUV444:
return kYUV444Formats[index];
- case AVIF_PIXEL_FORMAT_YV12:
- NOTIMPLEMENTED();
- return media::PIXEL_FORMAT_UNKNOWN;
case AVIF_PIXEL_FORMAT_NONE:
NOTREACHED();
return media::PIXEL_FORMAT_UNKNOWN;
}
}
+// |y_size| is the width or height of the Y plane. Returns the width or height
+// of the U and V planes. |chroma_shift| represents the subsampling of the
+// chroma (U and V) planes in the x (for width) or y (for height) direction.
+int UVSize(int y_size, int chroma_shift) {
+ DCHECK(chroma_shift == 0 || chroma_shift == 1);
+ return (y_size + chroma_shift) >> chroma_shift;
+}
+
inline void WritePixel(float max_channel,
const gfx::Point3F& pixel,
- int alpha,
+ float alpha,
+ bool premultiply_alpha,
uint32_t* rgba_dest) {
- *rgba_dest = SkPackARGB32NoCheck(
- alpha,
- gfx::ToRoundedInt(base::ClampToRange(pixel.x(), 0.0f, 1.0f) * 255.0f),
- gfx::ToRoundedInt(base::ClampToRange(pixel.y(), 0.0f, 1.0f) * 255.0f),
- gfx::ToRoundedInt(base::ClampToRange(pixel.z(), 0.0f, 1.0f) * 255.0f));
+ unsigned r =
+ gfx::ToRoundedInt(base::ClampToRange(pixel.x(), 0.0f, 1.0f) * 255.0f);
+ unsigned g =
+ gfx::ToRoundedInt(base::ClampToRange(pixel.y(), 0.0f, 1.0f) * 255.0f);
+ unsigned b =
+ gfx::ToRoundedInt(base::ClampToRange(pixel.z(), 0.0f, 1.0f) * 255.0f);
+ unsigned a = gfx::ToRoundedInt(alpha * 255.0f);
+ if (premultiply_alpha)
+ blink::ImageFrame::SetRGBAPremultiply(rgba_dest, r, g, b, a);
+ else
+ *rgba_dest = SkPackARGB32NoCheck(a, r, g, b);
}
inline void WritePixel(float max_channel,
const gfx::Point3F& pixel,
- int alpha,
+ float alpha,
+ bool premultiply_alpha,
uint64_t* rgba_dest) {
float rgba_pixels[4];
rgba_pixels[0] = pixel.x();
rgba_pixels[1] = pixel.y();
rgba_pixels[2] = pixel.z();
- rgba_pixels[3] = alpha / max_channel;
+ rgba_pixels[3] = alpha;
+ if (premultiply_alpha && alpha != 1.0f) {
+ rgba_pixels[0] *= alpha;
+ rgba_pixels[1] *= alpha;
+ rgba_pixels[2] *= alpha;
+ }
gfx::FloatToHalfFloat(rgba_pixels, reinterpret_cast<uint16_t*>(rgba_dest),
base::size(rgba_pixels));
@@ -123,6 +220,7 @@ enum class ColorType { kMono, kColor };
template <ColorType color_type, typename InputType, typename OutputType>
void YUVAToRGBA(const avifImage* image,
const gfx::ColorTransform* transform,
+ bool premultiply_alpha,
OutputType* rgba_dest) {
avifPixelFormatInfo format_info;
avifGetPixelFormatInfo(image->yuvFormat, &format_info);
@@ -146,24 +244,11 @@ void YUVAToRGBA(const avifImage* image,
for (uint32_t i = 0; i < image->width; ++i) {
const int uv_i = i >> format_info.chromaShiftX;
- // TODO(wtc): Use templates or other ways to avoid doing this comparison
- // and checking whether the image supports alpha in the inner loop.
- if (image->yuvRange == AVIF_RANGE_LIMITED) {
- pixel.set_x(avifLimitedToFullY(image->depth, y_ptr[i]) / max_channel);
- if (color_type == ColorType::kColor) {
- pixel.set_y(avifLimitedToFullUV(image->depth, u_ptr[uv_i]) /
- max_channel);
- pixel.set_z(avifLimitedToFullUV(image->depth, v_ptr[uv_i]) /
- max_channel);
- }
+ pixel.set_x(y_ptr[i] / max_channel);
+ if (color_type == ColorType::kColor) {
+ pixel.set_y(u_ptr[uv_i] / max_channel);
+ pixel.set_z(v_ptr[uv_i] / max_channel);
} else {
- pixel.set_x(y_ptr[i] / max_channel);
- if (color_type == ColorType::kColor) {
- pixel.set_y(u_ptr[uv_i] / max_channel);
- pixel.set_z(v_ptr[uv_i] / max_channel);
- }
- }
- if (color_type == ColorType::kMono) {
pixel.set_y(0.5f);
pixel.set_z(0.5f);
}
@@ -171,13 +256,17 @@ void YUVAToRGBA(const avifImage* image,
transform->Transform(&pixel, 1);
int alpha = max_channel_i;
+ // TODO(wtc): Use templates or other ways to avoid checking whether the
+ // image supports alpha and whether alpha is limited range in the inner
+ // loop.
if (a_ptr) {
alpha = a_ptr[i];
if (image->alphaRange == AVIF_RANGE_LIMITED)
alpha = avifLimitedToFullY(image->depth, alpha);
}
- WritePixel(max_channel, pixel, alpha, rgba_dest);
+ WritePixel(max_channel, pixel, alpha / max_channel, premultiply_alpha,
+ rgba_dest);
rgba_dest++;
}
}
@@ -199,7 +288,7 @@ AVIFImageDecoder::AVIFImageDecoder(AlphaOption alpha_option,
AVIFImageDecoder::~AVIFImageDecoder() = default;
bool AVIFImageDecoder::ImageIsHighBitDepth() {
- return is_high_bit_depth_;
+ return bit_depth_ > 8;
}
void AVIFImageDecoder::OnSetData(SegmentReader* data) {
@@ -210,6 +299,116 @@ void AVIFImageDecoder::OnSetData(SegmentReader* data) {
SetFailed();
}
+IntSize AVIFImageDecoder::DecodedYUVSize(int component) const {
+ DCHECK_GE(component, 0);
+ // TODO(crbug.com/910276): Change after alpha support.
+ DCHECK_LE(component, 2);
+ DCHECK(IsDecodedSizeAvailable());
+ if (component == SkYUVAIndex::kU_Index ||
+ component == SkYUVAIndex::kV_Index) {
+ return IntSize(UVSize(Size().Width(), chroma_shift_x_),
+ UVSize(Size().Height(), chroma_shift_y_));
+ }
+ return Size();
+}
+
+size_t AVIFImageDecoder::DecodedYUVWidthBytes(int component) const {
+ DCHECK_GE(component, 0);
+ // TODO(crbug.com/910276): Change after alpha support.
+ DCHECK_LE(component, 2);
+ DCHECK(IsDecodedSizeAvailable());
+ // Try to return the same width bytes as used by the dav1d library. This will
+ // allow DecodeToYUV() to copy each plane with a single memcpy() call.
+ //
+ // The comments for Dav1dPicAllocator in dav1d/picture.h require the pixel
+ // width be padded to a multiple of 128 pixels.
+ int aligned_width = base::bits::Align(Size().Width(), 128);
+ if (component == SkYUVAIndex::kU_Index ||
+ component == SkYUVAIndex::kV_Index) {
+ aligned_width >>= chroma_shift_x_;
+ }
+ // When the stride is a multiple of 1024, dav1d_default_picture_alloc()
+ // slightly pads the stride to avoid a reduction in cache hit rate in most
+ // L1/L2 cache implementations. Match that trick here. (Note that this padding
+ // is not documented in dav1d/picture.h.)
+ if ((aligned_width & 1023) == 0)
+ aligned_width += 64;
+ return aligned_width;
+}
+
+SkYUVColorSpace AVIFImageDecoder::GetYUVColorSpace() const {
+ DCHECK(CanDecodeToYUV());
+ DCHECK(yuv_color_space_);
+ return *yuv_color_space_;
+}
+
+void AVIFImageDecoder::DecodeToYUV() {
+ DCHECK(image_planes_);
+ DCHECK(CanDecodeToYUV());
+ DCHECK(IsAllDataReceived());
+
+ if (Failed())
+ return;
+
+ DCHECK(decoder_);
+ DCHECK_EQ(decoded_frame_count_, 1u); // Not animation.
+
+ // libavif cannot decode to an external buffer. So we need to copy from
+ // libavif's internal buffer to |image_planes_|.
+ // TODO(wtc): Enhance libavif to decode to an external buffer.
+ if (!DecodeImage(0)) {
+ SetFailed();
+ return;
+ }
+
+ const auto* image = decoder_->image;
+ // All frames must be the same size.
+ if (Size() != IntSize(image->width, image->height)) {
+ DVLOG(1) << "All frames must be the same size";
+ SetFailed();
+ return;
+ }
+ DCHECK_EQ(image->depth, 8u);
+ DCHECK(!image->alphaPlane);
+ static_assert(SkYUVAIndex::kY_Index == static_cast<int>(AVIF_CHAN_Y), "");
+ static_assert(SkYUVAIndex::kU_Index == static_cast<int>(AVIF_CHAN_U), "");
+ static_assert(SkYUVAIndex::kV_Index == static_cast<int>(AVIF_CHAN_V), "");
+ // Initialize |width| and |height| to the width and height of the luma plane.
+ uint32_t width = image->width;
+ uint32_t height = image->height;
+ // |height| comes from the AV1 sequence header or frame header, which encodes
+ // max_frame_height_minus_1 and frame_height_minus_1, respectively, as n-bit
+ // unsigned integers for some n.
+ DCHECK_GT(height, 0u);
+ for (int plane = 0; plane < 3; ++plane) {
+ const uint8_t* src = image->yuvPlanes[plane];
+ size_t src_row_bytes = base::strict_cast<size_t>(image->yuvRowBytes[plane]);
+ uint8_t* dst = static_cast<uint8_t*>(image_planes_->Plane(plane));
+ size_t dst_row_bytes = image_planes_->RowBytes(plane);
+ DCHECK_LE(width, src_row_bytes);
+ DCHECK_LE(width, dst_row_bytes);
+ if (src_row_bytes == dst_row_bytes) {
+ // If |src| and |dst| have the same stride, we can copy the plane with a
+ // single memcpy() call. For the last row we copy only |width| bytes to
+ // avoid reading past the end of the last row. For all other rows we copy
+ // |src_row_bytes| bytes.
+ memcpy(dst, src, (height - 1) * src_row_bytes + width);
+ } else {
+ for (uint32_t j = 0; j < height; ++j) {
+ memcpy(dst, src, width);
+ src += src_row_bytes;
+ dst += dst_row_bytes;
+ }
+ }
+ if (plane == 0) {
+ // Having processed the luma plane, change |width| and |height| to the
+ // width and height of the chroma planes.
+ width = UVSize(width, chroma_shift_x_);
+ height = UVSize(height, chroma_shift_y_);
+ }
+ }
+}
+
int AVIFImageDecoder::RepetitionCount() const {
return decoded_frame_count_ > 1 ? kAnimationLoopInfinite : kAnimationNone;
}
@@ -243,11 +442,13 @@ void AVIFImageDecoder::DecodeSize() {
}
size_t AVIFImageDecoder::DecodeFrameCount() {
- return decoded_frame_count_;
+ return Failed() ? frame_buffer_cache_.size() : decoded_frame_count_;
}
void AVIFImageDecoder::InitializeNewFrame(size_t index) {
auto& buffer = frame_buffer_cache_[index];
+ if (decode_to_half_float_)
+ buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
buffer.SetOriginalFrameRect(IntRect(IntPoint(), Size()));
@@ -263,21 +464,20 @@ void AVIFImageDecoder::InitializeNewFrame(size_t index) {
buffer.SetDisposalMethod(ImageFrame::kDisposeNotSpecified);
buffer.SetAlphaBlendSource(ImageFrame::kBlendAtopBgcolor);
- if (decode_to_half_float_)
- buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
-
// Leave all frames as being independent (the default) because we require all
// frames be the same size.
DCHECK_EQ(buffer.RequiredPreviousFrameIndex(), kNotFound);
}
void AVIFImageDecoder::Decode(size_t index) {
- // TODO(dalecurtis): For fragmented avif-sequence files we probably want to
- // allow partial decoding. Depends on if we see frequent use of multi-track
+ // TODO(dalecurtis): For fragmented AVIF image sequence files we probably want
+ // to allow partial decoding. Depends on if we see frequent use of multi-track
// images where there's lots to ignore.
if (Failed() || !IsAllDataReceived())
return;
+ UpdateAggressivePurging(index);
+
if (!DecodeImage(index)) {
SetFailed();
return;
@@ -286,37 +486,33 @@ void AVIFImageDecoder::Decode(size_t index) {
const auto* image = decoder_->image;
// All frames must be the same size.
if (Size() != IntSize(image->width, image->height)) {
+ DVLOG(1) << "All frames must be the same size";
+ SetFailed();
+ return;
+ }
+ // Frame bit depth must be equal to container bit depth.
+ if (image->depth != bit_depth_) {
+ DVLOG(1) << "Frame bit depth must be equal to container bit depth";
SetFailed();
return;
}
ImageFrame& buffer = frame_buffer_cache_[index];
- DCHECK_NE(buffer.GetStatus(), ImageFrame::kFrameComplete);
- if (decode_to_half_float_)
- buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
-
- // Set color space information on the frame if appropriate.
- gfx::ColorSpace frame_cs;
- if (!IgnoresColorSpace())
- frame_cs = GetColorSpace(image);
- if (CanSetColorSpace()) {
- last_color_space_ = frame_cs.GetAsFullRangeRGB();
- } else {
- // Just use whatever color space Skia wants us to use.
- }
+ DCHECK_EQ(buffer.GetStatus(), ImageFrame::kFrameEmpty);
- // TODO(wtc): This should use the value of |last_color_space_|. Implement it.
if (!InitFrameBuffer(index)) {
DVLOG(1) << "Failed to create frame buffer...";
SetFailed();
return;
}
- if (!RenderImage(image, frame_cs, &buffer)) {
+ if (!RenderImage(image, &buffer)) {
SetFailed();
return;
}
+ ColorCorrectImage(&buffer);
+
buffer.SetPixelsChanged(true);
buffer.SetHasAlpha(!!image->alphaPlane);
buffer.SetStatus(ImageFrame::kFrameComplete);
@@ -340,8 +536,12 @@ bool AVIFImageDecoder::MaybeCreateDemuxer() {
if (!decoder_)
return false;
+ // TODO(crbug.com/1114916): Disable grid image support in libavif until the
+ // libavif grid image code has been audited.
+ decoder_->disableGridImages = AVIF_TRUE;
+
// TODO(dalecurtis): This may create a second copy of the media data in
- // memory, which is not great. Upstream should provide a read() based API:
+ // memory, which is not great. libavif should provide a read() based API:
// https://github.com/AOMediaCodec/libavif/issues/11
image_data_ = data_->GetAsSkData();
if (!image_data_)
@@ -354,91 +554,146 @@ bool AVIFImageDecoder::MaybeCreateDemuxer() {
return false;
}
+ // Image metadata is available in decoder_->image after avifDecoderParse()
+ // even though decoder_->imageIndex is invalid (-1).
+ DCHECK_EQ(decoder_->imageIndex, -1);
+ // This variable is named |container| to emphasize the fact that the current
+ // contents of decoder_->image come from the container, not any frame.
+ const auto* container = decoder_->image;
+
+ // The container width and container height are read from either the tkhd
+ // (track header) box of a track or the ispe (image spatial extents) property
+ // of an image item, both of which are mandatory in the spec.
+ if (container->width == 0 || container->height == 0) {
+ DVLOG(1) << "Container width and height must be present";
+ return false;
+ }
+
+ // The container depth is read from either the av1C box of a track or the av1C
+ // property of an image item, both of which are mandatory in the spec.
+ if (container->depth == 0) {
+ DVLOG(1) << "Container depth must be present";
+ return false;
+ }
+
DCHECK_GT(decoder_->imageCount, 0);
decoded_frame_count_ = decoder_->imageCount;
- is_high_bit_depth_ = decoder_->containerDepth > 8;
+ bit_depth_ = container->depth;
decode_to_half_float_ =
- is_high_bit_depth_ &&
+ ImageIsHighBitDepth() &&
high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat;
- // Try to get the size from the container if possible instead of decoding.
- if (decoder_->containerWidth && decoder_->containerHeight)
- return SetSize(decoder_->containerWidth, decoder_->containerHeight);
+ const avifPixelFormat yuv_format = container->yuvFormat;
+ avifPixelFormatInfo format_info;
+ avifGetPixelFormatInfo(yuv_format, &format_info);
+ chroma_shift_x_ = format_info.chromaShiftX;
+ chroma_shift_y_ = format_info.chromaShiftY;
+
+ // SetEmbeddedColorProfile() must be called before IsSizeAvailable() becomes
+ // true. So call SetEmbeddedColorProfile() before calling SetSize(). The color
+ // profile is either an ICC profile or the CICP color description.
+
+ if (!IgnoresColorSpace()) {
+ // The CICP color description is always present because we can always get it
+ // from the AV1 sequence header for the frames. If an ICC profile is
+ // present, use it instead of the CICP color description.
+ if (container->icc.size) {
+ std::unique_ptr<ColorProfile> profile =
+ ColorProfile::Create(container->icc.data, container->icc.size);
+ if (!profile) {
+ DVLOG(1) << "Failed to parse image ICC profile";
+ return false;
+ }
+ uint32_t data_color_space = profile->GetProfile()->data_color_space;
+ const bool is_mono = container->yuvFormat == AVIF_PIXEL_FORMAT_YUV400;
+ if (is_mono) {
+ if (data_color_space != skcms_Signature_Gray &&
+ data_color_space != skcms_Signature_RGB)
+ profile = nullptr;
+ } else {
+ if (data_color_space != skcms_Signature_RGB)
+ profile = nullptr;
+ }
+ if (!profile) {
+ DVLOG(1)
+ << "Image contains ICC profile that does not match its color space";
+ return false;
+ }
+ SetEmbeddedColorProfile(std::move(profile));
+ } else if (container->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED ||
+ container->transferCharacteristics !=
+ AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) {
+ gfx::ColorSpace frame_cs = GetColorSpace(container);
+ sk_sp<SkColorSpace> sk_color_space =
+ frame_cs.GetAsFullRangeRGB().ToSkColorSpace();
+ skcms_ICCProfile profile;
+ sk_color_space->toProfile(&profile);
+ SetEmbeddedColorProfile(std::make_unique<ColorProfile>(profile));
+ }
+ }
- // We need to SetSize() to proceed, so decode the first frame.
- return DecodeImage(0) &&
- SetSize(decoder_->image->width, decoder_->image->height);
+ // Determine whether the image can be decoded to YUV.
+ // * Bit depths higher than 8 are not supported.
+ // * TODO(crbug.com/915972): Only YUV 4:2:0 subsampling format is supported.
+ // * Alpha channel is not supported.
+ // * Multi-frame images (animations) are not supported. (The DecodeToYUV()
+ // method does not have an 'index' parameter.)
+ // * If ColorTransform() returns a non-null pointer, the decoder has to do a
+ // color space conversion, so we don't decode to YUV.
+ allow_decode_to_yuv_ =
+ !ImageIsHighBitDepth() && yuv_format == AVIF_PIXEL_FORMAT_YUV420 &&
+ !decoder_->alphaPresent && decoded_frame_count_ == 1 &&
+ (yuv_color_space_ = GetSkYUVColorSpace(container)) && !ColorTransform();
+
+ return SetSize(container->width, container->height);
}
bool AVIFImageDecoder::DecodeImage(size_t index) {
- auto ret = avifDecoderNthImage(decoder_.get(), index);
- if (ret != AVIF_RESULT_OK) {
- // We shouldn't be called more times than specified in
- // DecodeFrameCount(); possibly this should truncate if the initial
- // count is wrong?
- DCHECK_NE(ret, AVIF_RESULT_NO_IMAGES_REMAINING);
- return false;
- }
-
- const auto* image = decoder_->image;
- is_high_bit_depth_ = image->depth > 8;
- decode_to_half_float_ =
- is_high_bit_depth_ &&
- high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat;
- return true;
+ const auto ret = avifDecoderNthImage(decoder_.get(), index);
+ // |index| should be less than what DecodeFrameCount() returns, so we should
+ // not get the AVIF_RESULT_NO_IMAGES_REMAINING error.
+ DCHECK_NE(ret, AVIF_RESULT_NO_IMAGES_REMAINING);
+ return ret == AVIF_RESULT_OK;
}
-bool AVIFImageDecoder::UpdateColorTransform(const gfx::ColorSpace& src_cs,
- const gfx::ColorSpace& dest_cs) {
- if (color_transform_ && color_transform_->GetSrcColorSpace() == src_cs)
- return true;
- color_transform_ = gfx::ColorTransform::NewColorTransform(
- src_cs, dest_cs, gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
- return !!color_transform_;
-}
+void AVIFImageDecoder::UpdateColorTransform(const gfx::ColorSpace& frame_cs,
+ int bit_depth) {
+ if (color_transform_ && color_transform_->GetSrcColorSpace() == frame_cs)
+ return;
-// TODO(wtc): We must be able to set the color space accurately. Find a solution
-// that lets us set the color space for all images and not just the half float
-// and animated cases.
-bool AVIFImageDecoder::CanSetColorSpace() const {
- return decode_to_half_float_ || decoded_frame_count_ > 1;
+ // For YUV-to-RGB color conversion we can pass an invalid dst color space to
+ // skip the code for full color conversion.
+ color_transform_ = gfx::ColorTransform::NewColorTransform(
+ frame_cs, bit_depth, gfx::ColorSpace(), bit_depth,
+ gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
}
-bool AVIFImageDecoder::RenderImage(const avifImage* image,
- const gfx::ColorSpace& frame_cs,
- ImageFrame* buffer) {
- const gfx::ColorSpace dest_rgb_cs(*buffer->Bitmap().colorSpace());
+bool AVIFImageDecoder::RenderImage(const avifImage* image, ImageFrame* buffer) {
+ const gfx::ColorSpace frame_cs = GetColorSpace(image);
+ const bool is_mono = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400;
+ const bool premultiply_alpha = buffer->PremultiplyAlpha();
- const bool is_mono = !image->yuvPlanes[AVIF_CHAN_U];
-
- // TODO(dalecurtis): We should decode to YUV when possible. Currently the
- // YUV path seems to only support still-image YUV8.
if (decode_to_half_float_) {
- if (!UpdateColorTransform(frame_cs, dest_rgb_cs)) {
- DVLOG(1) << "Failed to update color transform...";
- return false;
- }
+ UpdateColorTransform(frame_cs, image->depth);
uint64_t* rgba_hhhh = buffer->GetAddrF16(0, 0);
// Color and format convert from YUV HBD -> RGBA half float.
if (is_mono) {
YUVAToRGBA<ColorType::kMono, uint16_t>(image, color_transform_.get(),
- rgba_hhhh);
+ premultiply_alpha, rgba_hhhh);
} else {
// TODO: Add fast path for 10bit 4:2:0 using libyuv.
YUVAToRGBA<ColorType::kColor, uint16_t>(image, color_transform_.get(),
- rgba_hhhh);
+ premultiply_alpha, rgba_hhhh);
}
return true;
}
uint32_t* rgba_8888 = buffer->GetAddr(0, 0);
- // TODO(wtc): Figure out a way to check frame_cs == ~BT.2020 too since
- // ConvertVideoFrameToRGBPixels() can handle that too.
- if (frame_cs == gfx::ColorSpace::CreateREC709() ||
- frame_cs == gfx::ColorSpace::CreateREC601() ||
- frame_cs == gfx::ColorSpace::CreateJpeg()) {
+ // Call media::PaintCanvasVideoRenderer (PCVR) if the color space is
+ // supported.
+ if (IsColorSpaceSupportedByPCVR(image)) {
// Create temporary frame wrapping the YUVA planes.
scoped_refptr<media::VideoFrame> frame;
auto pixel_format = AvifToVideoPixelFormat(image->yuvFormat, image->depth);
@@ -446,14 +701,8 @@ bool AVIFImageDecoder::RenderImage(const avifImage* image,
return false;
auto size = gfx::Size(image->width, image->height);
if (image->alphaPlane) {
- if (pixel_format == media::PIXEL_FORMAT_I420 && image->yuvPlanes[1] &&
- image->yuvPlanes[2]) {
- // Genuine YUV 4:2:0, not monochrome 4:0:0.
- pixel_format = media::PIXEL_FORMAT_I420A;
- } else {
- NOTIMPLEMENTED();
- return false;
- }
+ DCHECK_EQ(pixel_format, media::PIXEL_FORMAT_I420);
+ pixel_format = media::PIXEL_FORMAT_I420A;
frame = media::VideoFrame::WrapExternalYuvaData(
pixel_format, size, gfx::Rect(size), size, image->yuvRowBytes[0],
image->yuvRowBytes[1], image->yuvRowBytes[2], image->alphaRowBytes,
@@ -476,32 +725,59 @@ bool AVIFImageDecoder::RenderImage(const avifImage* image,
//
// https://bugs.chromium.org/p/libyuv/issues/detail?id=845
media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
- frame.get(), rgba_8888, frame->visible_rect().width() * 4);
+ frame.get(), rgba_8888, frame->visible_rect().width() * 4,
+ premultiply_alpha);
return true;
}
- if (!UpdateColorTransform(frame_cs, dest_rgb_cs)) {
- DVLOG(1) << "Failed to update color transform...";
- return false;
- }
+ UpdateColorTransform(frame_cs, image->depth);
if (ImageIsHighBitDepth()) {
if (is_mono) {
YUVAToRGBA<ColorType::kMono, uint16_t>(image, color_transform_.get(),
- rgba_8888);
+ premultiply_alpha, rgba_8888);
} else {
YUVAToRGBA<ColorType::kColor, uint16_t>(image, color_transform_.get(),
- rgba_8888);
+ premultiply_alpha, rgba_8888);
}
} else {
if (is_mono) {
YUVAToRGBA<ColorType::kMono, uint8_t>(image, color_transform_.get(),
- rgba_8888);
+ premultiply_alpha, rgba_8888);
} else {
YUVAToRGBA<ColorType::kColor, uint8_t>(image, color_transform_.get(),
- rgba_8888);
+ premultiply_alpha, rgba_8888);
}
}
return true;
}
+void AVIFImageDecoder::ColorCorrectImage(ImageFrame* buffer) {
+ // Postprocess the image data according to the profile.
+ const ColorProfileTransform* const transform = ColorTransform();
+ if (!transform)
+ return;
+ const auto alpha_format = (buffer->HasAlpha() && buffer->PremultiplyAlpha())
+ ? skcms_AlphaFormat_PremulAsEncoded
+ : skcms_AlphaFormat_Unpremul;
+ if (decode_to_half_float_) {
+ const skcms_PixelFormat color_format = skcms_PixelFormat_RGBA_hhhh;
+ for (int y = 0; y < Size().Height(); ++y) {
+ ImageFrame::PixelDataF16* const row = buffer->GetAddrF16(0, y);
+ const bool success = skcms_Transform(
+ row, color_format, alpha_format, transform->SrcProfile(), row,
+ color_format, alpha_format, transform->DstProfile(), Size().Width());
+ DCHECK(success);
+ }
+ } else {
+ const skcms_PixelFormat color_format = XformColorFormat();
+ for (int y = 0; y < Size().Height(); ++y) {
+ ImageFrame::PixelData* const row = buffer->GetAddr(0, y);
+ const bool success = skcms_Transform(
+ row, color_format, alpha_format, transform->SrcProfile(), row,
+ color_format, alpha_format, transform->DstProfile(), Size().Width());
+ DCHECK(success);
+ }
+ }
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
index 44ea40594f8..105b093db57 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/optional.h"
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkData.h"
@@ -34,6 +35,10 @@ class PLATFORM_EXPORT AVIFImageDecoder final : public ImageDecoder {
String FilenameExtension() const override { return "avif"; }
bool ImageIsHighBitDepth() override;
void OnSetData(SegmentReader* data) override;
+ IntSize DecodedYUVSize(int component) const override;
+ size_t DecodedYUVWidthBytes(int component) const override;
+ SkYUVColorSpace GetYUVColorSpace() const override;
+ void DecodeToYUV() override;
int RepetitionCount() const override;
base::TimeDelta FrameDurationAtIndex(size_t) const override;
@@ -53,34 +58,32 @@ class PLATFORM_EXPORT AVIFImageDecoder final : public ImageDecoder {
bool MaybeCreateDemuxer();
// Decodes the frame at index |index|. The decoded frame is available in
- // decoder_->image. Returns true on success, false on failure.
+ // decoder_->image. Returns whether decoding completed successfully.
bool DecodeImage(size_t index);
- // Updates or creates |color_transform_|. Returns true on success, false on
- // failure.
- bool UpdateColorTransform(const gfx::ColorSpace& src_cs,
- const gfx::ColorSpace& dest_cs);
+ // Updates or creates |color_transform_| for YUV-to-RGB conversion.
+ void UpdateColorTransform(const gfx::ColorSpace& frame_cs, int bit_depth);
- // Returns true if we can set the color space on the image.
- bool CanSetColorSpace() const;
+ // Renders |image| in |buffer|. Returns whether |image| was rendered
+ // successfully.
+ bool RenderImage(const avifImage* image, ImageFrame* buffer);
- // Renders |image| in |buffer|. |frame_cs| is the color space of |image|.
- // Returns true on success, false on failure.
- bool RenderImage(const avifImage* image,
- const gfx::ColorSpace& frame_cs,
- ImageFrame* buffer);
+ // Applies color profile correction to the pixel data for |buffer|, if
+ // desired.
+ void ColorCorrectImage(ImageFrame* buffer);
- bool is_high_bit_depth_ = false;
+ uint8_t bit_depth_ = 0;
bool decode_to_half_float_ = false;
+ uint8_t chroma_shift_x_ = 0;
+ uint8_t chroma_shift_y_ = 0;
size_t decoded_frame_count_ = 0;
+ base::Optional<SkYUVColorSpace> yuv_color_space_;
std::unique_ptr<avifDecoder, void (*)(avifDecoder*)> decoder_{nullptr,
nullptr};
std::unique_ptr<gfx::ColorTransform> color_transform_;
- gfx::ColorSpace last_color_space_;
sk_sp<SkData> image_data_;
- SkBitmap temp_bitmap_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
index 9a8f00e5bc2..39ee7b16e74 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
@@ -6,6 +6,7 @@
#include <cmath>
#include <memory>
+#include <ostream>
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
@@ -13,13 +14,9 @@
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
#include "third_party/libavif/src/include/avif/avif.h"
-#define FIXME_SUPPORT_10BIT_IMAGE_WITH_ALPHA 0
-#define FIXME_SUPPORT_12BIT_IMAGE_WITH_ALPHA 0
-#define FIXME_CRASH_IF_COLOR_TRANSFORMATION_IS_ENABLED 0
#define FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM 0
#define FIXME_SUPPORT_ICC_PROFILE_TRANSFORM 0
#define FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS 0
-#define FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE 0
namespace blink {
@@ -63,6 +60,42 @@ struct StaticColorCheckParam {
std::vector<ExpectedColor> colors;
};
+std::ostream& operator<<(std::ostream& os, const StaticColorCheckParam& param) {
+ const char* color_type;
+ switch (param.color_type) {
+ case ColorType::kRgb:
+ color_type = "kRgb";
+ break;
+ case ColorType::kRgbA:
+ color_type = "kRgbA";
+ break;
+ case ColorType::kMono:
+ color_type = "kMono";
+ break;
+ case ColorType::kMonoA:
+ color_type = "kMonoA";
+ break;
+ }
+ const char* alpha_option =
+ (param.alpha_option == ImageDecoder::kAlphaPremultiplied
+ ? "kAlphaPremultiplied"
+ : "kAlphaNotPremultiplied");
+ const char* color_behavior;
+ if (param.color_behavior.IsIgnore()) {
+ color_behavior = "Ignore";
+ } else if (param.color_behavior.IsTag()) {
+ color_behavior = "Tag";
+ } else {
+ DCHECK(param.color_behavior.IsTransformToSRGB());
+ color_behavior = "TransformToSRGB";
+ }
+ return os << "\nStaticColorCheckParam {\n path: \"" << param.path
+ << "\",\n bit_depth: " << param.bit_depth
+ << ",\n color_type: " << color_type
+ << ",\n alpha_option: " << alpha_option
+ << ",\n color_behavior: " << color_behavior << "\n}";
+}
+
StaticColorCheckParam kTestParams[] = {
{
"/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif",
@@ -74,7 +107,6 @@ StaticColorCheckParam kTestParams[] = {
0,
{}, // we just check that this image is lossy.
},
-#if FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE
{
"/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif",
8,
@@ -86,7 +118,6 @@ StaticColorCheckParam kTestParams[] = {
{}, // we just check that the decoder won't crash when
// ColorBehavior::Ignore() is used.
},
-#endif
{"/images/resources/avif/red-with-alpha-8bpc.avif",
8,
ColorType::kRgbA,
@@ -117,7 +148,7 @@ StaticColorCheckParam kTestParams[] = {
ImageDecoder::kLosslessFormat,
ImageDecoder::kAlphaNotPremultiplied,
ColorBehavior::Tag(),
- 0,
+ 3,
{
{gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
@@ -147,7 +178,6 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}},
-#if FIXME_CRASH_IF_COLOR_TRANSFORMATION_IS_ENABLED
{"/images/resources/avif/red-with-alpha-8bpc.avif",
8,
ColorType::kRgbA,
@@ -157,14 +187,10 @@ StaticColorCheckParam kTestParams[] = {
1,
{
{gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)},
- // If the color space is sRGB, pre-multiplied red should be 187.84.
- // http://www.color.org/sRGB.pdf
- {gfx::Point(1, 1), SkColorSetARGB(128, 188, 0, 0)},
+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
-#endif
-#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM && \
- FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE
+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM
{"/images/resources/avif/red-with-profile-8bpc.avif",
8,
ColorType::kRgb,
@@ -196,7 +222,6 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
#endif
-#if FIXME_SUPPORT_10BIT_IMAGE_WITH_ALPHA
{"/images/resources/avif/red-with-alpha-10bpc.avif",
10,
ColorType::kRgbA,
@@ -221,7 +246,6 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
-#if FIXME_CRASH_IF_COLOR_TRANSFORMATION_IS_ENABLED
{"/images/resources/avif/red-with-alpha-10bpc.avif",
10,
ColorType::kRgbA,
@@ -231,20 +255,16 @@ StaticColorCheckParam kTestParams[] = {
1,
{
{gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)},
- // If the color space is sRGB, pre-multiplied red should be 187.84.
- // http://www.color.org/sRGB.pdf
- {gfx::Point(1, 1), SkColorSetARGB(128, 188, 0, 0)},
+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
-#endif
-#endif
{"/images/resources/avif/red-full-ranged-10bpc.avif",
10,
ColorType::kRgb,
ImageDecoder::kLosslessFormat,
ImageDecoder::kAlphaNotPremultiplied,
ColorBehavior::Tag(),
- 0,
+ 2,
{
{gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
@@ -274,8 +294,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}},
-#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM && \
- FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE
+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM
{"/images/resources/avif/red-with-profile-10bpc.avif",
10,
ColorType::kRgb,
@@ -307,7 +326,6 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
#endif
-#if FIXME_SUPPORT_12BIT_IMAGE_WITH_ALPHA
{"/images/resources/avif/red-with-alpha-12bpc.avif",
12,
ColorType::kRgbA,
@@ -332,7 +350,6 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
-#if FIXME_CRASH_IF_COLOR_TRANSFORMATION_IS_ENABLED
{"/images/resources/avif/red-with-alpha-12bpc.avif",
12,
ColorType::kRgbA,
@@ -342,20 +359,16 @@ StaticColorCheckParam kTestParams[] = {
1,
{
{gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)},
- // If the color space is sRGB, pre-multiplied red should be 187.84.
- // http://www.color.org/sRGB.pdf
- {gfx::Point(1, 1), SkColorSetARGB(128, 188, 0, 0)},
+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}},
-#endif
-#endif
{"/images/resources/avif/red-full-ranged-12bpc.avif",
12,
ColorType::kRgb,
ImageDecoder::kLosslessFormat,
ImageDecoder::kAlphaNotPremultiplied,
ColorBehavior::Tag(),
- 0,
+ 2,
{
{gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
@@ -385,14 +398,13 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}},
-#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM && \
- FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE
+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM
{"/images/resources/avif/red-with-profile-12bpc.avif",
12,
ColorType::kRgb,
ImageDecoder::kLosslessFormat,
ImageDecoder::kAlphaNotPremultiplied,
- ColorBehavior::Tag(),
+ ColorBehavior::Ignore(),
1,
{
{gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)},
@@ -451,8 +463,60 @@ void TestInvalidStaticImage(const char* avif_file, ErrorPhase error_phase) {
ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
ASSERT_TRUE(frame);
EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus());
+ EXPECT_TRUE(decoder->Failed());
}
- EXPECT_TRUE(decoder->Failed());
+}
+
+void ReadYUV(int* output_y_width,
+ int* output_y_height,
+ int* output_uv_width,
+ int* output_uv_height,
+ const char* image_file_path) {
+ scoped_refptr<SharedBuffer> data = ReadFile(image_file_path);
+ ASSERT_TRUE(data);
+
+ auto decoder = std::make_unique<AVIFImageDecoder>(
+ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth,
+ ColorBehavior::Tag(), ImageDecoder::kNoDecodedImageByteLimit);
+ decoder->SetData(data.get(), true);
+ ASSERT_TRUE(decoder->CanDecodeToYUV());
+
+ ASSERT_TRUE(decoder->IsSizeAvailable());
+
+ IntSize size = decoder->DecodedSize();
+ IntSize y_size = decoder->DecodedYUVSize(0);
+ IntSize u_size = decoder->DecodedYUVSize(1);
+ IntSize v_size = decoder->DecodedYUVSize(2);
+
+ EXPECT_EQ(size.Width(), y_size.Width());
+ EXPECT_EQ(size.Height(), y_size.Height());
+ EXPECT_EQ(u_size.Width(), v_size.Width());
+ EXPECT_EQ(u_size.Height(), v_size.Height());
+
+ *output_y_width = y_size.Width();
+ *output_y_height = y_size.Height();
+ *output_uv_width = u_size.Width();
+ *output_uv_height = u_size.Height();
+
+ size_t row_bytes[3];
+ row_bytes[0] = decoder->DecodedYUVWidthBytes(0);
+ row_bytes[1] = decoder->DecodedYUVWidthBytes(1);
+ row_bytes[2] = decoder->DecodedYUVWidthBytes(2);
+
+ size_t planes_data_size = row_bytes[0] * y_size.Height() +
+ row_bytes[1] * u_size.Height() +
+ row_bytes[2] * v_size.Height();
+ auto planes_data = std::make_unique<char[]>(planes_data_size);
+
+ void* planes[3];
+ planes[0] = planes_data.get();
+ planes[1] = static_cast<char*>(planes[0]) + row_bytes[0] * y_size.Height();
+ planes[2] = static_cast<char*>(planes[1]) + row_bytes[1] * u_size.Height();
+
+ decoder->SetImagePlanes(std::make_unique<ImagePlanes>(planes, row_bytes));
+
+ decoder->DecodeToYUV();
+ EXPECT_FALSE(decoder->Failed());
}
} // namespace
@@ -467,19 +531,15 @@ TEST(AnimatedAVIFTests, ValidImages) {
TestByteByByteDecode(&CreateAVIFDecoder,
"/images/resources/avif/star-10bpc.avifs", 5u,
kAnimationLoopInfinite);
-#if FIXME_SUPPORT_10BIT_IMAGE_WITH_ALPHA
TestByteByByteDecode(&CreateAVIFDecoder,
"/images/resources/avif/star-10bpc-with-alpha.avifs", 5u,
kAnimationLoopInfinite);
-#endif
TestByteByByteDecode(&CreateAVIFDecoder,
"/images/resources/avif/star-12bpc.avifs", 5u,
kAnimationLoopInfinite);
-#if FIXME_SUPPORT_12BIT_IMAGE_WITH_ALPHA
TestByteByByteDecode(&CreateAVIFDecoder,
"/images/resources/avif/star-12bpc-with-alpha.avifs", 5u,
kAnimationLoopInfinite);
-#endif
// TODO(ryoh): Add avifs with EditListBox.
}
@@ -517,6 +577,18 @@ TEST(StaticAVIFTests, ValidImages) {
1, kAnimationNone);
}
+TEST(StaticAVIFTests, YUV) {
+ const char* avif_file =
+ "/images/resources/avif/red-full-ranged-8bpc.avif"; // 3x3, YUV 4:2:0
+ int output_y_width, output_y_height, output_uv_width, output_uv_height;
+ ReadYUV(&output_y_width, &output_y_height, &output_uv_width,
+ &output_uv_height, avif_file);
+ EXPECT_EQ(3, output_y_width);
+ EXPECT_EQ(3, output_y_height);
+ EXPECT_EQ(2, output_uv_width);
+ EXPECT_EQ(2, output_uv_height);
+}
+
using StaticAVIFColorTests = ::testing::TestWithParam<StaticColorCheckParam>;
INSTANTIATE_TEST_CASE_P(Parameterized,
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
index 76c56ed5eb2..51dd49b3323 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
@@ -23,6 +23,8 @@
#include <memory>
#include "base/numerics/safe_conversions.h"
+#include "base/sys_byteorder.h"
+#include "build/build_config.h"
#include "media/media_buildflags.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h"
@@ -66,9 +68,22 @@ cc::ImageType FileExtensionToImageType(String image_extension) {
return cc::ImageType::kInvalid;
}
-} // namespace
+size_t CalculateMaxDecodedBytes(
+ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option,
+ const SkISize& desired_size) {
+ const size_t max_decoded_bytes =
+ Platform::Current() ? Platform::Current()->MaxDecodedImageBytes()
+ : ImageDecoder::kNoDecodedImageByteLimit;
+ if (desired_size.isEmpty())
+ return max_decoded_bytes;
-const size_t ImageDecoder::kNoDecodedImageByteLimit;
+ const size_t num_pixels = desired_size.width() * desired_size.height();
+ if (high_bit_depth_decoding_option == ImageDecoder::kDefaultBitDepth)
+ return std::min(4 * num_pixels, max_decoded_bytes);
+
+ // ImageDecoder::kHighBitDepthToHalfFloat
+ return std::min(8 * num_pixels, max_decoded_bytes);
+}
inline bool MatchesJPEGSignature(const char* contents) {
return !memcmp(contents, "\xFF\xD8\xFF", 3);
@@ -98,9 +113,46 @@ inline bool MatchesBMPSignature(const char* contents) {
return !memcmp(contents, "BM", 2) || !memcmp(contents, "BA", 2);
}
-static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
-static const size_t k4BytesPerPixel = 4;
-static const size_t k8BytesPerPixel = 8;
+constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
+
+// static
+String SniffMimeTypeInternal(scoped_refptr<SegmentReader> reader) {
+ // At least kLongestSignatureLength bytes are needed to sniff the signature.
+ if (reader->size() < kLongestSignatureLength)
+ return String();
+
+ // Access the first kLongestSignatureLength chars to sniff the signature.
+ // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
+ char buffer[kLongestSignatureLength];
+ const FastSharedBufferReader fast_reader(reader);
+ const char* contents =
+ fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
+
+ if (MatchesJPEGSignature(contents))
+ return "image/jpeg";
+ if (MatchesPNGSignature(contents))
+ return "image/png";
+ if (MatchesGIFSignature(contents))
+ return "image/gif";
+ if (MatchesWebPSignature(contents))
+ return "image/webp";
+ if (MatchesICOSignature(contents) || MatchesCURSignature(contents))
+ return "image/x-icon";
+ if (MatchesBMPSignature(contents))
+ return "image/bmp";
+#if BUILDFLAG(ENABLE_AV1_DECODER)
+ if (base::FeatureList::IsEnabled(features::kAVIF) &&
+ AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) {
+ return "image/avif";
+ }
+#endif
+
+ return String();
+}
+
+} // namespace
+
+const size_t ImageDecoder::kNoDecodedImageByteLimit;
std::unique_ptr<ImageDecoder> ImageDecoder::Create(
scoped_refptr<SegmentReader> data,
@@ -110,59 +162,61 @@ std::unique_ptr<ImageDecoder> ImageDecoder::Create(
const ColorBehavior& color_behavior,
const OverrideAllowDecodeToYuv allow_decode_to_yuv,
const SkISize& desired_size) {
- // At least kLongestSignatureLength bytes are needed to sniff the signature.
- if (data->size() < kLongestSignatureLength)
+ auto type = SniffMimeTypeInternal(data);
+ if (type.IsEmpty())
return nullptr;
+
// On low end devices, always decode to 8888.
if (high_bit_depth_decoding_option == kHighBitDepthToHalfFloat &&
Platform::Current() && Platform::Current()->IsLowEndDevice()) {
high_bit_depth_decoding_option = kDefaultBitDepth;
}
- size_t max_decoded_bytes = Platform::Current()
- ? Platform::Current()->MaxDecodedImageBytes()
- : kNoDecodedImageByteLimit;
- if (!desired_size.isEmpty()) {
- size_t num_pixels = desired_size.width() * desired_size.height();
- if (high_bit_depth_decoding_option == kDefaultBitDepth) {
- max_decoded_bytes =
- std::min(k4BytesPerPixel * num_pixels, max_decoded_bytes);
- } else { // kHighBitDepthToHalfFloat
- max_decoded_bytes =
- std::min(k8BytesPerPixel * num_pixels, max_decoded_bytes);
- }
- }
+ return CreateByMimeType(type, std::move(data), data_complete, alpha_option,
+ high_bit_depth_decoding_option, color_behavior,
+ allow_decode_to_yuv, desired_size);
+}
- // Access the first kLongestSignatureLength chars to sniff the signature.
- // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
- char buffer[kLongestSignatureLength];
- const FastSharedBufferReader fast_reader(data);
- const char* contents =
- fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
+std::unique_ptr<ImageDecoder> ImageDecoder::CreateByMimeType(
+ String mime_type,
+ scoped_refptr<SegmentReader> data,
+ bool data_complete,
+ AlphaOption alpha_option,
+ HighBitDepthDecodingOption high_bit_depth_decoding_option,
+ const ColorBehavior& color_behavior,
+ const OverrideAllowDecodeToYuv allow_decode_to_yuv,
+ const SkISize& desired_size) {
+ const size_t max_decoded_bytes =
+ CalculateMaxDecodedBytes(high_bit_depth_decoding_option, desired_size);
+ // Note: The mime types below should match those supported by
+ // MimeUtil::IsSupportedImageMimeType().
std::unique_ptr<ImageDecoder> decoder;
- if (MatchesJPEGSignature(contents)) {
+ if (mime_type == "image/jpeg" || mime_type == "image/pjpeg" ||
+ mime_type == "image/jpg") {
decoder = std::make_unique<JPEGImageDecoder>(
alpha_option, color_behavior, max_decoded_bytes, allow_decode_to_yuv);
- } else if (MatchesPNGSignature(contents)) {
+ } else if (mime_type == "image/png" || mime_type == "image/x-png" ||
+ mime_type == "image/apng") {
decoder = std::make_unique<PNGImageDecoder>(
alpha_option, high_bit_depth_decoding_option, color_behavior,
max_decoded_bytes);
- } else if (MatchesGIFSignature(contents)) {
+ } else if (mime_type == "image/gif") {
decoder = std::make_unique<GIFImageDecoder>(alpha_option, color_behavior,
max_decoded_bytes);
- } else if (MatchesWebPSignature(contents)) {
+ } else if (mime_type == "image/webp") {
decoder = std::make_unique<WEBPImageDecoder>(alpha_option, color_behavior,
max_decoded_bytes);
- } else if (MatchesICOSignature(contents) || MatchesCURSignature(contents)) {
+ } else if (mime_type == "image/x-icon" ||
+ mime_type == "image/vnd.microsoft.icon") {
decoder = std::make_unique<ICOImageDecoder>(alpha_option, color_behavior,
max_decoded_bytes);
- } else if (MatchesBMPSignature(contents)) {
+ } else if (mime_type == "image/bmp" || mime_type == "image/x-xbitmap") {
decoder = std::make_unique<BMPImageDecoder>(alpha_option, color_behavior,
max_decoded_bytes);
#if BUILDFLAG(ENABLE_AV1_DECODER)
} else if (base::FeatureList::IsEnabled(features::kAVIF) &&
- AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) {
+ mime_type == "image/avif") {
decoder = std::make_unique<AVIFImageDecoder>(
alpha_option, high_bit_depth_decoding_option, color_behavior,
max_decoded_bytes);
@@ -175,40 +229,38 @@ std::unique_ptr<ImageDecoder> ImageDecoder::Create(
return decoder;
}
-bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) {
- return data.size() >= kLongestSignatureLength;
-}
-
-// static
-String ImageDecoder::SniffImageType(scoped_refptr<SharedBuffer> image_data) {
- // Access the first kLongestSignatureLength chars to sniff the signature.
- // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
- char buffer[kLongestSignatureLength];
- const FastSharedBufferReader fast_reader(
- SegmentReader::CreateFromSharedBuffer(std::move(image_data)));
- const char* contents =
- fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
+bool ImageDecoder::HasSufficientDataToSniffMimeType(const SharedBuffer& data) {
+ // At least kLongestSignatureLength bytes are needed to sniff the signature.
+ if (data.size() < kLongestSignatureLength)
+ return false;
- if (MatchesJPEGSignature(contents))
- return "image/jpeg";
- if (MatchesPNGSignature(contents))
- return "image/png";
- if (MatchesGIFSignature(contents))
- return "image/gif";
- if (MatchesWebPSignature(contents))
- return "image/webp";
- if (MatchesICOSignature(contents) || MatchesCURSignature(contents))
- return "image/x-icon";
- if (MatchesBMPSignature(contents))
- return "image/bmp";
#if BUILDFLAG(ENABLE_AV1_DECODER)
- if (base::FeatureList::IsEnabled(features::kAVIF) &&
- AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) {
- // TODO(wtc): Sniff AVIF image sequences and return image/avif-sequence.
- return "image/avif";
+ if (base::FeatureList::IsEnabled(features::kAVIF)) {
+ // Check for an ISO BMFF File Type Box. Assume that 'largesize' is not used.
+ // The first eight bytes would be a big-endian 32-bit unsigned integer
+ // 'size' and a four-byte 'type'.
+ struct {
+ uint32_t size; // unsigned int(32) size;
+ char type[4]; // unsigned int(32) type = boxtype;
+ } box;
+ static_assert(sizeof(box) == 8, "");
+ static_assert(8 <= kLongestSignatureLength, "");
+ bool ok = data.GetBytes(&box, 8u);
+ DCHECK(ok);
+ if (memcmp(box.type, "ftyp", 4) == 0) {
+ // Returns whether we have received the File Type Box in its entirety.
+ box.size = base::NetToHost32(box.size);
+ return box.size <= data.size();
+ }
}
#endif
- return String();
+ return true;
+}
+
+// static
+String ImageDecoder::SniffMimeType(scoped_refptr<SharedBuffer> image_data) {
+ return SniffMimeTypeInternal(
+ SegmentReader::CreateFromSharedBuffer(std::move(image_data)));
}
// static
@@ -222,8 +274,8 @@ ImageDecoder::CompressionFormat ImageDecoder::GetCompressionFormat(
// (for example, due to a misconfigured web server), then it is possible that
// the wrong compression format will be returned. However, this case should be
// exceedingly rare.
- if (image_data && HasSufficientDataToSniffImageType(*image_data.get()))
- mime_type = SniffImageType(image_data);
+ if (image_data && HasSufficientDataToSniffMimeType(*image_data.get()))
+ mime_type = SniffMimeType(image_data);
if (!mime_type)
return kUndefinedFormat;
@@ -278,8 +330,7 @@ ImageDecoder::CompressionFormat ImageDecoder::GetCompressionFormat(
// TODO(wtc): Implement this. Figure out whether to return kUndefinedFormat or
// a new kAVIFAnimationFormat in the case of an animated AVIF image.
if (base::FeatureList::IsEnabled(features::kAVIF) &&
- (EqualIgnoringASCIICase(mime_type, "image/avif") ||
- EqualIgnoringASCIICase(mime_type, "image/avif-sequence"))) {
+ EqualIgnoringASCIICase(mime_type, "image/avif")) {
return kLossyFormat;
}
#endif
@@ -292,6 +343,35 @@ ImageDecoder::CompressionFormat ImageDecoder::GetCompressionFormat(
return kUndefinedFormat;
}
+bool ImageDecoder::IsSizeAvailable() {
+ if (failed_)
+ return false;
+ if (!size_available_)
+ DecodeSize();
+
+ if (!IsDecodedSizeAvailable())
+ return false;
+
+#if defined(OS_FUCHSIA)
+ unsigned decoded_bytes_per_pixel = 4;
+ if (ImageIsHighBitDepth() &&
+ high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) {
+ decoded_bytes_per_pixel = 8;
+ }
+
+ const IntSize size = DecodedSize();
+ const size_t decoded_size_bytes =
+ size.Width() * size.Height() * decoded_bytes_per_pixel;
+ if (decoded_size_bytes > max_decoded_bytes_) {
+ LOG(WARNING) << "Blocked decode of oversized image: " << size.Width() << "x"
+ << size.Height();
+ return SetFailed();
+ }
+#endif
+
+ return true;
+}
+
cc::ImageHeaderMetadata ImageDecoder::MakeMetadataForDecodeAcceleration()
const {
DCHECK(IsDecodedSizeAvailable());
@@ -353,10 +433,10 @@ size_t ImageDecoder::FrameBytesAtIndex(size_t index) const {
frame_buffer_cache_[index].GetStatus() == ImageFrame::kFrameEmpty)
return 0;
- size_t decoded_bytes_per_pixel = k4BytesPerPixel;
+ size_t decoded_bytes_per_pixel = 4;
if (frame_buffer_cache_[index].GetPixelFormat() ==
ImageFrame::PixelFormat::kRGBA_F16) {
- decoded_bytes_per_pixel = k8BytesPerPixel;
+ decoded_bytes_per_pixel = 8;
}
IntSize size = FrameSizeAtIndex(index);
base::CheckedNumeric<size_t> area = size.Width();
@@ -564,11 +644,11 @@ void ImageDecoder::UpdateAggressivePurging(size_t index) {
// As we decode we will learn the total number of frames, and thus total
// possible image memory used.
- size_t decoded_bytes_per_pixel = k4BytesPerPixel;
+ size_t decoded_bytes_per_pixel = 4;
if (frame_buffer_cache_.size() && frame_buffer_cache_[0].GetPixelFormat() ==
ImageFrame::PixelFormat::kRGBA_F16) {
- decoded_bytes_per_pixel = k8BytesPerPixel;
+ decoded_bytes_per_pixel = 8;
}
const uint64_t frame_memory_usage =
DecodedSize().Area() * decoded_bytes_per_pixel;
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 8e2ece9253e..130462c5729 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
@@ -143,7 +143,7 @@ class PLATFORM_EXPORT ImageDecoder {
};
// Enforces YUV decoding to be disallowed in the image decoder. The default
- // value defers to the YUV decoding decision to the decoder.
+ // value defers the YUV decoding decision to the decoder.
enum class OverrideAllowDecodeToYuv {
kDefault,
kDeny,
@@ -178,6 +178,19 @@ class PLATFORM_EXPORT ImageDecoder {
color_behavior, allow_decode_to_yuv, desired_size);
}
+ // Similar to above, but does not allow mime sniffing. Creates explicitly
+ // based on the |mime_type| value.
+ static std::unique_ptr<ImageDecoder> CreateByMimeType(
+ String mime_type,
+ scoped_refptr<SegmentReader> data,
+ bool data_complete,
+ AlphaOption alpha_option,
+ HighBitDepthDecodingOption high_bit_depth_decoding_option,
+ const ColorBehavior& color_behavior,
+ const OverrideAllowDecodeToYuv allow_decode_to_yuv =
+ OverrideAllowDecodeToYuv::kDefault,
+ const SkISize& desired_size = SkISize::MakeEmpty());
+
virtual String FilenameExtension() const = 0;
bool IsAllDataReceived() const { return is_all_data_received_; }
@@ -191,10 +204,10 @@ class PLATFORM_EXPORT ImageDecoder {
// Returns true if the buffer holds enough data to instantiate a decoder.
// This is useful for callers to determine whether a decoder instantiation
// failure is due to insufficient or bad data.
- static bool HasSufficientDataToSniffImageType(const SharedBuffer&);
+ static bool HasSufficientDataToSniffMimeType(const SharedBuffer&);
// Looks at the image data to determine and return the image MIME type.
- static String SniffImageType(scoped_refptr<SharedBuffer> image_data);
+ static String SniffMimeType(scoped_refptr<SharedBuffer> image_data);
// Returns the image data's compression format.
static CompressionFormat GetCompressionFormat(
@@ -216,13 +229,7 @@ class PLATFORM_EXPORT ImageDecoder {
virtual void OnSetData(SegmentReader* data) {}
- bool IsSizeAvailable() {
- if (failed_)
- return false;
- if (!size_available_)
- DecodeSize();
- return IsDecodedSizeAvailable();
- }
+ bool IsSizeAvailable();
bool IsDecodedSizeAvailable() const { return !failed_ && size_available_; }
@@ -376,7 +383,7 @@ class PLATFORM_EXPORT ImageDecoder {
frame_buffer_cache_[0].SetMemoryAllocator(allocator);
}
- bool CanDecodeToYUV() { return allow_decode_to_yuv_; }
+ bool CanDecodeToYUV() const { return allow_decode_to_yuv_; }
// Should only be called if CanDecodeToYuv() returns true, in which case
// the subclass of ImageDecoder must override this method.
virtual void DecodeToYUV() { NOTREACHED(); }
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
index 0ae6fa600b6..2c2e1693a04 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder_test.cc
@@ -31,7 +31,10 @@
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include <memory>
+#include "build/build_config.h"
+#include "media/media_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -39,12 +42,13 @@ namespace blink {
class TestImageDecoder : public ImageDecoder {
public:
- TestImageDecoder(
- ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option)
+ explicit TestImageDecoder(
+ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option,
+ size_t max_decoded_bytes = kNoDecodedImageByteLimit)
: ImageDecoder(kAlphaNotPremultiplied,
high_bit_depth_decoding_option,
ColorBehavior::TransformToSRGB(),
- kNoDecodedImageByteLimit) {}
+ max_decoded_bytes) {}
TestImageDecoder() : TestImageDecoder(ImageDecoder::kDefaultBitDepth) {}
@@ -299,4 +303,94 @@ TEST(ImageDecoderTest, clearCacheExceptFramePreverveClearExceptFrame) {
}
}
+#if defined(OS_FUCHSIA)
+
+TEST(ImageDecoderTest, decodedSizeLimitBoundary) {
+ constexpr unsigned kWidth = 100;
+ constexpr unsigned kHeight = 200;
+ constexpr unsigned kBitDepth = 4;
+ std::unique_ptr<TestImageDecoder> decoder(std::make_unique<TestImageDecoder>(
+ ImageDecoder::kDefaultBitDepth, (kWidth * kHeight * kBitDepth)));
+
+ // Smallest allowable size, should succeed.
+ EXPECT_TRUE(decoder->SetSize(1, 1));
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->Failed());
+
+ // At the limit, should succeed.
+ EXPECT_TRUE(decoder->SetSize(kWidth, kHeight));
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->Failed());
+
+ // Just over the limit, should fail.
+ EXPECT_TRUE(decoder->SetSize(kWidth + 1, kHeight));
+ EXPECT_FALSE(decoder->IsSizeAvailable());
+ EXPECT_TRUE(decoder->Failed());
+}
+
+TEST(ImageDecoderTest, decodedSizeUnlimited) {
+ // Very large values for width and height should be OK.
+ constexpr unsigned kWidth = 10000;
+ constexpr unsigned kHeight = 10000;
+
+ std::unique_ptr<TestImageDecoder> decoder(std::make_unique<TestImageDecoder>(
+ ImageDecoder::kDefaultBitDepth, ImageDecoder::kNoDecodedImageByteLimit));
+ EXPECT_TRUE(decoder->SetSize(kWidth, kHeight));
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->Failed());
+}
+
+#else
+
+// The limit is currently ignored on non-Fuchsia platforms (except for
+// JPEG, which would decode a down-sampled version).
+TEST(ImageDecoderTest, decodedSizeLimitIsIgnored) {
+ constexpr unsigned kWidth = 100;
+ constexpr unsigned kHeight = 200;
+ constexpr unsigned kBitDepth = 4;
+ std::unique_ptr<TestImageDecoder> decoder(std::make_unique<TestImageDecoder>(
+ ImageDecoder::kDefaultBitDepth, (kWidth * kHeight * kBitDepth)));
+
+ // Just over the limit. The limit should be ignored.
+ EXPECT_TRUE(decoder->SetSize(kWidth + 1, kHeight));
+ EXPECT_TRUE(decoder->IsSizeAvailable());
+ EXPECT_FALSE(decoder->Failed());
+}
+
+#endif // defined(OS_FUCHSIA)
+
+#if BUILDFLAG(ENABLE_AV1_DECODER)
+TEST(ImageDecoderTest, hasSufficientDataToSniffMimeTypeAvif) {
+ if (base::FeatureList::IsEnabled(features::kAVIF)) {
+ // The first 36 bytes of the Netflix AVIF test image
+ // Chimera-AV1-10bit-1280x720-2380kbps-100.avif. Since the major_brand is
+ // not "avif" or "avis", we must parse the compatible_brands to determine if
+ // this is an AVIF image.
+ constexpr char kData[] = {
+ // A File Type Box.
+ 0x00, 0x00, 0x00, 0x1c, // unsigned int(32) size; 0x1c = 28
+ 'f', 't', 'y', 'p', // unsigned int(32) type = boxtype;
+ 'm', 'i', 'f', '1', // unsigned int(32) major_brand;
+ 0x00, 0x00, 0x00, 0x00, // unsigned int(32) minor_version;
+ 'm', 'i', 'f', '1', // unsigned int(32) compatible_brands[];
+ 'a', 'v', 'i', 'f', //
+ 'm', 'i', 'a', 'f', //
+ // The beginning of a Media Data Box.
+ 0x00, 0x00, 0xa4, 0x3a, // unsigned int(32) size;
+ 'm', 'd', 'a', 't' // unsigned int(32) type = boxtype;
+ };
+
+ scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create<size_t>(kData, 8);
+ EXPECT_FALSE(ImageDecoder::HasSufficientDataToSniffMimeType(*buffer));
+ EXPECT_EQ(ImageDecoder::SniffMimeType(buffer), String());
+ buffer->Append<size_t>(kData + 8, 8);
+ EXPECT_FALSE(ImageDecoder::HasSufficientDataToSniffMimeType(*buffer));
+ EXPECT_EQ(ImageDecoder::SniffMimeType(buffer), String());
+ buffer->Append<size_t>(kData + 16, sizeof(kData) - 16);
+ EXPECT_TRUE(ImageDecoder::HasSufficientDataToSniffMimeType(*buffer));
+ EXPECT_EQ(ImageDecoder::SniffMimeType(buffer), "image/avif");
+ }
+}
+#endif // BUILDFLAG(ENABLE_AV1_DECODER)
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc
index 600c03c6218..ecd572abfe4 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame.cc
@@ -133,12 +133,13 @@ bool ImageFrame::AllocatePixelData(int new_width,
std::move(color_space));
if (pixel_format_ == kRGBA_F16)
info = info.makeColorType(kRGBA_F16_SkColorType);
- bitmap_.setInfo(info);
- bool allocated = bitmap_.tryAllocPixels(allocator_);
- if (allocated)
+ bool success = bitmap_.setInfo(info);
+ DCHECK(success);
+ success = bitmap_.tryAllocPixels(allocator_);
+ if (success)
status_ = kFrameInitialized;
- return allocated;
+ return success;
}
sk_sp<SkImage> ImageFrame::FinalizePixelsAndGetImage() {
@@ -194,7 +195,7 @@ static void BlendRGBAF16Buffer(ImageFrame::PixelDataF16* dst,
SkImage::MakeFromRaster(src_pixmap, nullptr, nullptr);
surface->getCanvas()->drawImage(src_image, 0, 0);
- surface->flush();
+ surface->flushAndSubmit();
}
void ImageFrame::BlendRGBARawF16Buffer(PixelDataF16* dst,
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 a53bc4b60d6..f6c6e53c1ff 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
@@ -290,11 +290,11 @@ static IntSize ExtractDensityCorrectedSize(const DecodedImageMetaData& metadata,
CHECK(metadata.resolution.Height());
// Division by zero is not possible since we check for empty resolution earlier.
- IntSize size_from_resolution(
- physical_size.Width() * kDefaultResolution / metadata.resolution.Width(),
- physical_size.Height() * kDefaultResolution / metadata.resolution.Height());
+ FloatSize size_from_resolution(
+ physical_size.Width() * kDefaultResolution / metadata.resolution.Width(),
+ physical_size.Height() * kDefaultResolution / metadata.resolution.Height());
- if (size_from_resolution == metadata.size)
+ if (RoundedIntSize(size_from_resolution) == metadata.size)
return metadata.size;
return physical_size;
@@ -719,7 +719,8 @@ class JPEGImageReader final {
profile = nullptr;
break;
}
- Decoder()->SetEmbeddedColorProfile(std::move(profile));
+ if (profile)
+ Decoder()->SetEmbeddedColorProfile(std::move(profile));
} else {
DLOG(ERROR) << "Failed to parse image ICC profile";
}
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h
index 203afe95a23..0c38ff5ec5c 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.h
@@ -47,13 +47,13 @@ class PLATFORM_EXPORT JPEGImageDecoder final : public ImageDecoder {
String FilenameExtension() const override { return "jpg"; }
void OnSetData(SegmentReader* data) override;
IntSize DecodedSize() const override { return decoded_size_; }
- IntSize DensityCorrectedSize() const { return density_corrected_size_.IsEmpty() ? DecodedSize() : density_corrected_size_; }
bool SetSize(unsigned width, unsigned height) override;
IntSize DecodedYUVSize(int component) const override;
size_t DecodedYUVWidthBytes(int component) const override;
void DecodeToYUV() override;
SkYUVColorSpace GetYUVColorSpace() const override;
Vector<SkISize> GetSupportedDecodeSizes() const override;
+
bool HasImagePlanes() const { return image_planes_.get(); }
bool OutputScanlines();
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 5d38e4c1de1..71c2ef590cd 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
@@ -51,11 +51,11 @@ namespace {
std::unique_ptr<JPEGImageDecoder> CreateJPEGDecoder(
size_t max_decoded_bytes,
- ImageDecoder::OverrideAllowDecodeToYuv decodeToYUV =
+ ImageDecoder::OverrideAllowDecodeToYuv allow_decode_to_yuv =
ImageDecoder::OverrideAllowDecodeToYuv::kDeny) {
return std::make_unique<JPEGImageDecoder>(
ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(),
- max_decoded_bytes, decodeToYUV);
+ max_decoded_bytes, allow_decode_to_yuv);
}
std::unique_ptr<ImageDecoder> CreateJPEGDecoder() {
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
index 509fe3ad16b..02f2eb9b0ef 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/png/png_image_decoder.cc
@@ -711,7 +711,7 @@ void PNGImageDecoder::RowAvailable(unsigned char* row_buffer,
// TODO: Apply the xform to the RGB pixels, skipping second pass over
// data.
if (ColorProfileTransform* xform = ColorTransform()) {
- skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Opaque;
+ skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Unpremul;
bool color_conversion_successful =
skcms_Transform(dst_row, XformColorFormat(), alpha_format,
xform->SrcProfile(), dst_row, XformColorFormat(),
@@ -737,12 +737,10 @@ void PNGImageDecoder::RowAvailable(unsigned char* row_buffer,
auto* dst_profile = xform ? xform->DstProfile() : nullptr;
auto src_format = has_alpha ? skcms_PixelFormat_RGBA_16161616BE
: skcms_PixelFormat_RGB_161616BE;
- auto src_alpha_format =
- has_alpha ? skcms_AlphaFormat_Unpremul : skcms_AlphaFormat_Opaque;
- auto dst_alpha_format = has_alpha ? (buffer.PremultiplyAlpha()
- ? skcms_AlphaFormat_PremulAsEncoded
- : skcms_AlphaFormat_Unpremul)
- : skcms_AlphaFormat_Opaque;
+ auto src_alpha_format = skcms_AlphaFormat_Unpremul;
+ auto dst_alpha_format = (has_alpha && buffer.PremultiplyAlpha())
+ ? skcms_AlphaFormat_PremulAsEncoded
+ : skcms_AlphaFormat_Unpremul;
bool success = skcms_Transform(
src_ptr, src_format, src_alpha_format, src_profile, dst_row_f16,
skcms_PixelFormat_RGBA_hhhh, dst_alpha_format, dst_profile, width);
diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc
index a75f37382a0..4dbf00b141f 100644
--- a/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc
+++ b/chromium/third_party/blink/renderer/platform/image-decoders/webp/webp_image_decoder.cc
@@ -430,7 +430,6 @@ IntSize WEBPImageDecoder::DecodedYUVSize(int component) const {
case SkYUVAIndex::kY_Index:
return Size();
case SkYUVAIndex::kU_Index:
- FALLTHROUGH;
case SkYUVAIndex::kV_Index:
return IntSize((Size().Width() + 1) / 2, (Size().Height() + 1) / 2);
}
@@ -445,7 +444,6 @@ size_t WEBPImageDecoder::DecodedYUVWidthBytes(int component) const {
case SkYUVAIndex::kY_Index:
return base::checked_cast<size_t>(Size().Width());
case SkYUVAIndex::kU_Index:
- FALLTHROUGH;
case SkYUVAIndex::kV_Index:
return base::checked_cast<size_t>((Size().Width() + 1) / 2);
}
diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc
index 7fb56fd4bb4..679c9fb42e0 100644
--- a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc
+++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
+#include "base/notreached.h"
#include "build/build_config.h"
#if defined(OS_WIN)
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn b/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn
index a53a2e66cac..afed343a1d4 100644
--- a/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/BUILD.gn
@@ -13,6 +13,8 @@ blink_platform_sources("instrumentation") {
]
sources = [
+ "canvas_memory_dump_provider.cc",
+ "canvas_memory_dump_provider.h",
"histogram.cc",
"histogram.h",
"instance_counters.cc",
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.cc b/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.cc
new file mode 100644
index 00000000000..168e75ece1c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.cc
@@ -0,0 +1,70 @@
+// 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/instrumentation/canvas_memory_dump_provider.h"
+
+#include "base/trace_event/memory_dump_manager.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+CanvasMemoryDumpProvider* CanvasMemoryDumpProvider::Instance() {
+ DEFINE_STATIC_LOCAL(CanvasMemoryDumpProvider, instance, ());
+ return &instance;
+}
+
+bool CanvasMemoryDumpProvider::OnMemoryDump(
+ const base::trace_event::MemoryDumpArgs& args,
+ base::trace_event::ProcessMemoryDump* memory_dump) {
+ if (args.level_of_detail ==
+ base::trace_event::MemoryDumpLevelOfDetail::DETAILED) {
+ base::AutoLock auto_lock(lock_);
+ for (auto* it : clients_)
+ it->OnMemoryDump(memory_dump);
+ return true;
+ }
+
+ size_t total_size = 0;
+ size_t clients_size = 0;
+ {
+ base::AutoLock auto_lock(lock_);
+ for (auto* it : clients_)
+ total_size += it->GetSize();
+ clients_size = clients_.size();
+ }
+
+ auto* dump =
+ memory_dump->CreateAllocatorDump("canvas/ResourceProvider/SkSurface");
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
+ base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+ total_size);
+ dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
+ base::trace_event::MemoryAllocatorDump::kUnitsObjects,
+ clients_size);
+
+ // SkiaMemoryDumpProvider reports only sk_glyph_cache and sk_resource_cache.
+ // So the SkSurface is suballocation of malloc, not SkiaDumpProvider.
+ if (const char* system_allocator_name =
+ base::trace_event::MemoryDumpManager::GetInstance()
+ ->system_allocator_pool_name()) {
+ memory_dump->AddSuballocation(dump->guid(), system_allocator_name);
+ }
+ return true;
+}
+
+void CanvasMemoryDumpProvider::RegisterClient(CanvasMemoryDumpClient* client) {
+ base::AutoLock auto_lock(lock_);
+ clients_.insert(client);
+}
+
+void CanvasMemoryDumpProvider::UnregisterClient(
+ CanvasMemoryDumpClient* client) {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(clients_.Contains(client));
+ clients_.erase(client);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h b/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h
new file mode 100644
index 00000000000..809f97a6eaa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h
@@ -0,0 +1,51 @@
+// 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_INSTRUMENTATION_CANVAS_MEMORY_DUMP_PROVIDER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_CANVAS_MEMORY_DUMP_PROVIDER_H_
+
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/trace_event/memory_dump_provider.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/hash_set.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT CanvasMemoryDumpClient {
+ public:
+ virtual void OnMemoryDump(base::trace_event::ProcessMemoryDump*) = 0;
+ virtual size_t GetSize() const = 0;
+
+ ~CanvasMemoryDumpClient() = default;
+};
+
+class PLATFORM_EXPORT CanvasMemoryDumpProvider final
+ : public base::trace_event::MemoryDumpProvider {
+ USING_FAST_MALLOC(CanvasMemoryDumpProvider);
+
+ public:
+ static CanvasMemoryDumpProvider* Instance();
+ ~CanvasMemoryDumpProvider() override = default;
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const base::trace_event::MemoryDumpArgs&,
+ base::trace_event::ProcessMemoryDump*) override;
+
+ void RegisterClient(CanvasMemoryDumpClient*);
+ void UnregisterClient(CanvasMemoryDumpClient*);
+
+ private:
+ CanvasMemoryDumpProvider() = default;
+
+ base::Lock lock_;
+ WTF::HashSet<CanvasMemoryDumpClient*> clients_ GUARDED_BY(lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(CanvasMemoryDumpProvider);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_CANVAS_MEMORY_DUMP_PROVIDER_H_
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 3cca4cb7409..261786c3aaa 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
@@ -133,7 +133,7 @@ void MemoryPressureListenerRegistry::ClearThreadSpecificMemory() {
FontGlobalContext::ClearMemory();
}
-void MemoryPressureListenerRegistry::Trace(Visitor* visitor) {
+void MemoryPressureListenerRegistry::Trace(Visitor* visitor) const {
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 cc24601b37a..d31890304ee 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
@@ -59,7 +59,7 @@ class PLATFORM_EXPORT MemoryPressureListenerRegistry final
void OnPurgeMemory();
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
private:
friend class Internals;
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 37ca752b695..910cd30e8a1 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(Visitor* visitor) {}
+void MemoryCacheDumpClient::Trace(Visitor* visitor) const {}
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 e996a368083..32e5523a9d0 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(Visitor*) override;
+ void Trace(Visitor*) const override;
};
// This class is wrapper around MemoryCache to take memory snapshots. It dumps
diff --git a/chromium/third_party/blink/renderer/platform/json/json_parser.cc b/chromium/third_party/blink/renderer/platform/json/json_parser.cc
index 54e15fa2379..56b11c0e8e8 100644
--- a/chromium/third_party/blink/renderer/platform/json/json_parser.cc
+++ b/chromium/third_party/blink/renderer/platform/json/json_parser.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/json/json_parser.h"
+#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/wtf/decimal.h"
diff --git a/chromium/third_party/blink/renderer/platform/json/json_values.cc b/chromium/third_party/blink/renderer/platform/json/json_values.cc
index 1d35618d262..63e752dab85 100644
--- a/chromium/third_party/blink/renderer/platform/json/json_values.cc
+++ b/chromium/third_party/blink/renderer/platform/json/json_values.cc
@@ -33,6 +33,7 @@
#include <algorithm>
#include <cmath>
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/decimal.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
index 813407f33b3..dad1cc0675d 100644
--- a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -35,6 +35,8 @@ blink_platform_sources("loader") {
"fetch/data_pipe_bytes_consumer.cc",
"fetch/data_pipe_bytes_consumer.h",
"fetch/detachable_use_counter.h",
+ "fetch/fetch_api_request_body_mojom_traits.cc",
+ "fetch/fetch_api_request_body_mojom_traits.h",
"fetch/fetch_client_settings_object.h",
"fetch/fetch_client_settings_object_snapshot.cc",
"fetch/fetch_client_settings_object_snapshot.h",
@@ -145,6 +147,7 @@ jumbo_source_set("unit_tests") {
"fetch/bytes_consumer_test.cc",
"fetch/client_hints_preferences_test.cc",
"fetch/data_pipe_bytes_consumer_test.cc",
+ "fetch/fetch_api_request_body_mojom_traits_test.cc",
"fetch/fetch_utils_test.cc",
"fetch/memory_cache_correctness_test.cc",
"fetch/memory_cache_test.cc",
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 4b4c2cc6c9d..4b4601889bb 100644
--- a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -277,7 +277,7 @@ bool CheckIfRequestCanSkipPreflight(
// This is the same as that function except using KURL and SecurityOrigin
// instead of GURL and url::Origin. We can't combine them because converting
// SecurityOrigin to url::Origin loses information about origins that are
-// whitelisted by SecurityPolicy.
+// allowed by SecurityPolicy.
//
// This function also doesn't use a |tainted_origin| flag because Blink loaders
// mutate the origin instead of using such a flag.
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/OWNERS b/chromium/third_party/blink/renderer/platform/loader/fetch/OWNERS
new file mode 100644
index 00000000000..a3060033221
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/OWNERS
@@ -0,0 +1,5 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
+
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 e8411eddd6c..b33a8bc2cf0 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
@@ -162,7 +162,7 @@ BytesConsumer::Error BufferingBytesConsumer::GetError() const {
return bytes_consumer_->GetError();
}
-void BufferingBytesConsumer::Trace(Visitor* visitor) {
+void BufferingBytesConsumer::Trace(Visitor* visitor) const {
visitor->Trace(bytes_consumer_);
visitor->Trace(client_);
BytesConsumer::Trace(visitor);
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 49155275231..78e916784b1 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(Visitor*) override;
+ void Trace(Visitor*) const override;
private:
void OnTimerFired(TimerBase*);
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.cc
index 1351b8de938..eb8d2dbe8d7 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.cc
@@ -66,4 +66,32 @@ BytesConsumer* BytesConsumer::CreateClosed() {
return MakeGarbageCollected<ClosedBytesConsumer>();
}
+std::ostream& operator<<(std::ostream& out,
+ const BytesConsumer::PublicState& state) {
+ switch (state) {
+ case BytesConsumer::PublicState::kReadableOrWaiting:
+ return out << "kReadableOrWaiting";
+ case BytesConsumer::PublicState::kClosed:
+ return out << "kClosed";
+ case BytesConsumer::PublicState::kErrored:
+ return out << "kErrored";
+ }
+ NOTREACHED();
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const BytesConsumer::Result& result) {
+ switch (result) {
+ case BytesConsumer::Result::kOk:
+ return out << "kOk";
+ case BytesConsumer::Result::kShouldWait:
+ return out << "kShouldWait";
+ case BytesConsumer::Result::kDone:
+ return out << "kDone";
+ case BytesConsumer::Result::kError:
+ return out << "kError";
+ }
+ NOTREACHED();
+}
+
} // namespace blink
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 5ec8bba5dd9..8b348381da8 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
@@ -5,6 +5,8 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BYTES_CONSUMER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BYTES_CONSUMER_H_
+#include <ostream>
+
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
@@ -177,7 +179,7 @@ class PLATFORM_EXPORT BytesConsumer : public GarbageCollected<BytesConsumer> {
// Returns a BytesConsumer whose state is Errored.
static BytesConsumer* CreateErrored(const Error&);
- virtual void Trace(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
protected:
// This InternalState directly corresponds to the states in the class
@@ -204,6 +206,12 @@ class PLATFORM_EXPORT BytesConsumer : public GarbageCollected<BytesConsumer> {
}
};
+PLATFORM_EXPORT std::ostream& operator<<(
+ std::ostream& out,
+ const BytesConsumer::PublicState& state);
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream& out,
+ const BytesConsumer::Result& result);
+
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BYTES_CONSUMER_H_
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 c0a819c63b0..00d990487d2 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
@@ -71,7 +71,7 @@ class CachedMetadataHandler : public GarbageCollected<CachedMetadataHandler> {
};
virtual ~CachedMetadataHandler() = default;
- virtual void Trace(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
// Reset existing metadata. Subclasses can ignore setting new metadata after
// clearing with |kDiscardLocally| to save memory.
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/console_logger.h b/chromium/third_party/blink/renderer/platform/loader/fetch/console_logger.h
index dc811ba901e..163b03c090f 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/console_logger.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/console_logger.h
@@ -51,7 +51,7 @@ class PLATFORM_EXPORT DetachableConsoleLogger final
// be no-op.
void Detach() { logger_ = nullptr; }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(logger_);
ConsoleLogger::Trace(visitor);
}
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 c36b32b18fc..4a825ae4c58 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
@@ -20,13 +20,18 @@ void DataPipeBytesConsumer::CompletionNotifier::SignalComplete() {
bytes_consumer_->SignalComplete();
}
+void DataPipeBytesConsumer::CompletionNotifier::SignalSize(uint64_t size) {
+ if (bytes_consumer_)
+ bytes_consumer_->SignalSize(size);
+}
+
void DataPipeBytesConsumer::CompletionNotifier::SignalError(
const BytesConsumer::Error& error) {
if (bytes_consumer_)
bytes_consumer_->SignalError(error);
}
-void DataPipeBytesConsumer::CompletionNotifier::Trace(Visitor* visitor) {
+void DataPipeBytesConsumer::CompletionNotifier::Trace(Visitor* visitor) const {
visitor->Trace(bytes_consumer_);
}
@@ -79,6 +84,10 @@ BytesConsumer::Result DataPipeBytesConsumer::BeginRead(const char** buffer,
return Result::kShouldWait;
case MOJO_RESULT_FAILED_PRECONDITION:
ClearDataPipe();
+ if (total_size_ && num_read_bytes_ < *total_size_) {
+ SetError(Error("error"));
+ return Result::kError;
+ }
MaybeClose();
// We hit the end of the pipe, but we may still need to wait for
// SignalComplete() or SignalError() to be called.
@@ -102,6 +111,7 @@ BytesConsumer::Result DataPipeBytesConsumer::EndRead(size_t read) {
SetError(Error("error"));
return Result::kError;
}
+ num_read_bytes_ += read;
if (has_pending_complete_) {
has_pending_complete_ = false;
SignalComplete();
@@ -112,6 +122,13 @@ BytesConsumer::Result DataPipeBytesConsumer::EndRead(size_t read) {
SignalError(Error("error"));
return Result::kError;
}
+ if (total_size_ == num_read_bytes_) {
+ ClearDataPipe();
+ ClearClient();
+ SignalComplete();
+ return Result::kDone;
+ }
+
if (has_pending_notification_) {
has_pending_notification_ = false;
task_runner_->PostTask(FROM_HERE,
@@ -153,7 +170,7 @@ BytesConsumer::PublicState DataPipeBytesConsumer::GetPublicState() const {
return GetPublicStateFromInternalState(state_);
}
-void DataPipeBytesConsumer::Trace(Visitor* visitor) {
+void DataPipeBytesConsumer::Trace(Visitor* visitor) const {
visitor->Trace(client_);
BytesConsumer::Trace(visitor);
}
@@ -193,6 +210,22 @@ void DataPipeBytesConsumer::SignalComplete() {
watcher_.ArmOrNotify();
}
+void DataPipeBytesConsumer::SignalSize(uint64_t size) {
+ if (!IsReadableOrWaiting() || has_pending_complete_ || has_pending_error_)
+ return;
+ total_size_ = base::make_optional(size);
+ DCHECK_LE(num_read_bytes_, *total_size_);
+ if (!data_pipe_.is_valid() && num_read_bytes_ < *total_size_) {
+ SignalError(Error());
+ return;
+ }
+
+ if (!is_in_two_phase_read_ && *total_size_ == num_read_bytes_) {
+ ClearDataPipe();
+ SignalComplete();
+ }
+}
+
void DataPipeBytesConsumer::SignalError(const Error& error) {
if (!IsReadableOrWaiting() || has_pending_complete_ || has_pending_error_)
return;
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 9bfc9775f21..7af43dd2b1a 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
@@ -35,12 +35,14 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer {
: bytes_consumer_(bytes_consumer) {}
// One of these methods must be called to signal the end of the data
- // stream. We cannot assume that the end of the pipe completes the
- // stream successfully since errors can occur after the last byte is
- // written into the pipe.
+ // stream. (SignalSize notifies the total size. That information can
+ // be used to detect the end-of-stream). We cannot assume that the end
+ // of the pipe completes the stream successfully since errors can
+ // occur after the last byte is written into the pipe.
void SignalComplete();
+ void SignalSize(uint64_t size);
void SignalError(const BytesConsumer::Error& error);
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
private:
const WeakMember<DataPipeBytesConsumer> bytes_consumer_;
@@ -65,7 +67,7 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer {
}
String DebugName() const override { return "DataPipeBytesConsumer"; }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
private:
bool IsReadableOrWaiting() const;
@@ -74,6 +76,7 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer {
void Notify(MojoResult);
void ClearDataPipe();
void SignalComplete();
+ void SignalSize(uint64_t);
void SignalError(const Error& error);
void Dispose();
@@ -83,6 +86,8 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer {
Member<BytesConsumer::Client> client_;
InternalState state_ = InternalState::kWaiting;
Error error_;
+ uint64_t num_read_bytes_ = 0;
+ base::Optional<uint64_t> total_size_;
bool is_in_two_phase_read_ = false;
bool has_pending_notification_ = false;
bool has_pending_complete_ = false;
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc
index 21bd571a15c..ec03ab4e89b 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc
@@ -165,6 +165,140 @@ TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeError) {
EXPECT_EQ(Result::kError, rv);
}
+TEST_F(DataPipeBytesConsumerTest, SignalSizeBeforeRead) {
+ mojo::ScopedDataPipeConsumerHandle readable;
+ mojo::ScopedDataPipeProducerHandle writable;
+ const MojoCreateDataPipeOptions options{
+ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(&options, &writable, &readable));
+ DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr;
+ DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+ task_runner_, std::move(readable), &notifier);
+
+ constexpr char kData[] = "hello";
+ uint32_t write_size = 5;
+ MojoResult write_result =
+ writable->WriteData(kData, &write_size, MOJO_WRITE_DATA_FLAG_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK, write_result);
+ ASSERT_EQ(5u, write_size);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+
+ notifier->SignalSize(5);
+
+ Result rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kOk, rv);
+ EXPECT_EQ(available, 5u);
+
+ rv = consumer->EndRead(2);
+ ASSERT_EQ(Result::kOk, rv);
+
+ rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kOk, rv);
+ EXPECT_EQ(available, 3u);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+ rv = consumer->EndRead(3);
+ ASSERT_EQ(Result::kDone, rv);
+ EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
+}
+
+TEST_F(DataPipeBytesConsumerTest, SignalExcessSizeBeforeEndOfData) {
+ mojo::ScopedDataPipeConsumerHandle readable;
+ mojo::ScopedDataPipeProducerHandle writable;
+ const MojoCreateDataPipeOptions options{
+ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(&options, &writable, &readable));
+ DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr;
+ DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+ task_runner_, std::move(readable), &notifier);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+
+ notifier->SignalSize(1);
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+ Result rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kShouldWait, rv);
+
+ writable.reset();
+
+ rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kError, rv);
+
+ EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
+}
+
+TEST_F(DataPipeBytesConsumerTest, SignalExcessSizeAfterEndOfData) {
+ mojo::ScopedDataPipeConsumerHandle readable;
+ mojo::ScopedDataPipeProducerHandle writable;
+ const MojoCreateDataPipeOptions options{
+ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(&options, &writable, &readable));
+ DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr;
+ DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+ task_runner_, std::move(readable), &notifier);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+
+ writable.reset();
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+ Result rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kShouldWait, rv);
+
+ notifier->SignalSize(1);
+
+ rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kError, rv);
+
+ EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
+}
+
+TEST_F(DataPipeBytesConsumerTest, SignalSizeAfterRead) {
+ mojo::ScopedDataPipeConsumerHandle readable;
+ mojo::ScopedDataPipeProducerHandle writable;
+ const MojoCreateDataPipeOptions options{
+ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
+ ASSERT_EQ(MOJO_RESULT_OK,
+ mojo::CreateDataPipe(&options, &writable, &readable));
+
+ DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr;
+ DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+ task_runner_, std::move(readable), &notifier);
+
+ constexpr char kData[] = "hello";
+ uint32_t write_size = 5;
+ MojoResult write_result =
+ writable->WriteData(kData, &write_size, MOJO_WRITE_DATA_FLAG_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK, write_result);
+ ASSERT_EQ(5u, write_size);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+
+ const char* buffer = nullptr;
+ size_t available = 0;
+
+ Result rv = consumer->BeginRead(&buffer, &available);
+ ASSERT_EQ(Result::kOk, rv);
+ EXPECT_EQ(available, 5u);
+
+ rv = consumer->EndRead(5);
+ ASSERT_EQ(Result::kOk, rv);
+
+ EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
+ notifier->SignalSize(5);
+ EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
+}
+
TEST_F(DataPipeBytesConsumerTest, ErrorBeforeEndOfPipe) {
mojo::DataPipe pipe;
ASSERT_TRUE(pipe.producer_handle.is_valid());
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h b/chromium/third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h
index 353f792e369..89d6781f2f1 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h
@@ -31,7 +31,7 @@ class DetachableUseCounter final
use_counter_->CountDeprecation(feature);
}
}
- void Trace(Visitor* visitor) override { visitor->Trace(use_counter_); }
+ void Trace(Visitor* visitor) const override { visitor->Trace(use_counter_); }
void Detach() { use_counter_ = nullptr; }
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body.typemap b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body.typemap
new file mode 100644
index 00000000000..86c785a2151
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body.typemap
@@ -0,0 +1,10 @@
+# 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.
+
+mojom = "//third_party/blink/public/mojom/fetch/fetch_api_request.mojom"
+public_headers =
+ [ "//third_party/blink/renderer/platform/loader/fetch/resource_request.h" ]
+traits_headers = [ "//third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h" ]
+
+type_mappings = [ "blink.mojom.FetchAPIRequestBody=::blink::ResourceRequestBody[nullable_is_same_type,move_only]" ]
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc
new file mode 100644
index 00000000000..eb6cdc34a34
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.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/loader/fetch/fetch_api_request_body_mojom_traits.h"
+
+#include "mojo/public/cpp/base/file_mojom_traits.h"
+#include "mojo/public/cpp/base/file_path_mojom_traits.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
+#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
+
+namespace mojo {
+
+// static
+WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr>
+StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+ blink::ResourceRequestBody>::elements(blink::ResourceRequestBody&
+ mutable_body) {
+ WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr> out_elements;
+ const auto& body = mutable_body;
+ if (body.IsEmpty()) {
+ return out_elements;
+ }
+
+ if (mutable_body.StreamBody()) {
+ auto out = blink::mojom::blink::FetchAPIDataElement::New();
+ out->type = network::mojom::DataElementType::kChunkedDataPipe;
+ out->chunked_data_pipe_getter = mutable_body.TakeStreamBody();
+ out_elements.push_back(std::move(out));
+ return out_elements;
+ }
+
+ DCHECK(body.FormBody());
+ for (const auto& element : body.FormBody()->elements_) {
+ auto out = blink::mojom::blink::FetchAPIDataElement::New();
+ switch (element.type_) {
+ case blink::FormDataElement::kData:
+ out->type = network::mojom::DataElementType::kBytes;
+ out->buf.ReserveCapacity(element.data_.size());
+ for (const char c : element.data_) {
+ out->buf.push_back(static_cast<uint8_t>(c));
+ }
+ break;
+ case blink::FormDataElement::kEncodedFile:
+ out->type = network::mojom::DataElementType::kFile;
+ out->path = base::FilePath::FromUTF8Unsafe(element.filename_.Utf8());
+ out->offset = element.file_start_;
+ out->length = element.file_length_;
+ out->expected_modification_time =
+ element.expected_file_modification_time_.value_or(base::Time());
+ break;
+ case blink::FormDataElement::kEncodedBlob:
+ if (element.optional_blob_data_handle_) {
+ out->type = network::mojom::DataElementType::kDataPipe;
+ out->length = element.optional_blob_data_handle_->size();
+
+ mojo::Remote<blink::mojom::blink::Blob> blob_remote(
+ mojo::PendingRemote<blink::mojom::blink::Blob>(
+ element.optional_blob_data_handle_->CloneBlobRemote()
+ .PassPipe(),
+ blink::mojom::blink::Blob::Version_));
+ mojo::PendingRemote<network::mojom::blink::DataPipeGetter>
+ data_pipe_getter_remote;
+ blob_remote->AsDataPipeGetter(
+ out->data_pipe_getter.InitWithNewPipeAndPassReceiver());
+ } else {
+ out->type = network::mojom::DataElementType::kBlob;
+ out->blob_uuid = element.blob_uuid_;
+ }
+ break;
+ case blink::FormDataElement::kDataPipe:
+ out->type = network::mojom::DataElementType::kDataPipe;
+ if (element.data_pipe_getter_) {
+ element.data_pipe_getter_->GetDataPipeGetter()->Clone(
+ out->data_pipe_getter.InitWithNewPipeAndPassReceiver());
+ }
+ break;
+ }
+ out_elements.push_back(std::move(out));
+ }
+ return out_elements;
+}
+
+// static
+bool StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+ blink::ResourceRequestBody>::
+ Read(blink::mojom::FetchAPIRequestBodyDataView in,
+ blink::ResourceRequestBody* out) {
+ if (in.is_null()) {
+ *out = blink::ResourceRequestBody();
+ return true;
+ }
+
+ mojo::ArrayDataView<blink::mojom::FetchAPIDataElementDataView> elements_view;
+ in.GetElementsDataView(&elements_view);
+ if (elements_view.size() == 1) {
+ blink::mojom::FetchAPIDataElementDataView view;
+ elements_view.GetDataView(0, &view);
+
+ network::mojom::DataElementType type;
+ if (!view.ReadType(&type)) {
+ return false;
+ }
+ if (type == network::mojom::DataElementType::kChunkedDataPipe) {
+ auto chunked_data_pipe_getter = view.TakeChunkedDataPipeGetter<
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>>();
+ *out = blink::ResourceRequestBody(std::move(chunked_data_pipe_getter));
+ return true;
+ }
+ }
+ auto form_data = blink::EncodedFormData::Create();
+ for (size_t i = 0; i < elements_view.size(); ++i) {
+ blink::mojom::FetchAPIDataElementDataView view;
+ elements_view.GetDataView(i, &view);
+
+ network::mojom::DataElementType type;
+ if (!view.ReadType(&type)) {
+ return false;
+ }
+ switch (type) {
+ case network::mojom::DataElementType::kBytes: {
+ // TODO(richard.li): Delete this workaround when type of
+ // blink::FormDataElement::data_ is changed to WTF::Vector<uint8_t>
+ WTF::Vector<uint8_t> buf;
+ if (!view.ReadBuf(&buf)) {
+ return false;
+ }
+ form_data->AppendData(buf.data(), buf.size());
+ break;
+ }
+ case network::mojom::DataElementType::kFile: {
+ base::FilePath file_path;
+ base::Time expected_time;
+ if (!view.ReadPath(&file_path) ||
+ !view.ReadExpectedModificationTime(&expected_time)) {
+ return false;
+ }
+ base::Optional<base::Time> expected_file_modification_time;
+ if (!expected_time.is_null()) {
+ expected_file_modification_time = expected_time;
+ }
+ form_data->AppendFileRange(blink::FilePathToString(file_path),
+ view.offset(), view.length(),
+ expected_file_modification_time);
+ break;
+ }
+ case network::mojom::DataElementType::kDataPipe: {
+ auto data_pipe_ptr_remote = view.TakeDataPipeGetter<
+ mojo::PendingRemote<network::mojom::blink::DataPipeGetter>>();
+ DCHECK(data_pipe_ptr_remote.is_valid());
+
+ form_data->AppendDataPipe(
+ base::MakeRefCounted<blink::WrappedDataPipeGetter>(
+ std::move(data_pipe_ptr_remote)));
+
+ break;
+ }
+ case network::mojom::DataElementType::kBlob:
+ case network::mojom::DataElementType::kUnknown:
+ case network::mojom::DataElementType::kChunkedDataPipe:
+ case network::mojom::DataElementType::kRawFile:
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ form_data->identifier_ = in.identifier();
+ form_data->contains_password_data_ = in.contains_sensitive_info();
+ form_data->SetBoundary(
+ blink::FormDataEncoder::GenerateUniqueBoundaryString());
+ *out = blink::ResourceRequestBody(std::move(form_data));
+ return true;
+}
+
+} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h
new file mode 100644
index 00000000000..f85aee270a8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.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_LOADER_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
+
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace mojo {
+
+template <>
+struct PLATFORM_EXPORT StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
+ blink::ResourceRequestBody> {
+ static bool IsNull(const blink::ResourceRequestBody& body) {
+ return body.IsEmpty();
+ }
+ static void SetToNull(blink::ResourceRequestBody* out) {
+ *out = blink::ResourceRequestBody();
+ }
+ static WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr> elements(
+ blink::ResourceRequestBody& mutable_body);
+ static int64_t identifier(const blink::ResourceRequestBody& body) {
+ return body.FormBody() ? body.FormBody()->Identifier() : 0;
+ }
+ static bool contains_sensitive_info(const blink::ResourceRequestBody& body) {
+ return body.FormBody() ? body.FormBody()->ContainsPasswordData() : false;
+ }
+
+ static bool Read(blink::mojom::FetchAPIRequestBodyDataView in,
+ blink::ResourceRequestBody* out);
+};
+
+} // namespace mojo
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_API_REQUEST_BODY_MOJOM_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc
new file mode 100644
index 00000000000..7f96b51c38f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc
@@ -0,0 +1,149 @@
+// 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/fetch_api_request_body_mojom_traits.h"
+
+#include "base/test/task_environment.h"
+#include "mojo/public/cpp/base/file_mojom_traits.h"
+#include "mojo/public/cpp/base/file_path_mojom_traits.h"
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#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/platform/file_path_conversion.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
+#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
+
+namespace blink {
+namespace {
+
+class FetchApiRequestBodyMojomTraitsTest : public testing::Test {
+ protected:
+ base::test::TaskEnvironment task_environment_;
+};
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripEmpty) {
+ ResourceRequestBody src;
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ EXPECT_TRUE(dest.IsEmpty());
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripBytes) {
+ ResourceRequestBody src(EncodedFormData::Create());
+ src.FormBody()->AppendData("hello", 5);
+ src.FormBody()->SetIdentifier(29);
+ src.FormBody()->SetContainsPasswordData(true);
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ ASSERT_TRUE(dest.FormBody());
+ EXPECT_EQ(dest.FormBody()->Identifier(), 29);
+ EXPECT_TRUE(dest.FormBody()->ContainsPasswordData());
+ ASSERT_EQ(1u, dest.FormBody()->Elements().size());
+ const FormDataElement& e = dest.FormBody()->Elements()[0];
+ EXPECT_EQ(e.type_, FormDataElement::kData);
+ EXPECT_EQ("hello", String(e.data_.data(), e.data_.size()));
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripFile) {
+ ResourceRequestBody src(EncodedFormData::Create());
+ const base::Time now = base::Time::Now();
+ src.FormBody()->AppendFile("file.name", now);
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ ASSERT_TRUE(dest.FormBody());
+ ASSERT_EQ(1u, dest.FormBody()->Elements().size());
+ const FormDataElement& e = dest.FormBody()->Elements()[0];
+ EXPECT_EQ(e.type_, FormDataElement::kEncodedFile);
+ EXPECT_EQ(e.filename_, "file.name");
+ EXPECT_EQ(e.file_start_, 0);
+ EXPECT_EQ(e.file_length_, BlobData::kToEndOfFile);
+ EXPECT_EQ(e.expected_file_modification_time_, now);
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripFileRange) {
+ ResourceRequestBody src(EncodedFormData::Create());
+ src.FormBody()->AppendFileRange("abc", 4, 8, base::nullopt);
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ ASSERT_TRUE(dest.FormBody());
+ ASSERT_EQ(1u, dest.FormBody()->Elements().size());
+ const FormDataElement& e = dest.FormBody()->Elements()[0];
+ EXPECT_EQ(e.type_, FormDataElement::kEncodedFile);
+ EXPECT_EQ(e.filename_, "abc");
+ EXPECT_EQ(e.file_start_, 4);
+ EXPECT_EQ(e.file_length_, 8);
+ EXPECT_EQ(e.expected_file_modification_time_, base::nullopt);
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripBlobWithOpionalHandle) {
+ ResourceRequestBody src(EncodedFormData::Create());
+ mojo::MessagePipe pipe;
+ String uuid = "test_uuid";
+ auto blob_data_handle = BlobDataHandle::Create(
+ uuid, "type-test", 100,
+ mojo::PendingRemote<mojom::blink::Blob>(std::move(pipe.handle0), 0));
+ src.FormBody()->AppendBlob(uuid, blob_data_handle);
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ ASSERT_TRUE(dest.FormBody());
+ ASSERT_EQ(1u, dest.FormBody()->Elements().size());
+ const FormDataElement& e = dest.FormBody()->Elements()[0];
+ EXPECT_EQ(e.type_, FormDataElement::kDataPipe);
+ EXPECT_EQ(e.blob_uuid_, String());
+ EXPECT_TRUE(e.data_pipe_getter_);
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripDataPipeGetter) {
+ ResourceRequestBody src(EncodedFormData::Create());
+ mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter;
+ ignore_result(data_pipe_getter.InitWithNewPipeAndPassReceiver());
+ src.FormBody()->AppendDataPipe(
+ base::MakeRefCounted<blink::WrappedDataPipeGetter>(
+ std::move(data_pipe_getter)));
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ ASSERT_TRUE(dest.FormBody());
+ ASSERT_EQ(1u, dest.FormBody()->Elements().size());
+ const FormDataElement& e = dest.FormBody()->Elements()[0];
+ EXPECT_EQ(e.type_, FormDataElement::kDataPipe);
+ EXPECT_TRUE(e.data_pipe_getter_);
+}
+
+TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripStreamBody) {
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ chunked_data_pipe_getter;
+ ignore_result(chunked_data_pipe_getter.InitWithNewPipeAndPassReceiver());
+ ResourceRequestBody src(std::move(chunked_data_pipe_getter));
+
+ ResourceRequestBody dest;
+ EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
+ blink::mojom::blink::FetchAPIRequestBody>(&src, &dest));
+
+ EXPECT_FALSE(dest.FormBody());
+ ASSERT_TRUE(dest.StreamBody());
+}
+
+} // namespace
+} // namespace blink
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 9f5f505218e..f3af4183d5c 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
@@ -89,7 +89,7 @@ class PLATFORM_EXPORT FetchClientSettingsObject
virtual const InsecureNavigationsSet& GetUpgradeInsecureNavigationsSet()
const = 0;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
index 2b44f836099..01135cbc9d3 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -55,7 +55,8 @@ void FetchContext::PopulateResourceRequest(
ResourceType,
const ClientHintsPreferences&,
const FetchParameters::ResourceWidth&,
- ResourceRequest&) {}
+ ResourceRequest&,
+ const FetchInitiatorInfo&) {}
mojo::PendingReceiver<mojom::blink::WorkerTimingContainer>
FetchContext::TakePendingWorkerTimingReceiver(int request_id) {
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 1be8c54a4f6..87cf4fd2c4c 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
@@ -76,7 +76,7 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> {
virtual ~FetchContext() = default;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
virtual void AddAdditionalRequestHeaders(ResourceRequest&);
@@ -114,7 +114,8 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> {
const KURL&,
const ResourceLoaderOptions&,
ReportingDisposition,
- ResourceRequest::RedirectStatus) const {
+ const base::Optional<ResourceRequest::RedirectInfo>& redirect_info)
+ const {
return ResourceRequestBlockedReason::kOther;
}
virtual base::Optional<ResourceRequestBlockedReason> CheckCSPForRequest(
@@ -123,6 +124,7 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> {
const KURL&,
const ResourceLoaderOptions&,
ReportingDisposition,
+ const KURL& url_before_redirects,
ResourceRequest::RedirectStatus) const {
return ResourceRequestBlockedReason::kOther;
}
@@ -133,7 +135,8 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> {
virtual void PopulateResourceRequest(ResourceType,
const ClientHintsPreferences&,
const FetchParameters::ResourceWidth&,
- ResourceRequest&);
+ ResourceRequest&,
+ const FetchInitiatorInfo&);
// Called when the underlying context is detached. Note that some
// FetchContexts continue working after detached (e.g., for fetch() operations
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 90f489445b7..5b5a5363894 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(Visitor* visitor) {
+void MemoryCacheEntry::Trace(Visitor* visitor) const {
visitor->template RegisterWeakCallbackMethod<
MemoryCacheEntry, &MemoryCacheEntry::ClearResourceWeak>(this);
}
@@ -93,7 +93,7 @@ MemoryCache::MemoryCache(
MemoryCache::~MemoryCache() = default;
-void MemoryCache::Trace(Visitor* visitor) {
+void MemoryCache::Trace(Visitor* visitor) const {
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 9ade4a0c74a..422f7c62006 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(Visitor*);
+ void Trace(Visitor*) const;
Resource* GetResource() const { return resource_; }
private:
@@ -72,7 +72,7 @@ class PLATFORM_EXPORT MemoryCache final : public GarbageCollected<MemoryCache>,
explicit MemoryCache(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
~MemoryCache() override;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
struct TypeStatistic {
STACK_ALLOCATED();
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 3a93692ce8b..5ee3444f91f 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
@@ -29,7 +29,7 @@ NullResourceFetcherProperties::NullResourceFetcherProperties()
mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone,
FetchClientSettingsObject::InsecureNavigationsSet())) {}
-void NullResourceFetcherProperties::Trace(Visitor* visitor) {
+void NullResourceFetcherProperties::Trace(Visitor* visitor) const {
visitor->Trace(fetch_client_settings_object_);
ResourceFetcherProperties::Trace(visitor);
}
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h b/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
index 0a2433b1e0f..893e4b2c0b7 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.h
@@ -20,7 +20,7 @@ class PLATFORM_EXPORT NullResourceFetcherProperties final
NullResourceFetcherProperties();
~NullResourceFetcherProperties() override = default;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
// ResourceFetcherProperties implementation
const FetchClientSettingsObject& GetFetchClientSettingsObject()
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 e4725807ea0..a713c39c685 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
@@ -148,7 +148,7 @@ class RawResource::PreloadBytesConsumerClient final
String DebugName() const override { return "PreloadBytesConsumerClient"; }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(bytes_consumer_);
visitor->Trace(resource_);
visitor->Trace(client_);
@@ -242,7 +242,7 @@ scoped_refptr<BlobDataHandle> RawResource::DownloadedBlob() const {
return downloaded_blob_;
}
-void RawResource::Trace(Visitor* visitor) {
+void RawResource::Trace(Visitor* visitor) const {
visitor->Trace(bytes_consumer_for_preload_);
Resource::Trace(visitor);
}
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 f6d5e482e33..760b6f9704a 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
@@ -94,7 +94,7 @@ class PLATFORM_EXPORT RawResource final : public Resource {
scoped_refptr<BlobDataHandle> DownloadedBlob() const;
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
protected:
CachedMetadataHandler* CreateCachedMetadataHandler(
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 f4085622ad2..af378f81f7c 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
@@ -124,7 +124,9 @@ class DummyClient final : public GarbageCollected<DummyClient>,
return number_of_redirects_received_;
}
const Vector<char>& Data() { return data_; }
- void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); }
+ void Trace(Visitor* visitor) const override {
+ RawResourceClient::Trace(visitor);
+ }
private:
bool called_;
@@ -160,7 +162,7 @@ class AddingClient final : public GarbageCollected<AddingClient>,
void RemoveClient() { resource_->RemoveClient(dummy_client_); }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(dummy_client_);
visitor->Trace(resource_);
RawResourceClient::Trace(visitor);
@@ -205,7 +207,7 @@ class RemovingClient : public GarbageCollected<RemovingClient>,
resource->RemoveClient(this);
}
String DebugName() const override { return "RemovingClient"; }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(dummy_client_);
RawResourceClient::Trace(visitor);
}
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 08c2b41d79e..d5290152785 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -173,7 +173,7 @@ Resource::~Resource() {
InstanceCounters::DecrementCounter(InstanceCounters::kResourceCounter);
}
-void Resource::Trace(Visitor* visitor) {
+void Resource::Trace(Visitor* visitor) const {
visitor->Trace(loader_);
visitor->Trace(cache_handler_);
visitor->Trace(clients_);
@@ -233,7 +233,7 @@ void Resource::CheckResourceIntegrity() {
}
void Resource::NotifyFinished() {
- CHECK(IsFinishedInternal());
+ CHECK(IsLoaded());
ResourceClientWalker<ResourceClient> w(clients_);
while (ResourceClient* c = w.Next()) {
@@ -577,7 +577,7 @@ void Resource::DidAddClient(ResourceClient* client) {
}
if (!HasClient(client))
return;
- if (IsFinishedInternal()) {
+ if (IsLoaded()) {
client->SetHasFinishedFromMemoryCache();
client->NotifyFinished(this);
if (clients_.Contains(client)) {
@@ -650,12 +650,6 @@ void Resource::AddFinishObserver(ResourceFinishObserver* client,
WillAddClientOrObserver();
finish_observers_.insert(client);
- // Despite these being "Finish" observers, what they actually care about is
- // whether the resource is "Loaded", not "Finished" (e.g. link onload). Hence
- // we check IsLoaded directly here, rather than IsFinishedInternal.
- //
- // TODO(leszeks): Either rename FinishObservers to LoadedObservers, or the
- // NotifyFinished method of ResourceClient to NotifyProcessed (or similar).
if (IsLoaded())
TriggerNotificationForFinishObservers(task_runner);
}
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 70f2b412cb8..aef9af9c8cf 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -62,6 +62,7 @@ class Clock;
namespace blink {
+class BlobDataHandle;
class CachedMetadataHandler;
class CachedMetadataSender;
class FetchParameters;
@@ -147,7 +148,7 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>,
~Resource() override;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); }
virtual void AppendData(const char*, size_t);
@@ -411,18 +412,6 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>,
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
- // NotifyFinished.
- //
- // By default this is the same as being loaded (i.e. no processing), but it is
- // used by ScriptResource to signal that streaming JavaScript compilation
- // completed. Note that classes overloading this method should also overload
- // NotifyFinished to not call Resource::NotifyFinished until this value
- // becomes true.
- // TODO(hiroshige): Remove this when ScriptResourceContent is introduced.
- virtual bool IsFinishedInternal() const { return IsLoaded(); }
-
virtual void NotifyDataReceived(const char* data, size_t size);
virtual void NotifyFinished();
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.cc
index cc5b402427a..b33bb8fc1c3 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.cc
@@ -29,7 +29,7 @@
namespace blink {
-void ResourceClient::Trace(Visitor* visitor) {
+void ResourceClient::Trace(Visitor* visitor) const {
visitor->Trace(resource_);
}
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 a4ea28e5e0f..026754807d8 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
@@ -70,7 +70,7 @@ class PLATFORM_EXPORT ResourceClient : public GarbageCollectedMixin {
// Name for debugging, e.g. shown in memory-infra.
virtual String DebugName() const = 0;
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
protected:
void ClearResource() { SetResource(nullptr, nullptr); }
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 efee303d37e..899a9d8db19 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
@@ -110,18 +110,6 @@ ResourceError::ResourceError(const WebURLError& error)
InitializeDescription();
}
-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_;
- error_copy.trust_token_operation_error_ = trust_token_operation_error_;
- return error_copy;
-}
-
ResourceError::operator WebURLError() const {
WebURLError::HasCopyInCache has_copy_in_cache =
has_copy_in_cache_ ? WebURLError::HasCopyInCache::kTrue
@@ -202,7 +190,6 @@ bool ResourceError::ShouldCollapseInitiator() const {
}
namespace {
-
blink::ResourceRequestBlockedReason
BlockedByResponseReasonToResourceRequestBlockedReason(
network::mojom::BlockedByResponseReason reason) {
@@ -227,8 +214,8 @@ BlockedByResponseReasonToResourceRequestBlockedReason(
NOTREACHED();
return blink::ResourceRequestBlockedReason::kOther;
}
-
} // namespace
+
base::Optional<ResourceRequestBlockedReason>
ResourceError::GetResourceRequestBlockedReason() const {
if (error_code_ != net::ERR_BLOCKED_BY_CLIENT &&
@@ -242,6 +229,15 @@ ResourceError::GetResourceRequestBlockedReason() const {
return static_cast<ResourceRequestBlockedReason>(extended_error_code_);
}
+base::Optional<network::mojom::BlockedByResponseReason>
+ResourceError::GetBlockedByResponseReason() const {
+ if (error_code_ != net::ERR_BLOCKED_BY_CLIENT &&
+ error_code_ != net::ERR_BLOCKED_BY_RESPONSE) {
+ return base::nullopt;
+ }
+ return blocked_by_response_reason_;
+}
+
namespace {
String DescriptionForBlockedByClientOrResponse(int error, int extended_error) {
if (extended_error == 0)
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 b5e3030a4e1..238efe12f31 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
@@ -71,10 +71,6 @@ class PLATFORM_EXPORT ResourceError final {
const network::CorsErrorStatus& status);
ResourceError(const WebURLError&);
- // Makes a deep copy. Useful for when you need to use a ResourceError on
- // another thread.
- ResourceError Copy() const;
-
int ErrorCode() const { return error_code_; }
const String& FailingURL() const { return failing_url_; }
const String& LocalizedDescription() const { return localized_description_; }
@@ -95,6 +91,8 @@ class PLATFORM_EXPORT ResourceError final {
bool ShouldCollapseInitiator() const;
base::Optional<ResourceRequestBlockedReason> GetResourceRequestBlockedReason()
const;
+ base::Optional<network::mojom::BlockedByResponseReason>
+ GetBlockedByResponseReason() const;
base::Optional<network::CorsErrorStatus> CorsErrorStatus() const {
return cors_error_status_;
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 73291aca6fa..6f014b84c7d 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
@@ -98,6 +98,10 @@ namespace {
constexpr base::TimeDelta kKeepaliveLoadersTimeout =
base::TimeDelta::FromSeconds(30);
+// Timeout for link preloads to be used after window.onload
+static constexpr base::TimeDelta kUnusedPreloadTimeout =
+ base::TimeDelta::FromSeconds(3);
+
#define RESOURCE_HISTOGRAM_PREFIX "Blink.MemoryCache.RevalidationPolicy."
#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \
@@ -315,9 +319,9 @@ void PopulateAndAddResourceTimingInfo(Resource* resource,
base::TimeTicks response_end,
int64_t encoded_data_length) {
info->SetInitialURL(
- resource->GetResourceRequest().GetInitialUrlForResourceTiming().IsNull()
- ? resource->GetResourceRequest().Url()
- : resource->GetResourceRequest().GetInitialUrlForResourceTiming());
+ resource->GetResourceRequest().GetRedirectInfo().has_value()
+ ? resource->GetResourceRequest().GetRedirectInfo()->original_url
+ : resource->GetResourceRequest().Url());
info->SetFinalResponse(resource->GetResponse());
info->SetLoadResponseEnd(response_end);
// encodedDataLength == -1 means "not available".
@@ -647,12 +651,12 @@ void ResourceFetcher::DidLoadResourceFromMemoryCache(
scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
resource->Options().initiator_info.name, base::TimeTicks::Now(),
request.GetRequestContext(), request.GetRequestDestination());
- // TODO(yoav): GetInitialUrlForResourceTiming() is only needed until
- // Out-of-Blink CORS lands: https://crbug.com/736308
+ // TODO(yoav): Getting the original URL before redirects here is only needed
+ // until Out-of-Blink CORS lands: https://crbug.com/736308
info->SetInitialURL(
- resource->GetResourceRequest().GetInitialUrlForResourceTiming().IsNull()
- ? resource->GetResourceRequest().Url()
- : resource->GetResourceRequest().GetInitialUrlForResourceTiming());
+ resource->GetResourceRequest().GetRedirectInfo().has_value()
+ ? resource->GetResourceRequest().GetRedirectInfo()->original_url
+ : resource->GetResourceRequest().Url());
ResourceResponse final_response = resource->GetResponse();
final_response.SetResourceLoadTiming(nullptr);
info->SetFinalResponse(final_response);
@@ -837,24 +841,33 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest(
? ReportingDisposition::kSuppressReporting
: ReportingDisposition::kReport;
- // Note that resource_request.GetRedirectStatus() may return kFollowedRedirect
- // here since e.g. ThreadableLoader may create a new Resource from
- // a ResourceRequest that originates from the ResourceRequest passed to
- // the redirect handling callback.
+ // Note that resource_request.GetRedirectInfo() may be non-null here since
+ // e.g. ThreadableLoader may create a new Resource from a ResourceRequest that
+ // originates from the ResourceRequest passed to the redirect handling
+ // callback.
// Before modifying the request for CSP, evaluate report-only headers. This
// allows site owners to learn about requests that are being modified
// (e.g. mixed content that is being upgraded by upgrade-insecure-requests).
+ const base::Optional<ResourceRequest::RedirectInfo>& redirect_info =
+ resource_request.GetRedirectInfo();
+ const KURL& url_before_redirects =
+ redirect_info ? redirect_info->original_url : params.Url();
+ const ResourceRequestHead::RedirectStatus redirect_status =
+ redirect_info ? ResourceRequestHead::RedirectStatus::kFollowedRedirect
+ : ResourceRequestHead::RedirectStatus::kNoRedirect;
Context().CheckCSPForRequest(
resource_request.GetRequestContext(),
resource_request.GetRequestDestination(),
MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), options,
- reporting_disposition, resource_request.GetRedirectStatus());
+ reporting_disposition,
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(url_before_redirects),
+ redirect_status);
// This may modify params.Url() (via the resource_request argument).
Context().PopulateResourceRequest(
resource_type, params.GetClientHintsPreferences(),
- params.GetResourceWidth(), resource_request);
+ params.GetResourceWidth(), resource_request, options.initiator_info);
if (!params.Url().IsValid())
return ResourceRequestBlockedReason::kOther;
@@ -911,7 +924,7 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest(
base::Optional<ResourceRequestBlockedReason> blocked_reason =
Context().CanRequest(resource_type, resource_request, url, options,
reporting_disposition,
- resource_request.GetRedirectStatus());
+ resource_request.GetRedirectInfo());
if (Context().CalculateIfAdSubresource(resource_request, resource_type,
options.initiator_info))
@@ -1547,7 +1560,7 @@ ResourceFetcher::DetermineRevalidationPolicyInternal(
// Don't reuse resources with Cache-control: no-store.
if (existing_resource.HasCacheControlNoStoreHeader()) {
return {RevalidationPolicy::kReload,
- "Reload due to cache-control: no-sotre."};
+ "Reload due to cache-control: no-store."};
}
// During the initial load, avoid loading the same resource multiple times for
@@ -1705,6 +1718,8 @@ void ResourceFetcher::ClearContext() {
console_logger_->Detach();
loader_factory_ = nullptr;
+ unused_preloads_timer_.Cancel();
+
// Make sure the only requests still going are keepalive requests.
// Callers of ClearContext() should be calling StopFetching() prior
// to this, but it's possible for additional requests to start during
@@ -1767,14 +1782,32 @@ void ResourceFetcher::ClearPreloads(ClearPreloadsPolicy policy) {
matched_preloads_.clear();
}
-Vector<KURL> ResourceFetcher::GetUrlsOfUnusedPreloads() {
- Vector<KURL> urls;
+void ResourceFetcher::ScheduleWarnUnusedPreloads() {
+ // If preloads_ is not empty here, it's full of link
+ // preloads, as speculative preloads should have already been cleared when
+ // parsing finished.
+ if (preloads_.IsEmpty())
+ return;
+ unused_preloads_timer_ = PostDelayedCancellableTask(
+ *task_runner_, FROM_HERE,
+ WTF::Bind(&ResourceFetcher::WarnUnusedPreloads, WrapWeakPersistent(this)),
+ kUnusedPreloadTimeout);
+}
+
+void ResourceFetcher::WarnUnusedPreloads() {
for (const auto& pair : preloads_) {
Resource* resource = pair.value;
- if (resource && resource->IsLinkPreload() && resource->IsUnusedPreload())
- urls.push_back(resource->Url());
+ if (!resource || !resource->IsLinkPreload() || !resource->IsUnusedPreload())
+ continue;
+ String message =
+ "The resource " + resource->Url().GetString() + " was preloaded " +
+ "using link preload but not used within a few seconds from the " +
+ "window's load event. Please make sure it has an appropriate `as` " +
+ "value and it is preloaded intentionally.";
+ console_logger_->AddConsoleMessage(
+ mojom::blink::ConsoleMessageSource::kJavaScript,
+ mojom::blink::ConsoleMessageLevel::kWarning, message);
}
- return urls;
}
void ResourceFetcher::HandleLoaderFinish(Resource* resource,
@@ -2082,7 +2115,7 @@ void ResourceFetcher::EmulateLoadStartedForInspector(
Context().CanRequest(resource->GetType(), last_resource_request,
last_resource_request.Url(), params.Options(),
ReportingDisposition::kReport,
- last_resource_request.GetRedirectStatus());
+ last_resource_request.GetRedirectInfo());
DidLoadResourceFromMemoryCache(resource, params.GetResourceRequest(),
false /* is_static_data */);
}
@@ -2162,7 +2195,7 @@ FrameOrWorkerScheduler* ResourceFetcher::GetFrameOrWorkerScheduler() {
return frame_or_worker_scheduler_.get();
}
-void ResourceFetcher::Trace(Visitor* visitor) {
+void ResourceFetcher::Trace(Visitor* visitor) const {
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 252c777bec6..1e52ae18bc8 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
@@ -87,7 +87,7 @@ class PLATFORM_EXPORT ResourceFetcher
public:
virtual ~LoaderFactory() = default;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
// Create a WebURLLoader for given the request information and task runner.
virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
@@ -103,7 +103,7 @@ class PLATFORM_EXPORT ResourceFetcher
// in ResourceFetcherInit to ensure correctness of this ResourceFetcher.
explicit ResourceFetcher(const ResourceFetcherInit&);
virtual ~ResourceFetcher();
- virtual void Trace(Visitor*);
+ virtual void Trace(Visitor*) const;
// - This function returns the same object throughout this fetcher's
// entire life.
@@ -198,7 +198,7 @@ class PLATFORM_EXPORT ResourceFetcher
int CountPreloads() const { return preloads_.size(); }
void ClearPreloads(ClearPreloadsPolicy = kClearAllPreloads);
- Vector<KURL> GetUrlsOfUnusedPreloads();
+ void ScheduleWarnUnusedPreloads();
MHTMLArchive* Archive() const { return archive_.Get(); }
@@ -385,6 +385,8 @@ class PLATFORM_EXPORT ResourceFetcher
void ScheduleStaleRevalidate(Resource* stale_resource);
void RevalidateStaleResource(Resource* stale_resource);
+ void WarnUnusedPreloads();
+
Member<DetachableResourceFetcherProperties> properties_;
Member<ResourceLoadObserver> resource_load_observer_;
Member<FetchContext> context_;
@@ -410,6 +412,8 @@ class PLATFORM_EXPORT ResourceFetcher
TaskRunnerTimer<ResourceFetcher> resource_timing_report_timer_;
+ TaskHandle unused_preloads_timer_;
+
using ResourceTimingInfoMap =
HeapHashMap<Member<Resource>, scoped_refptr<ResourceTimingInfo>>;
ResourceTimingInfoMap resource_timing_info_map_;
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
index 382d1de0c5b..6b4603a91dd 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.cc
@@ -29,7 +29,7 @@ void DetachableResourceFetcherProperties::Detach() {
properties_ = nullptr;
}
-void DetachableResourceFetcherProperties::Trace(Visitor* visitor) {
+void DetachableResourceFetcherProperties::Trace(Visitor* visitor) const {
visitor->Trace(properties_);
visitor->Trace(fetch_client_settings_object_);
ResourceFetcherProperties::Trace(visitor);
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
index 99ff28be578..cbb33a18d14 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h
@@ -40,7 +40,7 @@ class PLATFORM_EXPORT ResourceFetcherProperties
ResourceFetcherProperties() = default;
virtual ~ResourceFetcherProperties() = default;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
// Returns the client settings object bound to this global context.
virtual const FetchClientSettingsObject& GetFetchClientSettingsObject()
@@ -103,7 +103,7 @@ class PLATFORM_EXPORT DetachableResourceFetcherProperties final
void Detach();
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
// ResourceFetcherProperties implementation
// Add a test in resource_fetcher_test.cc when you change behaviors.
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 980d15a3fd0..a75f632fef6 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
@@ -83,19 +83,6 @@ constexpr char kTestResourceFilename[] = "white-1x1.png";
constexpr char kTestResourceMimeType[] = "image/png";
constexpr uint32_t kTestResourceSize = 103; // size of white-1x1.png
-void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
- const WebString& file_path,
- WebURLResponse response) {
- url_test_helpers::RegisterMockedURLLoadWithCustomResponse(full_url, file_path,
- response);
-}
-
-void RegisterMockedURLLoad(const KURL& url) {
- url_test_helpers::RegisterMockedURLLoad(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- kTestResourceMimeType);
-}
-
const FetchClientSettingsObjectSnapshot& CreateFetchClientSettingsObject(
network::mojom::IPAddressSpace address_space) {
return *MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
@@ -178,6 +165,7 @@ class ResourceFetcherTest : public testing::Test {
scoped_refptr<scheduler::FakeTaskRunner> CreateTaskRunner() {
return base::MakeRefCounted<scheduler::FakeTaskRunner>();
}
+
ResourceFetcher* CreateFetcher(
const TestResourceFetcherProperties& properties,
FetchContext* context) {
@@ -185,18 +173,27 @@ class ResourceFetcherTest : public testing::Test {
properties.MakeDetachable(), context, CreateTaskRunner(),
MakeGarbageCollected<TestLoaderFactory>()));
}
+
ResourceFetcher* CreateFetcher(
const TestResourceFetcherProperties& properties) {
return CreateFetcher(properties, MakeGarbageCollected<MockFetchContext>());
}
+
ResourceFetcher* CreateFetcher() {
return CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>());
}
+
void AddResourceToMemoryCache(Resource* resource) {
GetMemoryCache()->Add(resource);
}
+ void RegisterMockedURLLoad(const KURL& url) {
+ url_test_helpers::RegisterMockedURLLoad(
+ url, test::PlatformTestDataPath(kTestResourceFilename),
+ kTestResourceMimeType, platform_->GetURLLoaderMockFactory());
+ }
+
ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
private:
@@ -231,9 +228,9 @@ TEST_F(ResourceFetcherTest, UseExistingResource) {
ResourceResponse response(url);
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params{ResourceRequest(url)};
Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
@@ -360,9 +357,9 @@ TEST_F(ResourceFetcherTest, VaryResource) {
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kVary, "*");
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params_original{ResourceRequest(url)};
Resource* resource =
@@ -410,7 +407,9 @@ class RequestSameResourceOnComplete
}
bool NotifyFinishedCalled() const { return notify_finished_called_; }
- void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); }
+ void Trace(Visitor* visitor) const override {
+ RawResourceClient::Trace(visitor);
+ }
String DebugName() const override { return "RequestSameResourceOnComplete"; }
@@ -428,9 +427,9 @@ TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) {
response.SetHttpStatusCode(200);
response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600");
response.SetHttpHeaderField(http_names::kETag, "1234567890");
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
ResourceFetcher* fetcher1 = CreateFetcher(
*MakeGarbageCollected<TestResourceFetcherProperties>(source_origin));
@@ -469,8 +468,11 @@ class ServeRequestsOnCompleteClient final
USING_GARBAGE_COLLECTED_MIXIN(ServeRequestsOnCompleteClient);
public:
+ explicit ServeRequestsOnCompleteClient(WebURLLoaderMockFactory* mock_factory)
+ : mock_factory_(mock_factory) {}
+
void NotifyFinished(Resource*) override {
- url_test_helpers::ServeAsynchronousRequests();
+ mock_factory_->ServeAsynchronousRequests();
ClearResource();
}
@@ -494,9 +496,14 @@ class ServeRequestsOnCompleteClient final
}
void DataDownloaded(Resource*, uint64_t) override { ASSERT_TRUE(false); }
- void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); }
+ void Trace(Visitor* visitor) const override {
+ RawResourceClient::Trace(visitor);
+ }
String DebugName() const override { return "ServeRequestsOnCompleteClient"; }
+
+ private:
+ WebURLLoaderMockFactory* mock_factory_;
};
// Regression test for http://crbug.com/594072.
@@ -513,7 +520,8 @@ TEST_F(ResourceFetcherTest, ResponseOnCancel) {
resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
FetchParameters fetch_params(std::move(resource_request));
Persistent<ServeRequestsOnCompleteClient> client =
- MakeGarbageCollected<ServeRequestsOnCompleteClient>();
+ MakeGarbageCollected<ServeRequestsOnCompleteClient>(
+ platform_->GetURLLoaderMockFactory());
Resource* resource = RawResource::Fetch(fetch_params, fetcher, client);
resource->Loader()->Cancel();
}
@@ -523,9 +531,12 @@ class ScopedMockRedirectRequester {
public:
ScopedMockRedirectRequester(
+ WebURLLoaderMockFactory* mock_factory,
MockFetchContext* context,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
- : context_(context), task_runner_(std::move(task_runner)) {}
+ : mock_factory_(mock_factory),
+ context_(context),
+ task_runner_(std::move(task_runner)) {}
void RegisterRedirect(const WebString& from_url, const WebString& to_url) {
KURL redirect_url(from_url);
@@ -535,13 +546,13 @@ class ScopedMockRedirectRequester {
redirect_response.SetHttpHeaderField(http_names::kLocation, to_url);
redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes);
- RegisterMockedURLLoadWithCustomResponse(redirect_url, "",
- redirect_response);
+ mock_factory_->RegisterURL(redirect_url, redirect_response, "");
}
void RegisterFinalResource(const WebString& url) {
- KURL final_url(url);
- RegisterMockedURLLoad(final_url);
+ url_test_helpers::RegisterMockedURLLoad(
+ KURL(url), test::PlatformTestDataPath(kTestResourceFilename),
+ kTestResourceMimeType, mock_factory_);
}
void Request(const WebString& url) {
@@ -553,10 +564,11 @@ class ScopedMockRedirectRequester {
resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL);
FetchParameters fetch_params(std::move(resource_request));
RawResource::Fetch(fetch_params, fetcher, nullptr);
- url_test_helpers::ServeAsynchronousRequests();
+ mock_factory_->ServeAsynchronousRequests();
}
private:
+ WebURLLoaderMockFactory* mock_factory_;
MockFetchContext* context_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -567,7 +579,8 @@ TEST_F(ResourceFetcherTest, SameOriginRedirect) {
const char kRedirectURL[] = "http://127.0.0.1:8000/redirect.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
- ScopedMockRedirectRequester requester(context, CreateTaskRunner());
+ ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
+ context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL, kFinalURL);
requester.RegisterFinalResource(kFinalURL);
requester.Request(kRedirectURL);
@@ -580,7 +593,8 @@ TEST_F(ResourceFetcherTest, CrossOriginRedirect) {
const char kRedirectURL[] = "http://otherorigin.test/redirect.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
- ScopedMockRedirectRequester requester(context, CreateTaskRunner());
+ ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
+ context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL, kFinalURL);
requester.RegisterFinalResource(kFinalURL);
requester.Request(kRedirectURL);
@@ -594,7 +608,8 @@ TEST_F(ResourceFetcherTest, ComplexCrossOriginRedirect) {
const char kRedirectURL3[] = "http://127.0.0.1:8000/redirect3.html";
const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
MockFetchContext* context = MakeGarbageCollected<MockFetchContext>();
- ScopedMockRedirectRequester requester(context, CreateTaskRunner());
+ ScopedMockRedirectRequester requester(platform_->GetURLLoaderMockFactory(),
+ context, CreateTaskRunner());
requester.RegisterRedirect(kRedirectURL1, kRedirectURL2);
requester.RegisterRedirect(kRedirectURL2, kRedirectURL3);
requester.RegisterRedirect(kRedirectURL3, kFinalURL);
@@ -923,9 +938,9 @@ TEST_F(ResourceFetcherTest, ContentIdURL) {
KURL url("cid:0123456789@example.com");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
auto* fetcher = CreateFetcher();
@@ -959,9 +974,9 @@ TEST_F(ResourceFetcherTest, StaleWhileRevalidate) {
response.SetHttpHeaderField(http_names::kCacheControl,
"max-age=0, stale-while-revalidate=40");
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_TRUE(resource);
@@ -983,9 +998,9 @@ TEST_F(ResourceFetcherTest, StaleWhileRevalidate) {
ResourceResponse revalidate_response(url);
revalidate_response.SetHttpStatusCode(200);
platform_->GetURLLoaderMockFactory()->UnregisterURL(url);
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(revalidate_response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(revalidate_response),
+ test::PlatformTestDataPath(kTestResourceFilename));
new_resource = MockResource::Fetch(fetch_params2, fetcher, nullptr);
EXPECT_EQ(resource, new_resource);
EXPECT_TRUE(GetMemoryCache()->Contains(resource));
@@ -1007,9 +1022,9 @@ TEST_F(ResourceFetcherTest, CachedResourceShouldNotCrashByNullURL) {
KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHttpStatusCode(200);
- RegisterMockedURLLoadWithCustomResponse(
- url, test::PlatformTestDataPath(kTestResourceFilename),
- WrappedResourceResponse(response));
+ platform_->GetURLLoaderMockFactory()->RegisterURL(
+ url, WrappedResourceResponse(response),
+ test::PlatformTestDataPath(kTestResourceFilename));
FetchParameters fetch_params{ResourceRequest(url)};
MockResource::Fetch(fetch_params, fetcher, nullptr);
ASSERT_NE(fetcher->CachedResource(url), nullptr);
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 376800c68cd..6de0a227fbf 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
@@ -31,7 +31,7 @@ class PLATFORM_EXPORT ResourceFinishObserver
// Name for debugging
virtual String DebugName() const = 0;
- virtual void Trace(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
index 16104ff9310..698f8edd04d 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
@@ -96,7 +96,7 @@ class PLATFORM_EXPORT ResourceLoadObserver
int64_t encoded_data_length,
IsInternalRequest) = 0;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
};
} // 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 b73ea743cc7..3c712242b3f 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
@@ -105,7 +105,7 @@ ResourceLoadScheduler::ResourceLoadScheduler(
ResourceLoadScheduler::~ResourceLoadScheduler() = default;
-void ResourceLoadScheduler::Trace(Visitor* visitor) {
+void ResourceLoadScheduler::Trace(Visitor* visitor) const {
visitor->Trace(pending_request_map_);
visitor->Trace(resource_fetcher_properties_);
visitor->Trace(console_logger_);
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 7663076d189..df37d766d50 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
// ResourceLoadScheduler provides a unified per-frame infrastructure to schedule
@@ -170,7 +170,7 @@ class PLATFORM_EXPORT ResourceLoadScheduler final
DetachableConsoleLogger& console_logger);
~ResourceLoadScheduler() override;
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
// Changes the policy from |kTight| to |kNormal|. This function can be called
// multiple times, and does nothing when the scheduler is already working with
@@ -261,7 +261,7 @@ class PLATFORM_EXPORT ResourceLoadScheduler final
priority(priority),
intra_priority(intra_priority) {}
- void Trace(Visitor* visitor) { visitor->Trace(client); }
+ void Trace(Visitor* visitor) const { visitor->Trace(client); }
Member<ResourceLoadSchedulerClient> client;
ThrottleOption option;
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 d5bfad70b7a..d87179dd0fe 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(Visitor* visitor) { visitor->Trace(client_order_); }
+ void Trace(Visitor* visitor) const { 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(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
ResourceLoadSchedulerClient::Trace(visitor);
visitor->Trace(console_logger_);
}
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 d09e6c051fb..e285beac7c2 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
@@ -11,23 +11,26 @@ namespace blink {
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)
+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 worker_fetch_start,
+ base::TimeTicks worker_respond_with_settled,
+ 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),
@@ -37,6 +40,8 @@ ResourceLoadTiming::ResourceLoadTiming(base::TimeTicks request_time,
connect_end_(connect_end),
worker_start_(worker_start),
worker_ready_(worker_ready),
+ worker_fetch_start_(worker_fetch_start),
+ worker_respond_with_settled_(worker_respond_with_settled),
send_start_(send_start),
send_end_(send_end),
receive_headers_start_(receive_headers_start),
@@ -61,7 +66,9 @@ scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::FromMojo(
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->service_worker_ready_time,
+ mojo_timing->service_worker_fetch_start,
+ mojo_timing->service_worker_respond_with_settled, 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,
@@ -76,7 +83,9 @@ network::mojom::blink::LoadTimingInfoPtr ResourceLoadTiming::ToMojo() const {
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_);
+ /*first_early_hints_time=*/base::TimeTicks::Now(), push_start_,
+ push_end_, worker_start_, worker_ready_, worker_fetch_start_,
+ worker_respond_with_settled_);
return timing;
}
@@ -88,6 +97,8 @@ bool ResourceLoadTiming::operator==(const ResourceLoadTiming& other) const {
connect_end_ == other.connect_end_ &&
worker_start_ == other.worker_start_ &&
worker_ready_ == other.worker_ready_ &&
+ worker_fetch_start_ == other.worker_fetch_start_ &&
+ worker_respond_with_settled_ == other.worker_respond_with_settled_ &&
send_start_ == other.send_start_ && send_end_ == other.send_end_ &&
receive_headers_start_ == other.receive_headers_start_ &&
receive_headers_end_ == other.receive_headers_end_ &&
@@ -135,6 +146,16 @@ void ResourceLoadTiming::SetWorkerReady(base::TimeTicks worker_ready) {
worker_ready_ = worker_ready;
}
+void ResourceLoadTiming::SetWorkerFetchStart(
+ base::TimeTicks worker_fetch_start) {
+ worker_fetch_start_ = worker_fetch_start;
+}
+
+void ResourceLoadTiming::SetWorkerRespondWithSettled(
+ base::TimeTicks worker_respond_with_settled) {
+ worker_respond_with_settled_ = worker_respond_with_settled;
+}
+
void ResourceLoadTiming::SetSendStart(base::TimeTicks send_start) {
TRACE_EVENT_MARK_WITH_TIMESTAMP0("blink.user_timing", "requestStart",
send_start);
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 b87da1d183f..0e0de9534d1 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
@@ -54,6 +54,8 @@ class PLATFORM_EXPORT ResourceLoadTiming
void SetConnectEnd(base::TimeTicks);
void SetWorkerStart(base::TimeTicks);
void SetWorkerReady(base::TimeTicks);
+ void SetWorkerFetchStart(base::TimeTicks);
+ void SetWorkerRespondWithSettled(base::TimeTicks);
void SetSendStart(base::TimeTicks);
void SetSendEnd(base::TimeTicks);
void SetReceiveHeadersStart(base::TimeTicks);
@@ -72,6 +74,10 @@ class PLATFORM_EXPORT ResourceLoadTiming
base::TimeTicks ConnectEnd() const { return connect_end_; }
base::TimeTicks WorkerStart() const { return worker_start_; }
base::TimeTicks WorkerReady() const { return worker_ready_; }
+ base::TimeTicks WorkerFetchStart() const { return worker_fetch_start_; }
+ base::TimeTicks WorkerRespondWithSettled() const {
+ return worker_respond_with_settled_;
+ }
base::TimeTicks SendStart() const { return send_start_; }
base::TimeTicks SendEnd() const { return send_end_; }
base::TimeTicks ReceiveHeadersStart() const { return receive_headers_start_; }
@@ -94,6 +100,8 @@ class PLATFORM_EXPORT ResourceLoadTiming
base::TimeTicks connect_end,
base::TimeTicks worker_start,
base::TimeTicks worker_ready,
+ base::TimeTicks worker_fetch_start,
+ base::TimeTicks worker_respond_with_settled,
base::TimeTicks send_start,
base::TimeTicks send_end,
base::TimeTicks receive_headers_start,
@@ -123,6 +131,8 @@ class PLATFORM_EXPORT ResourceLoadTiming
base::TimeTicks connect_end_;
base::TimeTicks worker_start_;
base::TimeTicks worker_ready_;
+ base::TimeTicks worker_fetch_start_;
+ base::TimeTicks worker_respond_with_settled_;
base::TimeTicks send_start_;
base::TimeTicks send_end_;
base::TimeTicks receive_headers_start_;
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 cd2ec6c9b14..40e9601c5fb 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
@@ -418,7 +418,7 @@ ResourceLoader::ResourceLoader(ResourceFetcher* fetcher,
ResourceLoader::~ResourceLoader() = default;
-void ResourceLoader::Trace(Visitor* visitor) {
+void ResourceLoader::Trace(Visitor* visitor) const {
visitor->Trace(fetcher_);
visitor->Trace(scheduler_);
visitor->Trace(resource_);
@@ -749,6 +749,8 @@ bool ResourceLoader::WillFollowRedirect(
const ResourceResponse& redirect_response(
passed_redirect_response.ToResourceResponse());
+ const KURL& url_before_redirects = initial_request.Url();
+
if (!IsManualRedirectFetchRequest(initial_request)) {
bool unused_preload = resource_->IsUnusedPreload();
@@ -761,14 +763,13 @@ bool ResourceLoader::WillFollowRedirect(
// ensure that violations are sent.
Context().CheckCSPForRequest(
request_context, request_destination, new_url, options,
- reporting_disposition,
+ reporting_disposition, url_before_redirects,
ResourceRequest::RedirectStatus::kFollowedRedirect);
base::Optional<ResourceRequestBlockedReason> blocked_reason =
- Context().CanRequest(
- resource_type, *new_request, new_url, options,
- reporting_disposition,
- ResourceRequest::RedirectStatus::kFollowedRedirect);
+ Context().CanRequest(resource_type, *new_request, new_url, options,
+ reporting_disposition,
+ new_request->GetRedirectInfo());
if (Context().CalculateIfAdSubresource(*new_request, resource_type,
options.initiator_info))
@@ -1043,18 +1044,24 @@ void ResourceLoader::DidReceiveResponseInternal(
// pre-request checks, and consider running the checks regardless of service
// worker interception.
const KURL& response_url = response.ResponseUrl();
+ const base::Optional<ResourceRequest::RedirectInfo>&
+ previous_redirect_info = request.GetRedirectInfo();
+ const KURL& original_url = previous_redirect_info
+ ? previous_redirect_info->original_url
+ : request.Url();
+ const ResourceRequest::RedirectInfo redirect_info(original_url,
+ request.Url());
// CanRequest() below only checks enforced policies: check report-only
// here to ensure violations are sent.
Context().CheckCSPForRequest(
request_context, request_destination, response_url, options,
- ReportingDisposition::kReport,
+ ReportingDisposition::kReport, original_url,
ResourceRequest::RedirectStatus::kFollowedRedirect);
base::Optional<ResourceRequestBlockedReason> blocked_reason =
- Context().CanRequest(
- resource_type, ResourceRequest(initial_request), response_url,
- options, ReportingDisposition::kReport,
- ResourceRequest::RedirectStatus::kFollowedRedirect);
+ Context().CanRequest(resource_type, ResourceRequest(initial_request),
+ response_url, options,
+ ReportingDisposition::kReport, redirect_info);
if (blocked_reason) {
HandleError(ResourceError::CancelledDueToAccessCheckError(
response_url, blocked_reason.value()));
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 2ecfb827b55..572c5025c61 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
@@ -78,7 +78,7 @@ class PLATFORM_EXPORT ResourceLoader final
ResourceRequestBody request_body = ResourceRequestBody(),
uint32_t inflight_keepalive_bytes = 0);
~ResourceLoader() override;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
void Start();
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 7d307cefc7c..c389ccf608a 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
@@ -326,7 +326,7 @@ class TestRawResourceClient final
}
String DebugName() const override { return "TestRawResourceClient"; }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(body_);
RawResourceClient::Trace(visitor);
}
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 8ee020a6df7..d08337b3203 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
@@ -76,8 +76,7 @@ ResourceRequestHead::ResourceRequestHead(const KURL& url)
referrer_policy_(network::mojom::ReferrerPolicy::kDefault),
is_external_request_(false),
cors_preflight_policy_(
- network::mojom::CorsPreflightPolicy::kConsiderPreflight),
- redirect_status_(RedirectStatus::kNoRedirect) {}
+ network::mojom::CorsPreflightPolicy::kConsiderPreflight) {}
ResourceRequestHead::ResourceRequestHead(const ResourceRequestHead&) = default;
@@ -97,16 +96,26 @@ ResourceRequestBody::ResourceRequestBody(
scoped_refptr<EncodedFormData> form_body)
: form_body_(form_body) {}
+ResourceRequestBody::ResourceRequestBody(
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ stream_body)
+ : stream_body_(std::move(stream_body)) {}
+
ResourceRequestBody::ResourceRequestBody(ResourceRequestBody&& src)
- : ResourceRequestBody(std::move(src.form_body_)) {}
+ : form_body_(std::move(src.form_body_)),
+ stream_body_(std::move(src.stream_body_)) {}
-ResourceRequestBody& ResourceRequestBody::operator=(ResourceRequestBody&& src) {
- form_body_ = std::move(src.form_body_);
- return *this;
-}
+ResourceRequestBody& ResourceRequestBody::operator=(ResourceRequestBody&& src) =
+ default;
ResourceRequestBody::~ResourceRequestBody() = default;
+void ResourceRequestBody::SetStreamBody(
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ stream_body) {
+ stream_body_ = std::move(stream_body);
+}
+
ResourceRequest::ResourceRequest() : ResourceRequestHead(NullURL()) {}
ResourceRequest::ResourceRequest(const String& url_string)
@@ -118,6 +127,8 @@ ResourceRequest::ResourceRequest(const ResourceRequestHead& head)
: ResourceRequestHead(head) {}
ResourceRequest& ResourceRequest::operator=(const ResourceRequest& src) {
+ DCHECK(!body_.StreamBody().is_valid());
+ DCHECK(!src.body_.StreamBody().is_valid());
this->ResourceRequestHead::operator=(src);
body_.SetFormBody(src.body_.FormBody());
return *this;
@@ -130,6 +141,8 @@ ResourceRequest& ResourceRequest::operator=(ResourceRequest&&) = default;
ResourceRequest::~ResourceRequest() = default;
void ResourceRequest::CopyFrom(const ResourceRequest& src) {
+ DCHECK(!body_.StreamBody().is_valid());
+ DCHECK(!src.body_.StreamBody().is_valid());
*this = src;
}
@@ -155,7 +168,8 @@ std::unique_ptr<ResourceRequest> ResourceRequestHead::CreateRedirectRequest(
request->SetReferrerString(referrer);
request->SetReferrerPolicy(new_referrer_policy);
request->SetSkipServiceWorker(skip_service_worker);
- request->SetRedirectStatus(RedirectStatus::kFollowedRedirect);
+ request->redirect_info_ = RedirectInfo(
+ redirect_info_ ? redirect_info_->original_url : Url(), Url());
// Copy from parameters for |this|.
request->SetDownloadToBlob(DownloadToBlob());
@@ -197,14 +211,6 @@ void ResourceRequestHead::SetUrl(const KURL& url) {
url_ = url;
}
-const KURL& ResourceRequestHead::GetInitialUrlForResourceTiming() const {
- return initial_url_for_resource_timing_;
-}
-
-void ResourceRequestHead::SetInitialUrlForResourceTiming(const KURL& url) {
- initial_url_for_resource_timing_ = url;
-}
-
void ResourceRequestHead::RemoveUserAndPassFromURL() {
if (url_.User().IsEmpty() && url_.Pass().IsEmpty())
return;
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 71139b093a3..10d84b66618 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
@@ -36,6 +36,7 @@
#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/chunked_data_pipe_getter.mojom-blink.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"
@@ -64,7 +65,21 @@ class PLATFORM_EXPORT ResourceRequestHead {
DISALLOW_NEW();
public:
+ // TODO: Remove this enum from here since it is not used in this class anymore
enum class RedirectStatus : uint8_t { kFollowedRedirect, kNoRedirect };
+
+ struct RedirectInfo {
+ // Original (first) url in the redirect chain.
+ KURL original_url;
+
+ // Previous url in the redirect chain.
+ KURL previous_url;
+
+ RedirectInfo() = delete;
+ RedirectInfo(const KURL& original_url, const KURL& previous_url)
+ : original_url(original_url), previous_url(previous_url) {}
+ };
+
ResourceRequestHead();
explicit ResourceRequestHead(const KURL&);
@@ -91,14 +106,6 @@ class PLATFORM_EXPORT ResourceRequestHead {
const KURL& Url() const;
void SetUrl(const KURL&);
- // ThreadableLoader sometimes breaks redirect chains into separate Resource
- // and ResourceRequests. The ResourceTiming API needs the initial URL for the
- // name attribute of PerformanceResourceTiming entries. This property
- // remembers the initial URL for that purpose. Note that it can return a null
- // URL. In that case, use Url() instead.
- const KURL& GetInitialUrlForResourceTiming() const;
- void SetInitialUrlForResourceTiming(const KURL&);
-
void RemoveUserAndPassFromURL();
mojom::FetchCacheMode GetCacheMode() const;
@@ -334,8 +341,9 @@ class PLATFORM_EXPORT ResourceRequestHead {
cors_preflight_policy_ = policy;
}
- void SetRedirectStatus(RedirectStatus status) { redirect_status_ = status; }
- RedirectStatus GetRedirectStatus() const { return redirect_status_; }
+ const base::Optional<RedirectInfo>& GetRedirectInfo() const {
+ return redirect_info_;
+ }
void SetSuggestedFilename(const base::Optional<String>& suggested_filename) {
suggested_filename_ = suggested_filename;
@@ -454,15 +462,19 @@ class PLATFORM_EXPORT ResourceRequestHead {
// |url|,
bool CanDisplay(const KURL&) const;
+ void SetAllowHTTP1ForStreamingUpload(bool allow) {
+ allowHTTP1ForStreamingUpload_ = allow;
+ }
+ bool AllowHTTP1ForStreamingUpload() const {
+ return allowHTTP1ForStreamingUpload_;
+ }
+
private:
const CacheControlHeader& GetCacheControlHeader() const;
bool NeedsHTTPOrigin() const;
KURL url_;
- // TODO(yoav): initial_url_for_resource_timing_ is a stop-gap only needed
- // until Out-of-Blink CORS lands: https://crbug.com/736308
- KURL initial_url_for_resource_timing_;
// base::TimeDelta::Max() represents the default timeout on platforms that
// have one.
base::TimeDelta timeout_interval_;
@@ -503,7 +515,7 @@ class PLATFORM_EXPORT ResourceRequestHead {
network::mojom::ReferrerPolicy referrer_policy_;
bool is_external_request_;
network::mojom::CorsPreflightPolicy cors_preflight_policy_;
- RedirectStatus redirect_status_;
+ base::Optional<RedirectInfo> redirect_info_;
base::Optional<network::mojom::blink::TrustTokenParams> trust_token_params_;
base::Optional<String> suggested_filename_;
@@ -541,6 +553,8 @@ class PLATFORM_EXPORT ResourceRequestHead {
// the prefetch cache will be restricted to top-level-navigations.
bool prefetch_maybe_for_top_level_navigation_ = false;
+ bool allowHTTP1ForStreamingUpload_ = false;
+
// This is used when fetching preload header requests from cross-origin
// prefetch responses. The browser process uses this token to ensure the
// request is cached correctly.
@@ -551,6 +565,9 @@ class PLATFORM_EXPORT ResourceRequestBody {
public:
ResourceRequestBody();
explicit ResourceRequestBody(scoped_refptr<EncodedFormData> form_body);
+ explicit ResourceRequestBody(
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ stream_body);
ResourceRequestBody(const ResourceRequestBody&) = delete;
ResourceRequestBody(ResourceRequestBody&&);
@@ -559,11 +576,25 @@ class PLATFORM_EXPORT ResourceRequestBody {
~ResourceRequestBody();
+ bool IsEmpty() const { return !form_body_ && !stream_body_; }
const scoped_refptr<EncodedFormData>& FormBody() const { return form_body_; }
void SetFormBody(scoped_refptr<EncodedFormData>);
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ TakeStreamBody() {
+ return std::move(stream_body_);
+ }
+ const mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>&
+ StreamBody() const {
+ return stream_body_;
+ }
+ void SetStreamBody(
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>);
+
private:
scoped_refptr<EncodedFormData> form_body_;
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ stream_body_;
};
// A ResourceRequest is a "request" object for ResourceLoader. Conceptually
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 f7848a110e7..76717a71a0e 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
@@ -82,14 +82,10 @@ ResourceResponse::SignedCertificateTimestamp::IsolatedCopy() const {
signature_data_.IsolatedCopy());
}
-ResourceResponse::ResourceResponse()
- : is_null_(true),
- response_type_(network::mojom::FetchResponseType::kDefault) {}
+ResourceResponse::ResourceResponse() : is_null_(true) {}
ResourceResponse::ResourceResponse(const KURL& current_request_url)
- : current_request_url_(current_request_url),
- is_null_(false),
- response_type_(network::mojom::FetchResponseType::kDefault) {}
+ : current_request_url_(current_request_url), is_null_(false) {}
ResourceResponse::ResourceResponse(const ResourceResponse&) = default;
ResourceResponse& ResourceResponse::operator=(const ResourceResponse&) =
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 4028c6341d2..26dc39ba236 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
@@ -34,6 +34,7 @@
#include "base/optional.h"
#include "base/time/time.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.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"
@@ -326,6 +327,15 @@ class PLATFORM_EXPORT ResourceResponse final {
was_fetched_via_service_worker_ = value;
}
+ network::mojom::FetchResponseSource GetServiceWorkerResponseSource() const {
+ return service_worker_response_source_;
+ }
+
+ void SetServiceWorkerResponseSource(
+ network::mojom::FetchResponseSource value) {
+ service_worker_response_source_ = value;
+ }
+
// See network::ResourceResponseInfo::was_fallback_required_by_service_worker.
bool WasFallbackRequiredByServiceWorker() const {
return was_fallback_required_by_service_worker_;
@@ -525,6 +535,11 @@ class PLATFORM_EXPORT ResourceResponse final {
// Was the resource fetched over a ServiceWorker.
bool was_fetched_via_service_worker_ = false;
+ // The source of the resource, if it was fetched via ServiceWorker. This is
+ // kUnspecified if |was_fetched_via_service_worker| is false.
+ network::mojom::FetchResponseSource service_worker_response_source_ =
+ network::mojom::FetchResponseSource::kUnspecified;
+
// Was the fallback request with skip service worker flag required.
bool was_fallback_required_by_service_worker_ = false;
@@ -556,7 +571,8 @@ class PLATFORM_EXPORT ResourceResponse final {
bool was_alpn_negotiated_ = false;
// https://fetch.spec.whatwg.org/#concept-response-type
- network::mojom::FetchResponseType response_type_;
+ network::mojom::FetchResponseType response_type_ =
+ network::mojom::FetchResponseType::kDefault;
// HTTP version used in the response, if known.
HTTPVersion http_version_ = kHTTPVersionUnknown;
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
index ecb144b4d3b..c878bb13838 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
@@ -220,7 +220,7 @@ class ResponseBodyLoader::DelegatingBytesConsumer final
}
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(bytes_consumer_);
visitor->Trace(loader_);
visitor->Trace(bytes_consumer_client_);
@@ -480,7 +480,7 @@ void ResponseBodyLoader::OnStateChange() {
}
}
-void ResponseBodyLoader::Trace(Visitor* visitor) {
+void ResponseBodyLoader::Trace(Visitor* visitor) const {
visitor->Trace(bytes_consumer_);
visitor->Trace(delegating_bytes_consumer_);
visitor->Trace(client_);
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
index e6d51e04693..b3c0e8264a7 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
@@ -53,7 +53,7 @@ class PLATFORM_EXPORT ResponseBodyLoaderDrainableInterface
// them back to the associated client asynchronously.
virtual BytesConsumer& DrainAsBytesConsumer() = 0;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
};
// ResponseBodyLoader reads the response body and reports the contents to the
@@ -100,7 +100,7 @@ class PLATFORM_EXPORT ResponseBodyLoader final
bool IsSuspended() const { return suspended_; }
bool IsDrained() const { return drained_; }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
// The maximal number of bytes consumed in a task. When there are more bytes
// in the data pipe, they will be consumed in following tasks. Setting a too
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
index 15b6cb9322b..80dc82ccaeb 100644
--- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
@@ -76,7 +76,7 @@ class ResponseBodyLoaderTest : public testing::Test {
}
void SetLoader(ResponseBodyLoader& loader) { loader_ = loader; }
- void Trace(Visitor* visitor) override { visitor->Trace(loader_); }
+ void Trace(Visitor* visitor) const override { visitor->Trace(loader_); }
private:
const Option option_;
@@ -122,7 +122,7 @@ class ResponseBodyLoaderTest : public testing::Test {
EXPECT_FALSE(test_response_body_loader_client_->LoadingIsFailed());
}
String DebugName() const override { return "ReadingClient"; }
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(bytes_consumer_);
visitor->Trace(test_response_body_loader_client_);
BytesConsumer::Client::Trace(visitor);
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 33cedf848d7..08022c8bdc7 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
@@ -24,7 +24,7 @@ ScriptCachedMetadataHandler::ScriptCachedMetadataHandler(
std::unique_ptr<CachedMetadataSender> sender)
: sender_(std::move(sender)), encoding_(encoding) {}
-void ScriptCachedMetadataHandler::Trace(Visitor* visitor) {
+void ScriptCachedMetadataHandler::Trace(Visitor* visitor) const {
CachedMetadataHandler::Trace(visitor);
}
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 df5ca5535a1..4839b7f7350 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,7 +33,7 @@ class PLATFORM_EXPORT ScriptCachedMetadataHandler final
ScriptCachedMetadataHandler(const WTF::TextEncoding&,
std::unique_ptr<CachedMetadataSender>);
~ScriptCachedMetadataHandler() override = default;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
void SetCachedMetadata(uint32_t, const uint8_t*, size_t) override;
void ClearCachedMetadata(ClearCacheType) override;
scoped_refptr<CachedMetadata> GetCachedMetadata(uint32_t) const override;
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 7650c988f53..d024238c265 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
@@ -18,7 +18,7 @@ const size_t SourceKeyedCachedMetadataHandler::kKeySize;
class SourceKeyedCachedMetadataHandler::SingleKeyHandler final
: public SingleCachedMetadataHandler {
public:
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(parent_);
SingleCachedMetadataHandler::Trace(visitor);
}
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 bd1ead40055..2d1bb0e5f9b 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(Visitor* visitor) {
+void StaleRevalidationResourceClient::Trace(Visitor* visitor) const {
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 b1b9a3eb226..2389675b740 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(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const 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
index 6654f8e72d5..c8c631bd23a 100644
--- 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
@@ -26,6 +26,10 @@ network::OptionalTrustTokenParams ConvertTrustTokenParams(
for (const String& additional_header : in.additional_signed_headers) {
out->additional_signed_headers.push_back(additional_header.Latin1());
}
+ if (!in.possibly_unsafe_additional_signing_data.IsNull()) {
+ out->possibly_unsafe_additional_signing_data =
+ in.possibly_unsafe_additional_signing_data.Utf8();
+ }
return network::OptionalTrustTokenParams(std::move(out));
}
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
index c0ddc73d986..25fa0aa4493 100644
--- 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
@@ -30,6 +30,7 @@
#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/encoded_form_data.h"
#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
namespace blink {
@@ -347,6 +348,17 @@ void PopulateResourceRequest(const ResourceRequestHead& src,
dest->request_body = base::MakeRefCounted<network::ResourceRequestBody>();
PopulateResourceRequestBody(*body, dest->request_body.get());
+ } else if (src_body.StreamBody().is_valid()) {
+ DCHECK_NE(dest->method, net::HttpRequestHeaders::kGetMethod);
+ DCHECK_NE(dest->method, net::HttpRequestHeaders::kHeadMethod);
+ mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
+ stream_body = src_body.TakeStreamBody();
+ dest->request_body = base::MakeRefCounted<network::ResourceRequestBody>();
+ mojo::PendingRemote<network::mojom::ChunkedDataPipeGetter>
+ network_stream_body(stream_body.PassPipe(), 0u);
+ dest->request_body->SetToChunkedDataPipe(std::move(network_stream_body));
+ dest->request_body->SetAllowHTTP1ForStreamingUpload(
+ src.AllowHTTP1ForStreamingUpload());
}
if (resource_type == mojom::ResourceType::kStylesheet) {
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 92271f1c6ed..54d2ac975ab 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
@@ -31,7 +31,7 @@ class WorkerResourceTimingNotifier
mojo::PendingReceiver<mojom::blink::WorkerTimingContainer>
worker_timing_receiver) = 0;
- virtual void Trace(Visitor*) {}
+ virtual void Trace(Visitor*) const {}
};
} // namespace blink
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 5fd5c9b36ac..7fc5e7497a0 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(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(consumer_);
BytesConsumer::Client::Trace(visitor);
}
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
index 7b42360a1d8..9f39a8e8367 100644
--- a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
@@ -6,19 +6,23 @@
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FETCH_TESTING_PLATFORM_SUPPORT_H_
#include <memory>
+
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
namespace blink {
+class WebURLLoaderMockFactory;
+
class FetchTestingPlatformSupport
: public TestingPlatformSupportWithMockScheduler {
public:
FetchTestingPlatformSupport();
~FetchTestingPlatformSupport() override;
+ WebURLLoaderMockFactory* GetURLLoaderMockFactory();
+
// Platform:
- WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
std::unique_ptr<WebURLLoaderFactory> CreateDefaultURLLoaderFactory() override;
private:
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 5781e0042fb..4e0d70a9043 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
@@ -40,7 +40,8 @@ class MockFetchContext : public FetchContext {
const KURL&,
const ResourceLoaderOptions&,
ReportingDisposition,
- ResourceRequest::RedirectStatus redirect_status) const override {
+ const base::Optional<ResourceRequest::RedirectInfo>& redirect_info)
+ const override {
return base::nullopt;
}
base::Optional<ResourceRequestBlockedReason> CheckCSPForRequest(
@@ -49,6 +50,7 @@ class MockFetchContext : public FetchContext {
const KURL& url,
const ResourceLoaderOptions& options,
ReportingDisposition reporting_disposition,
+ const KURL& url_before_redirects,
ResourceRequest::RedirectStatus redirect_status) const override {
return base::nullopt;
}
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 d699ef377ea..37129f58b35 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(Visitor* visitor) {
+void ReplayingBytesConsumer::Trace(Visitor* visitor) const {
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 de29497afc4..cbdec26fb5c 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(Visitor*) override;
+ void Trace(Visitor*) const 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 9b222e3c600..d658ede1285 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
@@ -37,7 +37,7 @@ TestResourceFetcherProperties::TestResourceFetcherProperties(
const FetchClientSettingsObject& fetch_client_settings_object)
: fetch_client_settings_object_(fetch_client_settings_object) {}
-void TestResourceFetcherProperties::Trace(Visitor* visitor) {
+void TestResourceFetcherProperties::Trace(Visitor* visitor) const {
visitor->Trace(fetch_client_settings_object_);
ResourceFetcherProperties::Trace(visitor);
}
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h b/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
index e4c0e1b8f4f..8095add8c0a 100644
--- a/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h
@@ -23,7 +23,7 @@ class TestResourceFetcherProperties final : public ResourceFetcherProperties {
explicit TestResourceFetcherProperties(const FetchClientSettingsObject&);
~TestResourceFetcherProperties() override = default;
- void Trace(Visitor* visitor) override;
+ void Trace(Visitor* visitor) const override;
DetachableResourceFetcherProperties& MakeDetachable() const {
return *MakeGarbageCollected<DetachableResourceFetcherProperties>(*this);
diff --git a/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm
index bac6e5be833..3567753ae6d 100644
--- a/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm
+++ b/chromium/third_party/blink/renderer/platform/mac/block_exceptions.mm
@@ -25,6 +25,7 @@
#import "third_party/blink/renderer/platform/mac/block_exceptions.h"
+#include "base/notreached.h"
#import "third_party/blink/renderer/platform/wtf/assertions.h"
void ReportBlockedObjCException(NSException* exception) {
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/DEPS b/chromium/third_party/blink/renderer/platform/mediastream/DEPS
index 3298a7464b4..df126aab3dc 100644
--- a/chromium/third_party/blink/renderer/platform/mediastream/DEPS
+++ b/chromium/third_party/blink/renderer/platform/mediastream/DEPS
@@ -18,3 +18,9 @@ include_rules = [
"+third_party/blink/renderer/platform/wtf/uuid.h",
"+third_party/blink/renderer/platform/wtf",
]
+
+specific_include_rules = {
+ "media_stream_audio_test\.cc" : [
+ "+base/threading/platform_thread.h",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc
index e1ac3587d09..67232f568cd 100644
--- a/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc
@@ -498,7 +498,7 @@ String MediaTrackConstraintSetPlatform::ToString() const {
StringBuilder builder;
bool first = true;
for (auto* const constraint : AllConstraints()) {
- if (!constraint->IsEmpty()) {
+ if (constraint->IsPresent()) {
if (!first)
builder.Append(", ");
builder.Append(constraint->GetName());
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h
index 0e094e1c3ff..5686aa789b3 100644
--- a/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h
@@ -49,6 +49,10 @@ class PLATFORM_EXPORT BaseConstraint {
public:
explicit BaseConstraint(const char* name);
virtual ~BaseConstraint();
+
+ bool IsPresent() const { return is_present_ || !IsEmpty(); }
+ void SetIsPresent(bool is_present) { is_present_ = is_present; }
+
virtual bool IsEmpty() const = 0;
bool HasMandatory() const;
virtual bool HasMin() const { return false; }
@@ -59,6 +63,7 @@ class PLATFORM_EXPORT BaseConstraint {
private:
const char* name_;
+ bool is_present_ = false;
};
// Note this class refers to the "long" WebIDL definition which is
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 4c60ce0cff0..14efaae11d9 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
@@ -13,6 +13,7 @@
#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/mediastream/media_stream_component.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"
@@ -82,18 +83,17 @@ MediaStreamAudioSource* MediaStreamAudioSource::From(
return static_cast<MediaStreamAudioSource*>(source.GetPlatformSource());
}
-bool MediaStreamAudioSource::ConnectToTrack(
- const WebMediaStreamTrack& blink_track) {
+bool MediaStreamAudioSource::ConnectToTrack(MediaStreamComponent* component) {
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!blink_track.IsNull());
+ DCHECK(component);
SendLogMessage(base::StringPrintf("ConnectToTrack({track_id=%s})",
- blink_track.Id().Utf8().c_str()));
+ component->Id().Utf8().c_str()));
// Sanity-check that there is not already a MediaStreamAudioTrack instance
- // associated with |blink_track|.
- if (MediaStreamAudioTrack::From(blink_track)) {
- LOG(DFATAL)
- << "Attempting to connect another source to a WebMediaStreamTrack.";
+ // associated with |component|.
+ if (MediaStreamAudioTrack::From(component)) {
+ LOG(DFATAL) << "Attempting to connect another source to a "
+ "WebMediaStreamTrack/MediaStreamComponent.";
return false;
}
@@ -106,15 +106,14 @@ bool MediaStreamAudioSource::ConnectToTrack(
}
// Create and initialize a new MediaStreamAudioTrack and pass ownership of it
- // to the WebMediaStreamTrack.
- WebMediaStreamTrack mutable_blink_track = blink_track;
- mutable_blink_track.SetPlatformTrack(
- CreateMediaStreamAudioTrack(blink_track.Id().Utf8()));
+ // to the MediaStreamComponent.
+ component->SetPlatformTrack(
+ CreateMediaStreamAudioTrack(component->Id().Utf8()));
// Propagate initial "enabled" state.
- MediaStreamAudioTrack* const track = MediaStreamAudioTrack::From(blink_track);
+ MediaStreamAudioTrack* const track = MediaStreamAudioTrack::From(component);
DCHECK(track);
- track->SetEnabled(blink_track.IsEnabled());
+ track->SetEnabled(component->Enabled());
// If the source is stopped, do not start the track.
if (is_stopped_)
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 1dfc87329a2..ae3e88b5975 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
@@ -14,7 +14,6 @@
#include "media/base/limits.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"
@@ -28,6 +27,7 @@ namespace blink {
PLATFORM_EXPORT extern const int kFallbackAudioLatencyMs;
class MediaStreamAudioTrack;
+class MediaStreamComponent;
// Represents a source of audio, and manages the delivery of audio data between
// the source implementation and one or more MediaStreamAudioTracks. This is a
@@ -48,18 +48,18 @@ class MediaStreamAudioTrack;
//
// class MyAudioSource : public MediaStreamAudioSource { ... };
//
-// WebMediaStreamSource blink_source = ...;
-// WebMediaStreamTrack blink_track = ...;
-// blink_source.setExtraData(new MyAudioSource()); // Takes ownership.
-// if (MediaStreamAudioSource::From(blink_source)
-// ->ConnectToTrack(blink_track)) {
+// MediaStreamSource* media_stream_source = ...;
+// MediaStreamComponent* media_stream_track = ...;
+// source->setExtraData(new MyAudioSource()); // Takes ownership.
+// if (MediaStreamAudioSource::From(media_stream_source)
+// ->ConnectToTrack(media_stream_track)) {
// LOG(INFO) << "Success!";
// } else {
// LOG(ERROR) << "Failed!";
// }
// // Regardless of whether ConnectToTrack() succeeds, there will always be a
// // MediaStreamAudioTrack instance created.
-// CHECK(MediaStreamAudioTrack::From(blink_track));
+// CHECK(MediaStreamAudioTrack::From(media_stream_track));
class PLATFORM_EXPORT MediaStreamAudioSource
: public WebPlatformMediaStreamSource {
public:
@@ -91,7 +91,7 @@ class PLATFORM_EXPORT MediaStreamAudioSource
// implementation of the content::MediaStreamAudioTrack interface, which
// becomes associated with and owned by |track|. Returns true if the source
// was successfully started.
- bool ConnectToTrack(const WebMediaStreamTrack& track);
+ bool ConnectToTrack(MediaStreamComponent* component);
// Returns the current format of the audio passing through this source to the
// sinks. This can return invalid parameters if the source has not yet been
diff --git a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_test.cc
index e6c83416711..eb15e713909 100644
--- a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_test.cc
@@ -19,6 +19,8 @@
#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"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
namespace blink {
@@ -239,30 +241,30 @@ class FakeMediaStreamAudioSink : public WebMediaStreamAudioSink {
class MediaStreamAudioTest : public ::testing::Test {
protected:
void SetUp() override {
- blink_audio_source_.Initialize(
- WebString::FromUTF8("audio_id"), WebMediaStreamSource::kTypeAudio,
- WebString::FromUTF8("audio_track"), false /* remote */);
- blink_audio_track_.Initialize(blink_audio_source_.Id(),
- blink_audio_source_);
+ audio_source_ = MakeGarbageCollected<MediaStreamSource>(
+ String::FromUTF8("audio_id"), MediaStreamSource::kTypeAudio,
+ String::FromUTF8("audio_track"), false /* remote */);
+ audio_component_ = MakeGarbageCollected<MediaStreamComponent>(
+ audio_source_->Id(), audio_source_);
}
void TearDown() override {
- blink_audio_track_.Reset();
- blink_audio_source_.Reset();
+ audio_component_ = nullptr;
+ audio_source_ = nullptr;
WebHeap::CollectAllGarbageForTesting();
}
FakeMediaStreamAudioSource* source() const {
return static_cast<FakeMediaStreamAudioSource*>(
- MediaStreamAudioSource::From(blink_audio_source_));
+ MediaStreamAudioSource::From(audio_source_.Get()));
}
MediaStreamAudioTrack* track() const {
- return MediaStreamAudioTrack::From(blink_audio_track_);
+ return MediaStreamAudioTrack::From(audio_component_.Get());
}
- WebMediaStreamSource blink_audio_source_;
- WebMediaStreamTrack blink_audio_track_;
+ Persistent<MediaStreamSource> audio_source_;
+ Persistent<MediaStreamComponent> audio_component_;
base::test::TaskEnvironment task_environment_;
};
@@ -272,15 +274,16 @@ class MediaStreamAudioTest : public ::testing::Test {
TEST_F(MediaStreamAudioTest, BasicUsage) {
// Create the source, but it should not be started yet.
ASSERT_FALSE(source());
- blink_audio_source_.SetPlatformSource(
- std::make_unique<FakeMediaStreamAudioSource>());
+ auto platform_audio_source = std::make_unique<FakeMediaStreamAudioSource>();
+ platform_audio_source->SetOwner(audio_source_.Get());
+ audio_source_->SetPlatformSource(std::move(platform_audio_source));
ASSERT_TRUE(source());
EXPECT_FALSE(source()->was_started());
EXPECT_FALSE(source()->was_stopped());
// Connect a track to the source. This should auto-start the source.
ASSERT_FALSE(track());
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_));
+ EXPECT_TRUE(source()->ConnectToTrack(audio_component_));
ASSERT_TRUE(track());
EXPECT_TRUE(source()->was_started());
EXPECT_FALSE(source()->was_stopped());
@@ -317,22 +320,23 @@ TEST_F(MediaStreamAudioTest, BasicUsage) {
TEST_F(MediaStreamAudioTest, ConnectTrackAfterSourceStopped) {
// Create the source, connect one track, and stop it. This should
// automatically stop the source.
- blink_audio_source_.SetPlatformSource(
- std::make_unique<FakeMediaStreamAudioSource>());
+ auto platform_audio_source = std::make_unique<FakeMediaStreamAudioSource>();
+ platform_audio_source->SetOwner(audio_source_.Get());
+ audio_source_->SetPlatformSource(std::move(platform_audio_source));
ASSERT_TRUE(source());
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_));
+ EXPECT_TRUE(source()->ConnectToTrack(audio_component_));
track()->Stop();
EXPECT_TRUE(source()->was_started());
EXPECT_TRUE(source()->was_stopped());
// Now, connect another track. ConnectToTrack() will return false, but there
// should be a MediaStreamAudioTrack instance created and owned by the
- // WebMediaStreamTrack.
- WebMediaStreamTrack another_blink_track;
- another_blink_track.Initialize(blink_audio_source_.Id(), blink_audio_source_);
- EXPECT_FALSE(MediaStreamAudioTrack::From(another_blink_track));
- EXPECT_FALSE(source()->ConnectToTrack(another_blink_track));
- EXPECT_TRUE(MediaStreamAudioTrack::From(another_blink_track));
+ // MediaStreamComponent.
+ auto* another_component = MakeGarbageCollected<MediaStreamComponent>(
+ audio_source_->Id(), audio_source_);
+ EXPECT_FALSE(MediaStreamAudioTrack::From(another_component));
+ EXPECT_FALSE(source()->ConnectToTrack(another_component));
+ EXPECT_TRUE(MediaStreamAudioTrack::From(another_component));
}
// Tests that a sink is immediately "ended" when connected to a stopped track.
@@ -354,10 +358,11 @@ TEST_F(MediaStreamAudioTest, AddSinkToStoppedTrack) {
TEST_F(MediaStreamAudioTest, FormatChangesPropagate) {
// Create a source, connect it to track, and connect the track to a
// sink.
- blink_audio_source_.SetPlatformSource(
- std::make_unique<FakeMediaStreamAudioSource>());
+ auto platform_audio_source = std::make_unique<FakeMediaStreamAudioSource>();
+ platform_audio_source->SetOwner(audio_source_.Get());
+ audio_source_->SetPlatformSource(std::move(platform_audio_source));
ASSERT_TRUE(source());
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_));
+ EXPECT_TRUE(source()->ConnectToTrack(audio_component_));
ASSERT_TRUE(track());
FakeMediaStreamAudioSink sink;
ASSERT_TRUE(!sink.params().IsValid());
@@ -390,10 +395,11 @@ TEST_F(MediaStreamAudioTest, FormatChangesPropagate) {
// OnEnabledChanged() method should be called.
TEST_F(MediaStreamAudioTest, EnableAndDisableTracks) {
// Create a source and connect it to track.
- blink_audio_source_.SetPlatformSource(
- std::make_unique<FakeMediaStreamAudioSource>());
+ auto platform_audio_source = std::make_unique<FakeMediaStreamAudioSource>();
+ platform_audio_source->SetOwner(audio_source_.Get());
+ audio_source_->SetPlatformSource(std::move(platform_audio_source));
ASSERT_TRUE(source());
- EXPECT_TRUE(source()->ConnectToTrack(blink_audio_track_));
+ EXPECT_TRUE(source()->ConnectToTrack(audio_component_));
ASSERT_TRUE(track());
// Connect the track to a sink and expect the sink to be notified that the
@@ -420,12 +426,12 @@ TEST_F(MediaStreamAudioTest, EnableAndDisableTracks) {
// Create a second track and a second sink, but this time the track starts out
// disabled. Expect the sink to be notified at the start that the track is
// disabled.
- WebMediaStreamTrack another_blink_track;
- another_blink_track.Initialize(blink_audio_source_.Id(), blink_audio_source_);
- EXPECT_TRUE(source()->ConnectToTrack(another_blink_track));
- MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(false);
+ auto* another_component = MakeGarbageCollected<MediaStreamComponent>(
+ audio_source_->Id(), audio_source_);
+ EXPECT_TRUE(source()->ConnectToTrack(another_component));
+ MediaStreamAudioTrack::From(another_component)->SetEnabled(false);
FakeMediaStreamAudioSink another_sink;
- MediaStreamAudioTrack::From(another_blink_track)->AddSink(&another_sink);
+ MediaStreamAudioTrack::From(another_component)->AddSink(&another_sink);
EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED,
another_sink.enable_state());
@@ -437,7 +443,7 @@ TEST_F(MediaStreamAudioTest, EnableAndDisableTracks) {
EXPECT_TRUE(another_sink.is_audio_silent());
// Now, enable the second track and expect the second sink to be notified.
- MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(true);
+ MediaStreamAudioTrack::From(another_component)->SetEnabled(true);
EXPECT_EQ(FakeMediaStreamAudioSink::WAS_ENABLED, another_sink.enable_state());
// Wait until non-silent audio reaches the second sink.
@@ -450,7 +456,7 @@ TEST_F(MediaStreamAudioTest, EnableAndDisableTracks) {
EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED, sink.enable_state());
EXPECT_TRUE(sink.is_audio_silent());
- MediaStreamAudioTrack::From(another_blink_track)->RemoveSink(&another_sink);
+ MediaStreamAudioTrack::From(another_component)->RemoveSink(&another_sink);
track()->RemoveSink(&sink);
}
diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc
index 2aa0125de47..aab485aeff5 100644
--- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc
+++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc
@@ -12,6 +12,8 @@
#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"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
namespace blink {
@@ -38,12 +40,12 @@ MediaStreamAudioTrack::~MediaStreamAudioTrack() {
// static
MediaStreamAudioTrack* MediaStreamAudioTrack::From(
- const WebMediaStreamTrack& track) {
- if (track.IsNull() ||
- track.Source().GetType() != WebMediaStreamSource::kTypeAudio) {
+ const MediaStreamComponent* component) {
+ if (!component ||
+ component->Source()->GetType() != MediaStreamSource::kTypeAudio) {
return nullptr;
}
- return static_cast<MediaStreamAudioTrack*>(track.GetPlatformTrack());
+ return static_cast<MediaStreamAudioTrack*>(component->GetPlatformTrack());
}
void MediaStreamAudioTrack::AddSink(WebMediaStreamAudioSink* sink) {
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
index 5981a2aa271..deb29bee0dc 100644
--- 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
@@ -21,11 +21,12 @@ namespace blink {
class WebMediaStreamAudioSink;
class MediaStreamAudioSource;
+class MediaStreamComponent;
// 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.
+// of this class is owned by MediaStreamComponent/WebMediaStreamTrack, and
+// clients should use From() to gain access to a MediaStreamAudioTrack.
class PLATFORM_EXPORT MediaStreamAudioTrack
: public WebPlatformMediaStreamTrack {
public:
@@ -35,7 +36,7 @@ class PLATFORM_EXPORT MediaStreamAudioTrack
// Returns the MediaStreamAudioTrack instance owned by the given blink |track|
// or null.
- static MediaStreamAudioTrack* From(const WebMediaStreamTrack& track);
+ static MediaStreamAudioTrack* From(const MediaStreamComponent* component);
// Provides a weak reference to this MediaStreamAudioTrack which is
// invalidated when Stop() is called. The weak pointer may only be
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 bec6546fb21..3c58ce7f6b2 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(Visitor* visitor) {
+void MediaStreamComponent::Trace(Visitor* visitor) const {
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 6515d8bb8df..1cd4c782d66 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
@@ -102,7 +102,7 @@ class PLATFORM_EXPORT MediaStreamComponent final
}
void GetSettings(WebMediaStreamTrack::Settings&);
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
private:
// AudioSourceProviderImpl wraps a WebAudioSourceProvider::provideInput()
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 970dd08e6f6..dbacd0e022c 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(Visitor* visitor) {
+void MediaStreamDescriptor::Trace(Visitor* visitor) const {
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 80141ceea79..8ebcebe810c 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
class PLATFORM_EXPORT MediaStreamDescriptor final
@@ -111,7 +111,7 @@ class PLATFORM_EXPORT MediaStreamDescriptor final
void AddObserver(WebMediaStreamObserver*);
void RemoveObserver(WebMediaStreamObserver*);
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
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 2034974500d..4fff9a60bfb 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
@@ -242,7 +242,7 @@ void MediaStreamSource::ConsumeAudio(AudioBus* bus, size_t number_of_frames) {
consumer->ConsumeAudio(bus, number_of_frames);
}
-void MediaStreamSource::Trace(Visitor* visitor) {
+void MediaStreamSource::Trace(Visitor* visitor) const {
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 2cc1f159f49..7f064ee561b 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
@@ -121,7 +121,7 @@ class PLATFORM_EXPORT MediaStreamSource final
return audio_consumers_;
}
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
void Dispose();
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 a444135bea9..76e768b4c2a 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
@@ -13,14 +13,14 @@
namespace blink {
WebAudioMediaStreamSource::WebAudioMediaStreamSource(
- WebMediaStreamSource* blink_source,
+ MediaStreamSource* blink_source,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: MediaStreamAudioSource(std::move(task_runner), false /* is_remote */),
is_registered_consumer_(false),
fifo_(ConvertToBaseRepeatingCallback(CrossThreadBindRepeating(
&WebAudioMediaStreamSource::DeliverRebufferedAudio,
WTF::CrossThreadUnretained(this)))),
- blink_source_(*blink_source) {
+ blink_source_(blink_source) {
DVLOG(1) << "WebAudioMediaStreamSource::WebAudioMediaStreamSource()";
}
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 afdfc6e7c58..bc86f51ba9c 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
@@ -27,7 +27,7 @@ class PLATFORM_EXPORT WebAudioMediaStreamSource final
public WebAudioDestinationConsumer {
public:
WebAudioMediaStreamSource(
- WebMediaStreamSource* blink_source,
+ MediaStreamSource* blink_source,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
~WebAudioMediaStreamSource() override;
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 fde61b92783..2ef8ae4c5d5 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
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 9021739bd00..f185bd0b9b8 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(Visitor* visitor) {
+void MHTMLArchive::Trace(Visitor* visitor) const {
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 c1edc8d5764..032ed9ff483 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(Visitor*);
+ void Trace(Visitor*) const;
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 7f063783ff8..7ceacdcc3c5 100644
--- a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
+++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc
@@ -125,7 +125,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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
private:
static Encoding ParseContentTransferEncoding(const String&);
diff --git a/chromium/third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.cc b/chromium/third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.cc
index edd29a8c5d1..99a14a9c8e2 100644
--- a/chromium/third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.cc
+++ b/chromium/third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/platform/mhtml/shared_buffer_chunk_reader.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
diff --git a/chromium/third_party/blink/renderer/platform/mojo/DEPS b/chromium/third_party/blink/renderer/platform/mojo/DEPS
index 0520c841e8c..771f0ebe369 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/DEPS
+++ b/chromium/third_party/blink/renderer/platform/mojo/DEPS
@@ -6,7 +6,6 @@ include_rules = [
"+third_party/blink/renderer/platform/mojo",
# Dependencies.
- "+base/callback.h",
"+base/containers/span.h",
"+base/message_loop/message_loop_current.h",
"+base/observer_list.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 d439274cd08..32a27112e18 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -14,11 +14,10 @@ typemaps = [
"//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/loader/fetch/fetch_api_request_body.typemap",
"//third_party/blink/renderer/platform/mojo/fetch_api_request_headers.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",
diff --git a/chromium/third_party/blink/renderer/platform/mojo/features.cc b/chromium/third_party/blink/renderer/platform/mojo/features.cc
new file mode 100644
index 00000000000..8ce6ef9f97f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/features.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/mojo/features.h"
+
+namespace blink {
+// HeapMojo experiments to deprecate kWithoutContextObserver
+const base::Feature kHeapMojoUseContextObserver{
+ "HeapMojoUseContextObserver", base::FEATURE_DISABLED_BY_DEFAULT};
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/features.h b/chromium/third_party/blink/renderer/platform/mojo/features.h
new file mode 100644
index 00000000000..742150c78ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/mojo/features.h
@@ -0,0 +1,15 @@
+// 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_FEATURES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+PLATFORM_EXPORT extern const base::Feature kHeapMojoUseContextObserver;
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_FEATURES_H_
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h
index 6e6e9b9145f..37151f4c3fe 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h
@@ -10,6 +10,7 @@
#include "mojo/public/cpp/bindings/associated_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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -65,7 +66,7 @@ class HeapMojoAssociatedReceiver {
return wrapper_->associated_receiver().WaitForIncomingCall();
}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
FRIEND_TEST_ALL_PREFIXES(HeapMojoAssociatedReceiverGCWithContextObserverTest,
@@ -80,11 +81,10 @@ class HeapMojoAssociatedReceiver {
public:
Wrapper(Owner* owner, ContextLifecycleNotifier* notifier)
: owner_(owner), associated_receiver_(owner) {
- DCHECK(notifier);
SetContextLifecycleNotifier(notifier);
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(owner_);
ContextLifecycleObserver::Trace(visitor);
}
@@ -97,7 +97,9 @@ class HeapMojoAssociatedReceiver {
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
associated_receiver_.reset();
}
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
index 4a22d9950c2..720eb2563ef 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
@@ -10,6 +10,7 @@
#include "mojo/public/cpp/bindings/associated_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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -35,7 +36,6 @@ class HeapMojoAssociatedReceiverSet {
"Owner should implement Interface");
static_assert(IsGarbageCollectedType<Owner>::value,
"Owner needs to be a garbage collected object");
- DCHECK(context);
}
HeapMojoAssociatedReceiverSet(const HeapMojoAssociatedReceiverSet&) = delete;
HeapMojoAssociatedReceiverSet& operator=(
@@ -62,7 +62,7 @@ class HeapMojoAssociatedReceiverSet {
bool empty() const { return wrapper_->associated_receiver_set().empty(); }
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
FRIEND_TEST_ALL_PREFIXES(
@@ -81,7 +81,7 @@ class HeapMojoAssociatedReceiverSet {
SetContextLifecycleNotifier(notifier);
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(owner_);
ContextLifecycleObserver::Trace(visitor);
}
@@ -95,7 +95,9 @@ class HeapMojoAssociatedReceiverSet {
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
associated_receiver_set_.Clear();
}
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
index 80686308b0d..86eb5cb5079 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
@@ -45,7 +45,7 @@ class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -96,7 +96,9 @@ class GCOwner : public GarbageCollected<GCOwner<Mode>>,
test_->set_is_owner_alive(true);
}
void Dispose() { test_->set_is_owner_alive(false); }
- void Trace(Visitor* visitor) { visitor->Trace(associated_receiver_set_); }
+ void Trace(Visitor* visitor) const {
+ visitor->Trace(associated_receiver_set_);
+ }
HeapMojoAssociatedReceiverSet<sample::blink::Service, GCOwner, Mode>&
associated_receiver_set() {
@@ -122,7 +124,7 @@ class HeapMojoAssociatedReceiverSetGCWithContextObserverTest
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoAssociatedReceiverSetGCWithoutContextObserverTest
: public HeapMojoAssociatedReceiverSetGCBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
// Remove() a PendingAssociatedReceiver from HeapMojoAssociatedReceiverSet and
// verify that the receiver is no longer part of the set.
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_test.cc
index 44939a30360..f72d715f7cf 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_test.cc
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_test.cc
@@ -37,7 +37,7 @@ class MockContext final : public GarbageCollected<MockContext>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -76,7 +76,7 @@ class AssociatedReceiverOwner
return associated_receiver_;
}
- void Trace(Visitor* visitor) { visitor->Trace(associated_receiver_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(associated_receiver_); }
private:
// sample::blink::Service implementation
@@ -159,13 +159,13 @@ class HeapMojoAssociatedReceiverGCWithContextObserverTest
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoAssociatedReceiverGCWithoutContextObserverTest
: public HeapMojoAssociatedReceiverGCBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoAssociatedReceiverDestroyContextWithContextObserverTest
: public HeapMojoAssociatedReceiverDestroyContextBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoAssociatedReceiverDestroyContextWithoutContextObserverTest
: public HeapMojoAssociatedReceiverDestroyContextBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
// Make HeapMojoAssociatedReceiver with context observer garbage collected and
// check that the connection is disconnected right after the marking phase.
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
index fd13523b098..590897bc4e3 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h
@@ -10,6 +10,7 @@
#include "mojo/public/cpp/bindings/associated_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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -19,8 +20,7 @@ namespace blink {
// HeapMojoAssociatedRemote by default. HeapMojoAssociatedRemote must be
// associated with context. HeapMojoAssociatedRemote's constructor takes context
// as a mandatory parameter. HeapMojoAssociatedRemote resets the mojo connection
-// when 1) the owner object is garbage-collected and 2) the associated
-// ExecutionContext is detached.
+// when the associated ExecutionContext is detached.
// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure
// that the interface is not used after ContextDestroyed().
@@ -78,18 +78,16 @@ class HeapMojoAssociatedRemote {
return wrapper_->associated_remote().FlushForTesting();
}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
- // Garbage collected wrapper class to add a prefinalizer.
+ // Garbage collected wrapper class to add ContextLifecycleObserver.
class Wrapper final : public GarbageCollected<Wrapper>,
public ContextLifecycleObserver {
- USING_PRE_FINALIZER(Wrapper, Dispose);
USING_GARBAGE_COLLECTED_MIXIN(Wrapper);
public:
explicit Wrapper(ContextLifecycleNotifier* notifier) {
- DCHECK(notifier);
SetContextLifecycleNotifier(notifier);
}
Wrapper(const Wrapper&) = delete;
@@ -97,19 +95,19 @@ class HeapMojoAssociatedRemote {
Wrapper(Wrapper&&) = default;
Wrapper& operator=(Wrapper&&) = default;
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
ContextLifecycleObserver::Trace(visitor);
}
- void Dispose() { associated_remote_.reset(); }
-
mojo::AssociatedRemote<Interface>& associated_remote() {
return associated_remote_;
}
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
associated_remote_.reset();
}
diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote_test.cc
index d866a76d33a..c3a7faf763a 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote_test.cc
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote_test.cc
@@ -37,7 +37,7 @@ class MockContext final : public GarbageCollected<MockContext>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -77,45 +77,12 @@ class AssociatedRemoteOwner
return associated_remote_;
}
- void Trace(Visitor* visitor) { visitor->Trace(associated_remote_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(associated_remote_); }
HeapMojoAssociatedRemote<sample::blink::Service, Mode> associated_remote_;
};
template <HeapMojoWrapperMode Mode>
-class HeapMojoAssociatedRemoteGCBaseTest : 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<AssociatedRemoteOwner<Mode>>(context_);
- scoped_refptr<base::NullTaskRunner> null_task_runner =
- base::MakeRefCounted<base::NullTaskRunner>();
- impl_.associated_receiver().Bind(
- owner_->associated_remote().BindNewEndpointAndPassReceiver(
- null_task_runner));
- impl_.associated_receiver().set_disconnect_handler(WTF::Bind(
- [](HeapMojoAssociatedRemoteGCBaseTest* associated_remote_test) {
- associated_remote_test->run_loop().Quit();
- associated_remote_test->disconnected() = true;
- },
- WTF::Unretained(this)));
- }
-
- ServiceImpl impl_;
- Persistent<MockContext> context_;
- Persistent<AssociatedRemoteOwner<Mode>> owner_;
- base::RunLoop run_loop_;
- bool disconnected_ = false;
-};
-
-template <HeapMojoWrapperMode Mode>
class HeapMojoAssociatedRemoteDestroyContextBaseTest : public TestSupportingGC {
protected:
void SetUp() override {
@@ -190,53 +157,25 @@ class HeapMojoAssociatedRemoteMoveBaseTest : public TestSupportingGC {
} // namespace
-class HeapMojoAssociatedRemoteGCWithContextObserverTest
- : public HeapMojoAssociatedRemoteGCBaseTest<
- HeapMojoWrapperMode::kWithContextObserver> {};
-class HeapMojoAssociatedRemoteGCWithoutContextObserverTest
- : public HeapMojoAssociatedRemoteGCBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
class HeapMojoAssociatedRemoteDestroyContextWithContextObserverTest
: public HeapMojoAssociatedRemoteDestroyContextBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoAssociatedRemoteDestroyContextWithoutContextObserverTest
: public HeapMojoAssociatedRemoteDestroyContextBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoAssociatedRemoteDisconnectWithReasonHandlerWithContextObserverTest
: public HeapMojoAssociatedRemoteDisconnectWithReasonHandlerBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class
HeapMojoAssociatedRemoteDisconnectWithReasonHandlerWithoutContextObserverTest
: public HeapMojoAssociatedRemoteDisconnectWithReasonHandlerBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoAssociatedRemoteMoveWithContextObserverTest
: public HeapMojoAssociatedRemoteMoveBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoAssociatedRemoteMoveWithoutContextObserverTest
: public HeapMojoAssociatedRemoteMoveBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
-
-// Make HeapAssociatedRemote with context observer garbage collected and check
-// that the connection is disconnected right after the marking phase.
-TEST_F(HeapMojoAssociatedRemoteGCWithContextObserverTest, ResetsOnGC) {
- ClearOwner();
- EXPECT_FALSE(disconnected());
- PreciselyCollectGarbage();
- run_loop().Run();
- EXPECT_TRUE(disconnected());
- CompleteSweepingIfNeeded();
-}
-
-// Make HeapAssociatedRemote without context observer garbage collected and
-// check that the connection is disconnected right after the marking phase.
-TEST_F(HeapMojoAssociatedRemoteGCWithoutContextObserverTest, ResetsOnGC) {
- ClearOwner();
- EXPECT_FALSE(disconnected());
- PreciselyCollectGarbage();
- run_loop().Run();
- EXPECT_TRUE(disconnected());
- CompleteSweepingIfNeeded();
-}
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
// Destroy the context with context observer and check that the connection is
// disconnected.
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
index dcaba9dae1a..8079ec94b9f 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h
@@ -8,6 +8,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -69,7 +70,7 @@ class HeapMojoReceiver {
return wrapper_->receiver().WaitForIncomingCall();
}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
FRIEND_TEST_ALL_PREFIXES(HeapMojoReceiverGCWithContextObserverTest,
@@ -87,7 +88,7 @@ class HeapMojoReceiver {
SetContextLifecycleNotifier(notifier);
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(owner_);
ContextLifecycleObserver::Trace(visitor);
}
@@ -98,7 +99,9 @@ class HeapMojoReceiver {
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
receiver_.reset();
}
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
index fc9342706c7..7100593da9b 100644
--- 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
@@ -9,6 +9,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -24,18 +25,20 @@ namespace blink {
// that the interface is not used after ContextDestroyed().
template <typename Interface,
typename Owner,
- HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver>
+ HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver,
+ typename ContextType = void>
class HeapMojoReceiverSet {
DISALLOW_NEW();
public:
+ using ContextTraits = mojo::ReceiverSetContextTraits<ContextType>;
+ using Context = typename ContextTraits::Type;
explicit HeapMojoReceiverSet(Owner* owner, ContextLifecycleNotifier* context)
: wrapper_(MakeGarbageCollected<Wrapper>(owner, context)) {
static_assert(std::is_base_of<Interface, Owner>::value,
"Owner should implement Interface");
static_assert(IsGarbageCollectedType<Owner>::value,
"Owner needs to be a garbage collected object");
- DCHECK(context);
}
HeapMojoReceiverSet(const HeapMojoReceiverSet&) = delete;
HeapMojoReceiverSet& operator=(const HeapMojoReceiverSet&) = delete;
@@ -48,6 +51,14 @@ class HeapMojoReceiverSet {
task_runner);
}
+ mojo::ReceiverId Add(mojo::PendingReceiver<Interface> receiver,
+ Context context,
+ scoped_refptr<base::SequencedTaskRunner> task_runner) {
+ DCHECK(task_runner);
+ return wrapper_->receiver_set().Add(wrapper_->owner(), std::move(receiver),
+ std::move(context), task_runner);
+ }
+
bool Remove(mojo::ReceiverId id) {
return wrapper_->receiver_set().Remove(id);
}
@@ -58,7 +69,13 @@ class HeapMojoReceiverSet {
return wrapper_->receiver_set().HasReceiver(id);
}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ bool empty() const { return wrapper_->receiver_set().empty(); }
+ size_t size() const { return wrapper_->receiver_set().size(); }
+ const Context& current_context() const {
+ return wrapper_->receiver_set().current_context();
+ }
+
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
FRIEND_TEST_ALL_PREFIXES(HeapMojoReceiverSetGCWithContextObserverTest,
@@ -76,25 +93,29 @@ class HeapMojoReceiverSet {
SetContextLifecycleNotifier(notifier);
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(owner_);
ContextLifecycleObserver::Trace(visitor);
}
void Dispose() { receiver_set_.Clear(); }
- mojo::ReceiverSet<Interface>& receiver_set() { return receiver_set_; }
+ mojo::ReceiverSet<Interface, ContextType>& receiver_set() {
+ return receiver_set_;
+ }
Owner* owner() { return owner_; }
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
receiver_set_.Clear();
}
private:
Member<Owner> owner_;
- mojo::ReceiverSet<Interface> receiver_set_;
+ mojo::ReceiverSet<Interface, ContextType> receiver_set_;
};
Member<Wrapper> wrapper_;
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
index 64f04ddb71a..d5a28bc604c 100644
--- 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
@@ -6,6 +6,7 @@
#include <utility>
+#include <string>
#include "base/test/null_task_runner.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote.h"
@@ -43,7 +44,7 @@ class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -52,24 +53,23 @@ class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>,
HeapObserverList<ContextLifecycleObserver> observers_;
};
-template <HeapMojoWrapperMode Mode>
+template <HeapMojoWrapperMode Mode, typename ContextType>
class HeapMojoReceiverSetGCBaseTest;
-template <HeapMojoWrapperMode Mode>
-class GCOwner : public GarbageCollected<GCOwner<Mode>>,
+template <HeapMojoWrapperMode Mode, typename ContextType>
+class GCOwner : public GarbageCollected<GCOwner<Mode, ContextType>>,
public sample::blink::Service {
public:
explicit GCOwner(FakeContextNotifier* context,
- HeapMojoReceiverSetGCBaseTest<Mode>* test)
+ HeapMojoReceiverSetGCBaseTest<Mode, ContextType>* test)
: receiver_set_(this, context), test_(test) {
test_->set_is_owner_alive(true);
}
- void Dispose() {
- test_->set_is_owner_alive(false);
- }
- void Trace(Visitor* visitor) { visitor->Trace(receiver_set_); }
+ void Dispose() { test_->set_is_owner_alive(false); }
+ void Trace(Visitor* visitor) const { visitor->Trace(receiver_set_); }
- HeapMojoReceiverSet<sample::blink::Service, GCOwner, Mode>& receiver_set() {
+ HeapMojoReceiverSet<sample::blink::Service, GCOwner, Mode, ContextType>&
+ receiver_set() {
return receiver_set_;
}
@@ -80,18 +80,19 @@ class GCOwner : public GarbageCollected<GCOwner<Mode>>,
void GetPort(mojo::PendingReceiver<sample::blink::Port> receiver) override {}
private:
- HeapMojoReceiverSet<sample::blink::Service, GCOwner, Mode> receiver_set_;
- HeapMojoReceiverSetGCBaseTest<Mode>* test_;
+ HeapMojoReceiverSet<sample::blink::Service, GCOwner, Mode, ContextType>
+ receiver_set_;
+ HeapMojoReceiverSetGCBaseTest<Mode, ContextType>* test_;
};
-template <HeapMojoWrapperMode Mode>
+template <HeapMojoWrapperMode Mode, typename ContextType>
class HeapMojoReceiverSetGCBaseTest : public TestSupportingGC {
public:
FakeContextNotifier* context() { return context_; }
scoped_refptr<base::NullTaskRunner> task_runner() {
return null_task_runner_;
}
- GCOwner<Mode>* owner() { return owner_; }
+ GCOwner<Mode, ContextType>* owner() { return owner_; }
void set_is_owner_alive(bool alive) { is_owner_alive_ = alive; }
void ClearOwner() { owner_ = nullptr; }
@@ -99,7 +100,7 @@ class HeapMojoReceiverSetGCBaseTest : public TestSupportingGC {
protected:
void SetUp() override {
context_ = MakeGarbageCollected<FakeContextNotifier>();
- owner_ = MakeGarbageCollected<GCOwner<Mode>>(context(), this);
+ owner_ = MakeGarbageCollected<GCOwner<Mode, ContextType>>(context(), this);
}
void TearDown() override {
owner_ = nullptr;
@@ -107,7 +108,7 @@ class HeapMojoReceiverSetGCBaseTest : public TestSupportingGC {
}
Persistent<FakeContextNotifier> context_;
- Persistent<GCOwner<Mode>> owner_;
+ Persistent<GCOwner<Mode, ContextType>> owner_;
bool is_owner_alive_ = false;
scoped_refptr<base::NullTaskRunner> null_task_runner_ =
base::MakeRefCounted<base::NullTaskRunner>();
@@ -117,10 +118,16 @@ class HeapMojoReceiverSetGCBaseTest : public TestSupportingGC {
class HeapMojoReceiverSetGCWithContextObserverTest
: public HeapMojoReceiverSetGCBaseTest<
- HeapMojoWrapperMode::kWithContextObserver> {};
+ HeapMojoWrapperMode::kWithContextObserver,
+ void> {};
+class HeapMojoReceiverSetStringContextGCWithContextObserverTest
+ : public HeapMojoReceiverSetGCBaseTest<
+ HeapMojoWrapperMode::kWithContextObserver,
+ std::string> {};
class HeapMojoReceiverSetGCWithoutContextObserverTest
: public HeapMojoReceiverSetGCBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver,
+ void> {};
// GC the HeapMojoReceiverSet with context observer and verify that the receiver
// is no longer part of the set, and that the service was deleted.
@@ -203,4 +210,70 @@ TEST_F(HeapMojoReceiverSetGCWithoutContextObserverTest, ClearLeavesSetEmpty) {
EXPECT_FALSE(receiver_set.HasReceiver(rid));
}
+// Add several receiver and confirm that receiver_set holds properly.
+TEST_F(HeapMojoReceiverSetGCWithContextObserverTest, AddSeveralReceiverSet) {
+ auto& receiver_set = owner()->receiver_set();
+
+ EXPECT_TRUE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 0u);
+
+ auto receiver_1 = mojo::PendingReceiver<sample::blink::Service>(
+ mojo::MessagePipe().handle0);
+ mojo::ReceiverId rid_1 =
+ receiver_set.Add(std::move(receiver_1), task_runner());
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_1));
+ EXPECT_FALSE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 1u);
+
+ auto receiver_2 = mojo::PendingReceiver<sample::blink::Service>(
+ mojo::MessagePipe().handle0);
+ mojo::ReceiverId rid_2 =
+ receiver_set.Add(std::move(receiver_2), task_runner());
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_1));
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_2));
+ EXPECT_FALSE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 2u);
+
+ receiver_set.Clear();
+
+ EXPECT_FALSE(receiver_set.HasReceiver(rid_1));
+ EXPECT_FALSE(receiver_set.HasReceiver(rid_2));
+ EXPECT_TRUE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 0u);
+}
+
+// Add several receiver with context and confirm that receiver_set holds
+// properly.
+TEST_F(HeapMojoReceiverSetStringContextGCWithContextObserverTest,
+ AddSeveralReceiverSetWithContext) {
+ auto& receiver_set = owner()->receiver_set();
+
+ EXPECT_TRUE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 0u);
+
+ auto receiver_1 = mojo::PendingReceiver<sample::blink::Service>(
+ mojo::MessagePipe().handle0);
+ mojo::ReceiverId rid_1 = receiver_set.Add(
+ std::move(receiver_1), std::string("context1"), task_runner());
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_1));
+ EXPECT_FALSE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 1u);
+
+ auto receiver_2 = mojo::PendingReceiver<sample::blink::Service>(
+ mojo::MessagePipe().handle0);
+ mojo::ReceiverId rid_2 = receiver_set.Add(
+ std::move(receiver_2), std::string("context2"), task_runner());
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_1));
+ EXPECT_TRUE(receiver_set.HasReceiver(rid_2));
+ EXPECT_FALSE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 2u);
+
+ receiver_set.Clear();
+
+ EXPECT_FALSE(receiver_set.HasReceiver(rid_1));
+ EXPECT_FALSE(receiver_set.HasReceiver(rid_2));
+ EXPECT_TRUE(receiver_set.empty());
+ EXPECT_EQ(receiver_set.size(), 0u);
+}
+
} // 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
index 8605a107392..82b82fe2b85 100644
--- 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
#include "base/test/null_task_runner.h"
+#include "base/test/scoped_feature_list.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"
@@ -11,6 +12,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -39,7 +41,7 @@ class MockContext final : public GarbageCollected<MockContext>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -73,7 +75,7 @@ class ReceiverOwner : public GarbageCollected<ReceiverOwner<Mode>>,
return receiver_;
}
- void Trace(Visitor* visitor) { visitor->Trace(receiver_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(receiver_); }
private:
// sample::blink::Service implementation
@@ -178,19 +180,22 @@ class HeapMojoReceiverGCWithContextObserverTest
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoReceiverGCWithoutContextObserverTest
: public HeapMojoReceiverGCBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoReceiverDestroyContextWithContextObserverTest
: public HeapMojoReceiverDestroyContextBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoReceiverDestroyContextWithoutContextObserverTest
: public HeapMojoReceiverDestroyContextBaseTest<
HeapMojoWrapperMode::kWithoutContextObserver> {};
+class HeapMojoReceiverDestroyContextForceWithoutContextObserverTest
+ : public HeapMojoReceiverDestroyContextBaseTest<
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoReceiverDisconnectWithReasonHandlerWithContextObserverTest
: public HeapMojoReceiverDisconnectWithReasonHandlerBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoReceiverDisconnectWithReasonHandlerWithoutContextObserverTest
: public HeapMojoReceiverDisconnectWithReasonHandlerBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
// Make HeapMojoReceiver with context observer garbage collected and check that
// the connection is disconnected right after the marking phase.
@@ -235,9 +240,32 @@ TEST_F(HeapMojoReceiverDestroyContextWithContextObserverTest,
EXPECT_FALSE(owner_->receiver().is_bound());
}
+// Destroy the context with context observer and check that the connection is
+// disconnected.
+TEST_F(HeapMojoReceiverDestroyContextWithoutContextObserverTest,
+ ResetsOnContextDestroyedWhenFinchEnabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeaturesAndParameters(
+ {{kHeapMojoUseContextObserver, {}}}, {});
+ 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,
+ ResetsOnContextDestroyedWhenFinchDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeaturesAndParameters({}, {kHeapMojoUseContextObserver});
+ EXPECT_TRUE(owner_->receiver().is_bound());
+ context_->NotifyContextDestroyed();
+ EXPECT_TRUE(owner_->receiver().is_bound());
+}
+
+// Destroy the context without context observer and check that the connection is
+// still connected.
+TEST_F(HeapMojoReceiverDestroyContextForceWithoutContextObserverTest,
ResetsOnContextDestroyed) {
EXPECT_TRUE(owner_->receiver().is_bound());
context_->NotifyContextDestroyed();
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
index d5bd21207df..5fce5c8a6b9 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h
@@ -10,6 +10,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -18,8 +19,8 @@ namespace blink {
// 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.
+// HeapMojoRemote resets the mojo connection when the associated
+// ExecutionContext is detached.
// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure
// that the interface is not used after ContextDestroyed().
@@ -70,13 +71,12 @@ class HeapMojoRemote {
mojo::PendingFlush FlushAsync() { return wrapper_->remote().FlushAsync(); }
void FlushForTesting() { return wrapper_->remote().FlushForTesting(); }
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
- // Garbage collected wrapper class to add a prefinalizer.
+ // Garbage collected wrapper class to add ContextLifecycleObserver.
class Wrapper final : public GarbageCollected<Wrapper>,
public ContextLifecycleObserver {
- USING_PRE_FINALIZER(Wrapper, Dispose);
USING_GARBAGE_COLLECTED_MIXIN(Wrapper);
public:
@@ -88,17 +88,17 @@ class HeapMojoRemote {
Wrapper(Wrapper&&) = default;
Wrapper& operator=(Wrapper&&) = default;
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
ContextLifecycleObserver::Trace(visitor);
}
- void Dispose() { remote_.reset(); }
-
mojo::Remote<Interface>& remote() { return remote_; }
// ContextLifecycleObserver methods
void ContextDestroyed() override {
- if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
remote_.reset();
}
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
index e6dafd1b7e8..0c67bba174b 100644
--- 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
#include "base/test/null_task_runner.h"
+#include "base/test/scoped_feature_list.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"
@@ -11,6 +12,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -39,7 +41,7 @@ class MockContext final : public GarbageCollected<MockContext>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -74,44 +76,12 @@ class RemoteOwner : public GarbageCollected<RemoteOwner<Mode>> {
HeapMojoRemote<sample::blink::Service, Mode>& remote() { return remote_; }
- void Trace(Visitor* visitor) { visitor->Trace(remote_); }
+ void Trace(Visitor* visitor) const { 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 {
@@ -180,52 +150,27 @@ class HeapMojoRemoteMoveBaseTest : public TestSupportingGC {
} // 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> {};
+class HeapMojoRemoteDestroyContextForceWithoutContextObserverTest
+ : public HeapMojoRemoteDestroyContextBaseTest<
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoRemoteDisconnectWithReasonHandlerWithContextObserverTest
: public HeapMojoRemoteDisconnectWithReasonHandlerBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoRemoteDisconnectWithReasonHandlerWithoutContextObserverTest
: public HeapMojoRemoteDisconnectWithReasonHandlerBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
class HeapMojoRemoteMoveWithContextObserverTest
: public HeapMojoRemoteMoveBaseTest<
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoRemoteMoveWithoutContextObserverTest
: public HeapMojoRemoteMoveBaseTest<
- 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();
-}
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
// Destroy the context with context observer and check that the connection is
// disconnected.
@@ -237,8 +182,31 @@ TEST_F(HeapMojoRemoteDestroyContextWithContextObserverTest,
}
// Destroy the context without context observer and check that the connection is
+// disconnected.
+TEST_F(HeapMojoRemoteDestroyContextWithoutContextObserverTest,
+ ResetsOnContextDestroyedWhenFinchEnabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeaturesAndParameters(
+ {{kHeapMojoUseContextObserver, {}}}, {});
+ 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,
+ ResetsOnContextDestroyedWhenFinchDisabled) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitWithFeaturesAndParameters({}, {kHeapMojoUseContextObserver});
+ EXPECT_TRUE(owner_->remote().is_bound());
+ context_->NotifyContextDestroyed();
+ EXPECT_TRUE(owner_->remote().is_bound());
+}
+
+// Destroy the context without context observer and check that the connection is
+// still connected.
+TEST_F(HeapMojoRemoteDestroyContextForceWithoutContextObserverTest,
ResetsOnContextDestroyed) {
EXPECT_TRUE(owner_->remote().is_bound());
context_->NotifyContextDestroyed();
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
index 7b99f1aac5b..6a284d97995 100644
--- 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
@@ -9,6 +9,7 @@
#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/features.h"
#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
namespace blink {
@@ -18,8 +19,7 @@ namespace blink {
// 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.
+// connection when the associated ExecutionContext is detached.
template <typename Interface,
typename Deleter = std::default_delete<Interface>,
HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver>
@@ -32,9 +32,7 @@ class HeapMojoUniqueReceiverSet {
mojo::UniquePtrImplRefTraits<Interface, Deleter>>::ImplPointerType;
explicit HeapMojoUniqueReceiverSet(ContextLifecycleNotifier* context)
- : wrapper_(MakeGarbageCollected<Wrapper>(context)) {
- DCHECK(context);
- }
+ : wrapper_(MakeGarbageCollected<Wrapper>(context)) {}
HeapMojoUniqueReceiverSet(const HeapMojoUniqueReceiverSet&) = delete;
HeapMojoUniqueReceiverSet& operator=(const HeapMojoUniqueReceiverSet&) =
delete;
@@ -57,13 +55,12 @@ class HeapMojoUniqueReceiverSet {
return wrapper_->receiver_set().HasReceiver(id);
}
- void Trace(Visitor* visitor) { visitor->Trace(wrapper_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); }
private:
- // Garbage collected wrapper class to add a prefinalizer.
+ // Garbage collected wrapper class to add ContextLifecycleObserver.
class Wrapper final : public GarbageCollected<Wrapper>,
public ContextLifecycleObserver {
- USING_PRE_FINALIZER(Wrapper, Dispose);
USING_GARBAGE_COLLECTED_MIXIN(Wrapper);
public:
@@ -71,19 +68,19 @@ class HeapMojoUniqueReceiverSet {
SetContextLifecycleNotifier(notifier);
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const 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)
+ if (Mode == HeapMojoWrapperMode::kWithContextObserver ||
+ (Mode == HeapMojoWrapperMode::kWithoutContextObserver &&
+ base::FeatureList::IsEnabled(kHeapMojoUseContextObserver)))
receiver_set_.Clear();
}
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
index ec6a5d3e50f..4280837e38b 100644
--- 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
@@ -39,7 +39,7 @@ class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>,
});
}
- void Trace(Visitor* visitor) override {
+ void Trace(Visitor* visitor) const override {
visitor->Trace(observers_);
ContextLifecycleNotifier::Trace(visitor);
}
@@ -52,7 +52,7 @@ 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_); }
+ void Trace(Visitor* visitor) const { visitor->Trace(receiver_set_); }
HeapMojoUniqueReceiverSet<sample::blink::Service,
std::default_delete<sample::blink::Service>,
@@ -100,7 +100,7 @@ class HeapMojoUniqueReceiverSetWithContextObserverTest
HeapMojoWrapperMode::kWithContextObserver> {};
class HeapMojoUniqueReceiverSetWithoutContextObserverTest
: public HeapMojoUniqueReceiverSetBaseTest<
- HeapMojoWrapperMode::kWithoutContextObserver> {};
+ HeapMojoWrapperMode::kForceWithoutContextObserver> {};
} // namespace
@@ -125,50 +125,6 @@ class MockService : public sample::blink::Service {
} // 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,
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
index f59f0e0e8dc..4362a72a71d 100644
--- 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
@@ -18,6 +18,10 @@ enum class HeapMojoWrapperMode {
// But, it will not reset the mojo connection when the associated
// ExecutionContext is detached.
kWithoutContextObserver,
+ // We are now experimenting with deprecating kWithoutContextObserver.
+ // kWithoutContextObserver is ignored in the Finch experiment. To enforce
+ // kWithoutContextObserver, use kForceWithoutContextObserver.
+ kForceWithoutContextObserver,
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h
index a73f44cb6ac..afbb8339c5c 100644
--- a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h
+++ b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits.h
@@ -6,7 +6,6 @@
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_STRING16_MOJOM_TRAITS_H_
#include "base/containers/span.h"
-#include "base/logging.h"
#include "base/strings/string16.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
#include "mojo/public/mojom/base/string16.mojom-blink.h"
diff --git a/chromium/third_party/blink/renderer/platform/network/BUILD.gn b/chromium/third_party/blink/renderer/platform/network/BUILD.gn
index 983c4aba408..c36f20ba4ed 100644
--- a/chromium/third_party/blink/renderer/platform/network/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/network/BUILD.gn
@@ -29,10 +29,6 @@ blink_platform_sources("network") {
"content_security_policy_response_headers.h",
"encoded_form_data.cc",
"encoded_form_data.h",
- "encoded_form_data_element_mojom_traits.cc",
- "encoded_form_data_element_mojom_traits.h",
- "encoded_form_data_mojom_traits.cc",
- "encoded_form_data_mojom_traits.h",
"form_data_encoder.cc",
"form_data_encoder.h",
"header_field_tokenizer.cc",
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 cbb1eed257e..51433fe9bea 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
@@ -46,6 +46,7 @@ class FetchAPIRequestBodyDataView;
} // namespace mojom
class BlobDataHandle;
+class ResourceRequestBody;
class WrappedDataPipeGetter;
class PLATFORM_EXPORT FormDataElement final {
@@ -155,8 +156,8 @@ class PLATFORM_EXPORT EncodedFormData : public RefCounted<EncodedFormData> {
bool IsSafeToSendToAnotherThread() const;
private:
- friend struct mojo::StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
- scoped_refptr<blink::EncodedFormData>>;
+ friend struct mojo::StructTraits<mojom::FetchAPIRequestBodyDataView,
+ ResourceRequestBody>;
EncodedFormData();
EncodedFormData(const EncodedFormData&);
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.typemap b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.typemap
deleted file mode 100644
index 7057f7ec3b4..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.typemap
+++ /dev/null
@@ -1,10 +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.
-
-mojom = "//third_party/blink/public/mojom/fetch/fetch_api_request.mojom"
-public_headers =
- [ "//third_party/blink/renderer/platform/network/encoded_form_data.h" ]
-traits_headers = [ "//third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h" ]
-
-type_mappings = [ "blink.mojom.FetchAPIRequestBody=::scoped_refptr<::blink::EncodedFormData>[nullable_is_same_type,copyable_pass_by_value]" ]
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap
deleted file mode 100644
index aac97c6e9e5..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element.typemap
+++ /dev/null
@@ -1,11 +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.
-
-mojom = "//third_party/blink/public/mojom/fetch/fetch_api_request.mojom"
-public_headers =
- [ "//third_party/blink/renderer/platform/network/encoded_form_data.h" ]
-traits_headers = [ "//third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h" ]
-
-type_mappings =
- [ "blink.mojom.FetchAPIDataElement=::blink::FormDataElement[move_only]" ]
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
deleted file mode 100644
index d95ad2f00f0..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc
+++ /dev/null
@@ -1,167 +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 <utility>
-
-#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
-
-#include "base/feature_list.h"
-#include "mojo/public/cpp/base/file_mojom_traits.h"
-#include "mojo/public/cpp/base/file_path_mojom_traits.h"
-#include "mojo/public/cpp/base/time_mojom_traits.h"
-#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "mojo/public/cpp/bindings/string_traits_wtf.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/mojom/data_pipe_getter.mojom-blink.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/platform/file_path_conversion.h"
-#include "third_party/blink/public/platform/platform.h"
-#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
-
-namespace mojo {
-
-// static
-network::mojom::DataElementType
-StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::type(const blink::FormDataElement& data) {
- switch (data.type_) {
- case blink::FormDataElement::kData:
- return network::mojom::DataElementType::kBytes;
- case blink::FormDataElement::kEncodedFile:
- return network::mojom::DataElementType::kFile;
- case blink::FormDataElement::kEncodedBlob: {
- if (data.optional_blob_data_handle_)
- return network::mojom::DataElementType::kDataPipe;
- return network::mojom::DataElementType::kBlob;
- }
- case blink::FormDataElement::kDataPipe:
- return network::mojom::DataElementType::kDataPipe;
- }
- NOTREACHED();
- return network::mojom::DataElementType::kUnknown;
-}
-
-// static
-base::span<const uint8_t>
-StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::buf(const blink::FormDataElement& data) {
- return base::make_span(reinterpret_cast<const uint8_t*>(data.data_.data()),
- data.data_.size());
-}
-
-// static
-base::File
-StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::file(const blink::FormDataElement& data) {
- return base::File();
-}
-
-// static
-base::FilePath
-StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::path(const blink::FormDataElement& data) {
- return base::FilePath::FromUTF8Unsafe(data.filename_.Utf8());
-}
-
-// static
-mojo::PendingRemote<network::mojom::blink::DataPipeGetter> StructTraits<
- blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::data_pipe_getter(const blink::FormDataElement&
- data) {
- if (data.type_ == blink::FormDataElement::kDataPipe) {
- if (!data.data_pipe_getter_)
- return mojo::NullRemote();
- mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter;
- data.data_pipe_getter_->GetDataPipeGetter()->Clone(
- data_pipe_getter.InitWithNewPipeAndPassReceiver());
- return data_pipe_getter;
- }
- if (data.type_ == blink::FormDataElement::kEncodedBlob) {
- if (data.optional_blob_data_handle_) {
- mojo::Remote<blink::mojom::blink::Blob> blob_remote(
- mojo::PendingRemote<blink::mojom::blink::Blob>(
- data.optional_blob_data_handle_->CloneBlobRemote().PassPipe(),
- blink::mojom::blink::Blob::Version_));
- mojo::PendingRemote<network::mojom::blink::DataPipeGetter>
- data_pipe_getter_remote;
- blob_remote->AsDataPipeGetter(
- data_pipe_getter_remote.InitWithNewPipeAndPassReceiver());
- return data_pipe_getter_remote;
- }
- }
- return mojo::NullRemote();
-}
-
-// static
-base::Time StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::
- expected_modification_time(const blink::FormDataElement& data) {
- if (data.type_ == blink::FormDataElement::kEncodedFile)
- return data.expected_file_modification_time_.value_or(base::Time());
- return base::Time();
-}
-
-// static
-bool StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement>::
- Read(blink::mojom::FetchAPIDataElementDataView data,
- blink::FormDataElement* out) {
- network::mojom::DataElementType data_type;
- if (!data.ReadType(&data_type)) {
- return false;
- }
- out->file_start_ = data.offset();
- out->file_length_ = data.length();
-
- switch (data_type) {
- case network::mojom::DataElementType::kBytes: {
- out->type_ = blink::FormDataElement::kData;
- // TODO(richard.li): Delete this workaround when type of
- // blink::FormDataElement::data_ is changed to WTF::Vector<uint8_t>
- WTF::Vector<uint8_t> buf;
- if (!data.ReadBuf(&buf)) {
- return false;
- }
- out->data_.AppendRange(buf.begin(), buf.end());
- break;
- }
- case network::mojom::DataElementType::kFile: {
- out->type_ = blink::FormDataElement::kEncodedFile;
- base::FilePath file_path;
- base::Time expected_time;
- if (!data.ReadPath(&file_path) ||
- !data.ReadExpectedModificationTime(&expected_time)) {
- return false;
- }
- if (expected_time.is_null())
- out->expected_file_modification_time_ = base::nullopt;
- else
- out->expected_file_modification_time_ = expected_time;
- out->filename_ = blink::FilePathToString(file_path);
- break;
- }
- case network::mojom::DataElementType::kDataPipe: {
- out->type_ = blink::FormDataElement::kDataPipe;
- auto data_pipe_ptr_remote = data.TakeDataPipeGetter<
- mojo::PendingRemote<network::mojom::blink::DataPipeGetter>>();
- DCHECK(data_pipe_ptr_remote.is_valid());
-
- out->data_pipe_getter_ =
- base::MakeRefCounted<blink::WrappedDataPipeGetter>(
- std::move(data_pipe_ptr_remote));
- break;
- }
- case network::mojom::DataElementType::kBlob:
- case network::mojom::DataElementType::kUnknown:
- case network::mojom::DataElementType::kChunkedDataPipe:
- case network::mojom::DataElementType::kRawFile:
- NOTREACHED();
- return false;
- }
- return true;
-}
-
-} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
deleted file mode 100644
index c81eef75fe8..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h
+++ /dev/null
@@ -1,60 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
-
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/network/public/mojom/url_loader.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
-#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
-
-namespace mojo {
-
-template <>
-struct PLATFORM_EXPORT StructTraits<blink::mojom::FetchAPIDataElementDataView,
- blink::FormDataElement> {
- static network::mojom::DataElementType type(
- const blink::FormDataElement& data);
-
- static base::span<const uint8_t> buf(const blink::FormDataElement& data);
-
- static base::File file(const blink::FormDataElement& data);
-
- static base::FilePath path(const blink::FormDataElement& data);
-
- static const WTF::String& blob_uuid(const blink::FormDataElement& data) {
- return data.blob_uuid_;
- }
-
- static mojo::PendingRemote<network::mojom::blink::DataPipeGetter>
- data_pipe_getter(const blink::FormDataElement& data);
-
- static mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
- chunked_data_pipe_getter(const blink::FormDataElement& data) {
- return mojo::NullRemote();
- }
-
- static uint64_t offset(const blink::FormDataElement& data) {
- return data.file_start_;
- }
-
- static uint64_t length(const blink::FormDataElement& data) {
- if (data.type_ == blink::FormDataElement::kEncodedBlob &&
- data.optional_blob_data_handle_) {
- return data.optional_blob_data_handle_->size();
- }
- return data.file_length_;
- }
-
- static base::Time expected_modification_time(
- const blink::FormDataElement& data);
-
- static bool Read(blink::mojom::FetchAPIDataElementDataView data,
- blink::FormDataElement* out);
-};
-
-} // namespace mojo
-
-#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_ELEMENT_MOJOM_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc
deleted file mode 100644
index 4389fceb4a7..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.cc
+++ /dev/null
@@ -1,29 +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/network/encoded_form_data_mojom_traits.h"
-
-#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
-#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
-#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
-
-namespace mojo {
-
-// static
-bool StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
- scoped_refptr<blink::EncodedFormData>>::
- Read(blink::mojom::FetchAPIRequestBodyDataView in,
- scoped_refptr<blink::EncodedFormData>* out) {
- *out = blink::EncodedFormData::Create();
- if (!in.ReadElements(&((*out)->elements_))) {
- return false;
- }
- (*out)->identifier_ = in.identifier();
- (*out)->contains_password_data_ = in.contains_sensitive_info();
- (*out)->SetBoundary(blink::FormDataEncoder::GenerateUniqueBoundaryString());
-
- return true;
-}
-
-} // namespace mojo
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
deleted file mode 100644
index 07b98ae23b7..00000000000
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h
+++ /dev/null
@@ -1,40 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
-
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
-#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
-
-namespace mojo {
-
-template <>
-struct PLATFORM_EXPORT StructTraits<blink::mojom::FetchAPIRequestBodyDataView,
- scoped_refptr<blink::EncodedFormData>> {
- static bool IsNull(const scoped_refptr<blink::EncodedFormData>& data) {
- return !data;
- }
- static void SetToNull(scoped_refptr<blink::EncodedFormData>* out) {
- *out = nullptr;
- }
- static const WTF::Vector<blink::FormDataElement>& elements(
- const scoped_refptr<blink::EncodedFormData>& data) {
- return data->elements_;
- }
- static int64_t identifier(const scoped_refptr<blink::EncodedFormData>& data) {
- return data->identifier_;
- }
- static bool contains_sensitive_info(
- const scoped_refptr<blink::EncodedFormData>& data) {
- return data->contains_password_data_;
- }
-
- static bool Read(blink::mojom::FetchAPIRequestBodyDataView in,
- scoped_refptr<blink::EncodedFormData>* out);
-};
-
-} // namespace mojo
-
-#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_ENCODED_FORM_DATA_MOJOM_TRAITS_H_
diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
index 4cbe801e38c..d90f3607e4e 100644
--- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
+++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_test.cc
@@ -5,21 +5,11 @@
#include <utility>
#include "base/sequenced_task_runner.h"
-#include "base/test/task_environment.h"
-#include "mojo/public/cpp/base/file_mojom_traits.h"
-#include "mojo/public/cpp/base/file_path_mojom_traits.h"
-#include "mojo/public/cpp/base/time_mojom_traits.h"
-#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/string_traits_wtf.h"
#include "mojo/public/cpp/test_support/test_utils.h"
-#include "services/network/public/mojom/url_loader.mojom-blink.h"
-#include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
-#include "third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.h"
-#include "third_party/blink/renderer/platform/network/encoded_form_data_mojom_traits.h"
-#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -49,11 +39,6 @@ class EncodedFormDataTest : public testing::Test {
}
};
-class EncodedFormDataMojomTraitsTest : public testing::Test {
- protected:
- base::test::TaskEnvironment task_environment_;
-};
-
TEST_F(EncodedFormDataTest, DeepCopy) {
scoped_refptr<EncodedFormData> original(EncodedFormData::Create());
original->AppendData("Foo", 3);
@@ -118,67 +103,5 @@ TEST_F(EncodedFormDataTest, DeepCopy) {
}
}
-TEST_F(EncodedFormDataMojomTraitsTest, Roundtrips_FormDataElement) {
- FormDataElement original1;
- original1.type_ = blink::FormDataElement::kData;
- original1.data_ = {'a', 'b', 'c', 'd'};
- FormDataElement copied1;
- EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
- blink::mojom::blink::FetchAPIDataElement>(&original1, &copied1));
- EXPECT_EQ(original1.type_, copied1.type_);
- EXPECT_EQ(original1.data_, copied1.data_);
-
- FormDataElement original2;
- original2.type_ = blink::FormDataElement::kEncodedFile;
- original2.file_start_ = 0;
- original2.file_length_ = 4;
- original2.filename_ = "file.name";
- original2.expected_file_modification_time_ = base::Time::Now();
- FormDataElement copied2;
- EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
- blink::mojom::blink::FetchAPIDataElement>(&original2, &copied2));
- EXPECT_EQ(original2.type_, copied2.type_);
- EXPECT_EQ(original2.file_start_, copied2.file_start_);
- EXPECT_EQ(original2.file_length_, copied2.file_length_);
- EXPECT_EQ(original2.filename_, copied2.filename_);
- EXPECT_EQ(original2.expected_file_modification_time_,
- copied2.expected_file_modification_time_);
-
- FormDataElement original3;
- original3.type_ = blink::FormDataElement::kEncodedBlob;
- original3.blob_uuid_ = "uuid-test";
- mojo::MessagePipe pipe;
- original3.optional_blob_data_handle_ = BlobDataHandle::Create(
- original3.blob_uuid_, "type-test", 100,
- mojo::PendingRemote<mojom::blink::Blob>(std::move(pipe.handle0), 0));
- FormDataElement copied3;
- EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
- blink::mojom::blink::FetchAPIDataElement>(&original3, &copied3));
- EXPECT_EQ(copied3.type_, blink::FormDataElement::kDataPipe);
-
- FormDataElement original4;
- original4.type_ = blink::FormDataElement::kDataPipe;
- mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter;
- ignore_result(data_pipe_getter.InitWithNewPipeAndPassReceiver());
- original4.data_pipe_getter_ =
- base::MakeRefCounted<blink::WrappedDataPipeGetter>(
- std::move(data_pipe_getter));
- FormDataElement copied4;
- EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
- blink::mojom::blink::FetchAPIDataElement>(&original4, &copied4));
- EXPECT_TRUE(copied4.data_pipe_getter_);
-}
-
-TEST_F(EncodedFormDataMojomTraitsTest, Roundtrips_EncodedFormData) {
- scoped_refptr<EncodedFormData> original1 = EncodedFormData::Create();
- original1->SetIdentifier(1);
- original1->SetContainsPasswordData(true);
- scoped_refptr<EncodedFormData> copied1 = EncodedFormData::Create();
- EXPECT_TRUE(mojo::test::SerializeAndDeserialize<
- blink::mojom::blink::FetchAPIRequestBody>(&original1, &copied1));
- EXPECT_EQ(original1->Identifier(), copied1->Identifier());
- EXPECT_EQ(original1->ContainsPasswordData(), copied1->ContainsPasswordData());
-}
-
} // namespace
} // namespace blink
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 17404bbfa91..4c1eea52ce5 100644
--- a/chromium/third_party/blink/renderer/platform/network/http_parsers.cc
+++ b/chromium/third_party/blink/renderer/platform/network/http_parsers.cc
@@ -97,6 +97,8 @@ blink::ContentSecurityPolicyPtr ConvertToBlink(
ConvertToBlink(std::move(list.second)));
}
policy->upgrade_insecure_requests = policy_in->upgrade_insecure_requests;
+ policy->sandbox = policy_in->sandbox;
+ policy->treat_as_public_address = policy_in->treat_as_public_address;
for (auto& endpoint : policy_in->report_endpoints)
policy->report_endpoints.push_back(String::FromUTF8(endpoint));
@@ -125,6 +127,7 @@ blink::ParsedHeadersPtr ConvertToBlink(ParsedHeadersPtr parsed_headers) {
ConvertToBlink(std::move(parsed_headers->content_security_policy)),
std::move(parsed_headers->cross_origin_embedder_policy),
std::move(parsed_headers->cross_origin_opener_policy),
+ parsed_headers->origin_isolation,
parsed_headers->accept_ch.has_value()
? base::make_optional(
ConvertToBlink(parsed_headers->accept_ch.value()))
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 e35ee692fe3..201e9392300 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
@@ -217,4 +217,69 @@ bool MIMETypeRegistry::IsLosslessImageMIMEType(const String& mime_type) {
EqualIgnoringASCIICase(mime_type, "image/x-png");
}
+bool MIMETypeRegistry::IsXMLMIMEType(const String& mime_type) {
+ if (EqualIgnoringASCIICase(mime_type, "text/xml") ||
+ EqualIgnoringASCIICase(mime_type, "application/xml") ||
+ EqualIgnoringASCIICase(mime_type, "text/xsl"))
+ return true;
+
+ // Per RFCs 3023 and 2045, an XML MIME type is of the form:
+ // ^[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]+/[0-9a-zA-Z_\\-+~!$\\^{}|.%'`#&*]+\+xml$
+
+ int length = mime_type.length();
+ if (length < 7)
+ return false;
+
+ if (mime_type[0] == '/' || mime_type[length - 5] == '/' ||
+ !mime_type.EndsWithIgnoringASCIICase("+xml"))
+ return false;
+
+ bool has_slash = false;
+ for (int i = 0; i < length - 4; ++i) {
+ UChar ch = mime_type[i];
+ if (ch >= '0' && ch <= '9')
+ continue;
+ if (ch >= 'a' && ch <= 'z')
+ continue;
+ if (ch >= 'A' && ch <= 'Z')
+ continue;
+ switch (ch) {
+ case '_':
+ case '-':
+ case '+':
+ case '~':
+ case '!':
+ case '$':
+ case '^':
+ case '{':
+ case '}':
+ case '|':
+ case '.':
+ case '%':
+ case '\'':
+ case '`':
+ case '#':
+ case '&':
+ case '*':
+ continue;
+ case '/':
+ if (has_slash)
+ return false;
+ has_slash = true;
+ continue;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool MIMETypeRegistry::IsPlainTextMIMEType(const String& mime_type) {
+ return mime_type.StartsWithIgnoringASCIICase("text/") &&
+ !(EqualIgnoringASCIICase(mime_type, "text/html") ||
+ EqualIgnoringASCIICase(mime_type, "text/xml") ||
+ EqualIgnoringASCIICase(mime_type, "text/xsl"));
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
index 8d8eb216769..ae0e3ad3bb6 100644
--- a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.h
@@ -118,6 +118,12 @@ class PLATFORM_EXPORT MIMETypeRegistry {
// compression, whose size may be restricted via the
// 'unoptimized-lossless-images' feature policy. (BMP, GIF, PNG, WEBP)
static bool IsLosslessImageMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as XML.
+ static bool IsXMLMIMEType(const String& mime_type);
+
+ // Checks to see if a mime type is suitable for being loaded as plain text.
+ static bool IsPlainTextMIMEType(const String& mime_type);
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc
index 5fa398dd155..7ac5c18c4d7 100644
--- a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc
+++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry_test.cc
@@ -34,4 +34,42 @@ TEST(MIMETypeRegistryTest, PluginMimeTypes) {
MIMETypeRegistry::GetWellKnownMIMETypeForExtension("swf").Utf8());
}
+TEST(MIMETypeRegistryTest, PlainTextMIMEType) {
+ EXPECT_TRUE(MIMETypeRegistry::IsPlainTextMIMEType("text/plain"));
+ EXPECT_TRUE(MIMETypeRegistry::IsPlainTextMIMEType("text/javascript"));
+ EXPECT_TRUE(MIMETypeRegistry::IsPlainTextMIMEType("TEXT/JavaScript"));
+ EXPECT_FALSE(MIMETypeRegistry::IsPlainTextMIMEType("text/html"));
+ EXPECT_FALSE(MIMETypeRegistry::IsPlainTextMIMEType("text/xml"));
+ EXPECT_FALSE(MIMETypeRegistry::IsPlainTextMIMEType("text/xsl"));
+}
+
+TEST(MIMETypeRegistryTest, TextXMLType) {
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("text/xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("Text/xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("tEXt/XML"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/XML"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/x-tra+xML"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/xslt+xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/rdf+Xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("image/svg+xml"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("text/xsl"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("text/XSL"));
+ EXPECT_TRUE(MIMETypeRegistry::IsXMLMIMEType("application/x+xml"));
+
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-custom;a=a+xml"));
+ EXPECT_FALSE(
+ MIMETypeRegistry::IsXMLMIMEType("application/x-custom;a=a+xml ;"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-custom+xml2"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-custom+xml2 "));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-custom+exml"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("text/html"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/xml;"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/xml "));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-what+xml;"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/x-tra+xML;a=2"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/+xML"));
+ EXPECT_FALSE(MIMETypeRegistry::IsXMLMIMEType("application/+xml"));
+}
+
} // namespace blink
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 d88a917ed73..291cecfc644 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
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 dd0ff33cf06..98d58c4a31b 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(Visitor* visitor) override {}
+ void Trace(Visitor* visitor) const override {}
};
RtcDtmfSenderHandler(scoped_refptr<base::SingleThreadTaskRunner> main_thread,
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
index 01f1af1a521..aa08e8a5772 100644
--- 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
@@ -20,8 +20,12 @@
#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/api/test/mock_transformable_video_frame.h"
#include "third_party/webrtc/rtc_base/ref_counted_object.h"
+using ::testing::NiceMock;
+using ::testing::Return;
+
namespace blink {
namespace {
@@ -42,29 +46,11 @@ class MockTransformerCallbackHolder {
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);
+std::unique_ptr<webrtc::TransformableVideoFrameInterface> CreateMockFrame() {
+ auto mock_frame =
+ std::make_unique<NiceMock<webrtc::MockTransformableVideoFrame>>();
+ ON_CALL(*mock_frame.get(), GetSsrc).WillByDefault(Return(kSSRC));
+ return mock_frame;
}
} // namespace
@@ -127,13 +113,13 @@ TEST_F(RTCEncodedVideoStreamTransformerTest,
*webrtc_task_runner_, FROM_HERE,
CrossThreadBindOnce(&webrtc::FrameTransformerInterface::Transform,
encoded_video_stream_transformer_.Delegate(),
- CreateFakeFrame()));
+ CreateMockFrame()));
task_environment_.RunUntilIdle();
}
TEST_F(RTCEncodedVideoStreamTransformerTest, TransformerForwardsFrameToWebRTC) {
EXPECT_CALL(*webrtc_callback_, OnTransformedFrame);
- encoded_video_stream_transformer_.SendFrameToSink(CreateFakeFrame());
+ encoded_video_stream_transformer_.SendFrameToSink(CreateMockFrame());
task_environment_.RunUntilIdle();
}
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 2751f57f056..7c021d74322 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
@@ -73,7 +73,7 @@ class PLATFORM_EXPORT RTCIceCandidatePlatform final
const base::Optional<uint16_t>& RelatedPort() const { return related_port_; }
const String& UsernameFragment() const { return username_fragment_; }
- void Trace(Visitor*) {}
+ void Trace(Visitor*) const {}
private:
void PopulateFields(bool use_username_from_candidate);
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 9e8e23a538a..d529316c074 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
private:
int32_t offer_to_receive_video_;
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 882a6893b69..47e063d842d 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
@@ -21,7 +21,7 @@ namespace blink {
class RTCEncodedAudioStreamTransformer;
class RTCEncodedVideoStreamTransformer;
class RTCRtpSource;
-class WebMediaStreamTrack;
+class MediaStreamComponent;
// Implementations of this interface keep the corresponding WebRTC-layer
// receiver alive through reference counting. Multiple |RTCRtpReceiverPlatform|s
@@ -40,7 +40,7 @@ class PLATFORM_EXPORT RTCRtpReceiverPlatform {
// Note: For convenience, DtlsTransportInformation always returns a value.
// The information is only interesting if DtlsTransport() is non-null.
virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0;
- virtual const WebMediaStreamTrack& Track() const = 0;
+ virtual MediaStreamComponent* Track() const = 0;
virtual Vector<String> StreamIds() const = 0;
virtual Vector<std::unique_ptr<RTCRtpSource>> GetSources() = 0;
virtual void GetStats(RTCStatsReportCallback,
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 b39bca799ea..a8fbc418c9e 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
@@ -21,7 +21,7 @@ class RtcDtmfSenderHandler;
class RTCEncodedAudioStreamTransformer;
class RTCEncodedVideoStreamTransformer;
class RTCVoidRequest;
-class WebMediaStreamTrack;
+class MediaStreamComponent;
// Implementations of this interface keep the corresponding WebRTC-layer sender
// alive through reference counting. Multiple |RTCRtpSenderPlatform|s could
@@ -41,12 +41,12 @@ class PLATFORM_EXPORT RTCRtpSenderPlatform {
// Note: For convenience, DtlsTransportInformation always returns a value.
// The information is only interesting if DtlsTransport() is non-null.
virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0;
- virtual WebMediaStreamTrack Track() const = 0;
+ virtual MediaStreamComponent* Track() 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 void ReplaceTrack(MediaStreamComponent*, RTCVoidRequest*) = 0;
virtual std::unique_ptr<RtcDtmfSenderHandler> GetDtmfSender() const = 0;
virtual std::unique_ptr<webrtc::RtpParameters> GetParameters() const = 0;
virtual void SetParameters(Vector<webrtc::RtpEncodingParameters>,
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 69ea1e6b320..cac5d13028c 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
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 6bc18622c67..34b7d77315b 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
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 083e9c33525..b2a3543b933 100644
--- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc
@@ -22,50 +22,50 @@ namespace blink {
namespace {
-class RTCStatsWhitelist {
+class RTCStatsAllowlist {
public:
- RTCStatsWhitelist() {
- whitelisted_stats_types_.insert(webrtc::RTCCertificateStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCCodecStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCDataChannelStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCIceCandidatePairStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCIceCandidateStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCLocalIceCandidateStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCRemoteIceCandidateStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCMediaStreamStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCMediaStreamTrackStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCPeerConnectionStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType);
- whitelisted_stats_types_.insert(
+ RTCStatsAllowlist() {
+ allowlisted_stats_types_.insert(webrtc::RTCCertificateStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCCodecStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCDataChannelStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCIceCandidatePairStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCIceCandidateStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCLocalIceCandidateStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCRemoteIceCandidateStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCMediaStreamStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCMediaStreamTrackStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCPeerConnectionStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCRTPStreamStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCInboundRTPStreamStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCOutboundRTPStreamStats::kType);
+ allowlisted_stats_types_.insert(
webrtc::RTCRemoteInboundRtpStreamStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCMediaSourceStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCAudioSourceStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCVideoSourceStats::kType);
- whitelisted_stats_types_.insert(webrtc::RTCTransportStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCMediaSourceStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCAudioSourceStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCVideoSourceStats::kType);
+ allowlisted_stats_types_.insert(webrtc::RTCTransportStats::kType);
}
- bool IsWhitelisted(const webrtc::RTCStats& stats) {
- return whitelisted_stats_types_.find(stats.type()) !=
- whitelisted_stats_types_.end();
+ bool IsAllowlisted(const webrtc::RTCStats& stats) {
+ return allowlisted_stats_types_.find(stats.type()) !=
+ allowlisted_stats_types_.end();
}
- void WhitelistStatsForTesting(const char* type) {
- whitelisted_stats_types_.insert(type);
+ void AllowStatsForTesting(const char* type) {
+ allowlisted_stats_types_.insert(type);
}
private:
- std::set<std::string> whitelisted_stats_types_;
+ std::set<std::string> allowlisted_stats_types_;
};
-RTCStatsWhitelist* GetStatsWhitelist() {
- static RTCStatsWhitelist* whitelist = new RTCStatsWhitelist();
- return whitelist;
+RTCStatsAllowlist* GetStatsAllowlist() {
+ static RTCStatsAllowlist* list = new RTCStatsAllowlist();
+ return list;
}
-bool IsWhitelistedStats(const webrtc::RTCStats& stats) {
- return GetStatsWhitelist()->IsWhitelisted(stats);
+bool IsAllowlistedStats(const webrtc::RTCStats& stats) {
+ return GetStatsAllowlist()->IsAllowlisted(stats);
}
// Filters stats that should be surfaced to JS. Stats are surfaced if they're
@@ -74,9 +74,9 @@ bool IsWhitelistedStats(const webrtc::RTCStats& stats) {
std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers(
std::vector<const webrtc::RTCStatsMemberInterface*> stats_members,
const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) {
- // Note that using "is_standarized" avoids having to maintain a whitelist of
+ // Note that using "is_standarized" avoids having to maintain an allowlist of
// every single standardized member, as we do at the "stats object" level
- // with "RTCStatsWhitelist".
+ // with "RTCStatsAllowlist".
base::EraseIf(
stats_members,
[&exposed_group_ids](const webrtc::RTCStatsMemberInterface* member) {
@@ -96,11 +96,11 @@ std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers(
return stats_members;
}
-size_t CountWhitelistedStats(
+size_t CountAllowlistedStats(
const scoped_refptr<const webrtc::RTCStatsReport>& stats_report) {
size_t size = 0;
for (const auto& stats : *stats_report) {
- if (IsWhitelistedStats(stats)) {
+ if (IsAllowlistedStats(stats)) {
++size;
}
}
@@ -123,7 +123,7 @@ RTCStatsReportPlatform::RTCStatsReportPlatform(
it_(stats_report_->begin()),
end_(stats_report_->end()),
exposed_group_ids_(exposed_group_ids),
- size_(CountWhitelistedStats(stats_report)) {
+ size_(CountAllowlistedStats(stats_report)) {
DCHECK(stats_report_);
}
@@ -138,7 +138,7 @@ std::unique_ptr<RTCStatsReportPlatform> RTCStatsReportPlatform::CopyHandle()
std::unique_ptr<RTCStats> RTCStatsReportPlatform::GetStats(
const String& id) const {
const webrtc::RTCStats* stats = stats_report_->Get(id.Utf8());
- if (!stats || !IsWhitelistedStats(*stats))
+ if (!stats || !IsAllowlistedStats(*stats))
return std::unique_ptr<RTCStats>();
return std::make_unique<RTCStats>(stats_report_, stats, exposed_group_ids_);
}
@@ -147,7 +147,7 @@ std::unique_ptr<RTCStats> RTCStatsReportPlatform::Next() {
while (it_ != end_) {
const webrtc::RTCStats& next = *it_;
++it_;
- if (IsWhitelistedStats(next)) {
+ if (IsAllowlistedStats(next)) {
return std::make_unique<RTCStats>(stats_report_, &next,
exposed_group_ids_);
}
@@ -346,8 +346,8 @@ void RTCStatsCollectorCallbackImpl::OnStatsDeliveredOnMainThread(
base::WrapRefCounted(report.get()), exposed_group_ids_));
}
-void WhitelistStatsForTesting(const char* type) {
- GetStatsWhitelist()->WhitelistStatsForTesting(type);
+void AllowStatsForTesting(const char* type) {
+ GetStatsAllowlist()->AllowStatsForTesting(type);
}
} // namespace blink
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 e6e85007988..532ad85a4dd 100644
--- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h
@@ -31,9 +31,9 @@ class RTCStats;
class RTCStatsMember;
// Wrapper around a webrtc::RTCStatsReport. Filters out any stats objects that
-// aren't whitelisted. |filter| controls whether to include only standard
-// members (RTCStatsMemberInterface::is_standardized return true) or not
-// (RTCStatsMemberInterface::is_standardized return false).
+// aren't listed in the allow list. |filter| controls whether to include only
+// standard members (RTCStatsMemberInterface::is_standardized return true) or
+// not (RTCStatsMemberInterface::is_standardized return false).
//
// Note: This class is named |RTCStatsReportPlatform| not to collide with class
// |RTCStatsReport|, from renderer/modules/peerconnection/rtc_stats_report.cc|h.
@@ -62,7 +62,7 @@ class PLATFORM_EXPORT RTCStatsReportPlatform {
webrtc::RTCStatsReport::ConstIterator it_;
const webrtc::RTCStatsReport::ConstIterator end_;
Vector<webrtc::NonStandardGroupId> exposed_group_ids_;
- // Number of whitelisted webrtc::RTCStats in |stats_report_|.
+ // Number of allowlisted webrtc::RTCStats in |stats_report_|.
const size_t size_;
};
@@ -156,7 +156,7 @@ class PLATFORM_EXPORT RTCStatsCollectorCallbackImpl
Vector<webrtc::NonStandardGroupId> exposed_group_ids_;
};
-PLATFORM_EXPORT void WhitelistStatsForTesting(const char* type);
+PLATFORM_EXPORT void AllowStatsForTesting(const char* type);
} // namespace blink
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 e013c6b151d..c5e52fb6aca 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
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 98ec0d03b41..61fce2d9fc5 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
@@ -14,40 +14,40 @@
namespace blink {
-TEST(RTCStatsTest, OnlyIncludeWhitelistedStats_GetStats) {
- const char* not_whitelisted_id = "NotWhitelistedId";
- const char* whitelisted_id = "WhitelistedId";
+TEST(RTCStatsTest, OnlyIncludeAllowlistedStats_GetStats) {
+ const char* not_allowlisted_id = "NotAllowlistedId";
+ const char* allowlisted_id = "AllowlistedId";
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(42);
webrtc_report->AddStats(std::unique_ptr<webrtc::RTCTestStats>(
- new webrtc::RTCTestStats(not_whitelisted_id, 42)));
+ new webrtc::RTCTestStats(not_allowlisted_id, 42)));
webrtc_report->AddStats(std::unique_ptr<webrtc::RTCPeerConnectionStats>(
- new webrtc::RTCPeerConnectionStats(whitelisted_id, 42)));
+ new webrtc::RTCPeerConnectionStats(allowlisted_id, 42)));
RTCStatsReportPlatform report(webrtc_report.get(), {});
- EXPECT_FALSE(report.GetStats(not_whitelisted_id));
- EXPECT_TRUE(report.GetStats(whitelisted_id));
+ EXPECT_FALSE(report.GetStats(not_allowlisted_id));
+ EXPECT_TRUE(report.GetStats(allowlisted_id));
}
-TEST(RTCStatsTest, OnlyIncludeWhitelistedStats_Iteration) {
- const char* not_whitelisted_id = "NotWhitelistedId";
- const char* whitelisted_id = "WhitelistedId";
+TEST(RTCStatsTest, OnlyIncludeAllowlistedStats_Iteration) {
+ const char* not_allowlisted_id = "NotAllowlistedId";
+ const char* allowlisted_id = "AllowlistedId";
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(42);
webrtc_report->AddStats(std::unique_ptr<webrtc::RTCTestStats>(
- new webrtc::RTCTestStats(not_whitelisted_id, 42)));
+ new webrtc::RTCTestStats(not_allowlisted_id, 42)));
webrtc_report->AddStats(std::unique_ptr<webrtc::RTCPeerConnectionStats>(
- new webrtc::RTCPeerConnectionStats(whitelisted_id, 42)));
+ new webrtc::RTCPeerConnectionStats(allowlisted_id, 42)));
RTCStatsReportPlatform report(webrtc_report.get(), {});
- // Only whitelisted stats are counted.
+ // Only allowlisted stats are counted.
EXPECT_EQ(report.Size(), 1u);
std::unique_ptr<RTCStats> stats = report.Next();
EXPECT_TRUE(stats);
- EXPECT_EQ(stats->Id(), whitelisted_id);
+ EXPECT_EQ(stats->Id(), allowlisted_id);
EXPECT_FALSE(report.Next());
}
@@ -78,12 +78,12 @@ TestStats::TestStats(const std::string& id, int64_t timestamp_us)
{webrtc::NonStandardGroupId::kGroupIdForTesting}) {}
} // namespace
-// Similar to how only whitelisted stats objects should be surfaced, only
-// standardized members of the whitelisted objects should be surfaced.
+// Similar to how only allowlisted stats objects should be surfaced, only
+// standardized members of the allowlisted objects should be surfaced.
TEST(RTCStatsTest, OnlyIncludeStandarizedMembers) {
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(42);
- WhitelistStatsForTesting(TestStats::kType);
+ AllowStatsForTesting(TestStats::kType);
webrtc_report->AddStats(std::make_unique<TestStats>("id", 0));
// TestStats has two members, but the non-standard member should be filtered
@@ -98,7 +98,7 @@ TEST(RTCStatsTest, OnlyIncludeStandarizedMembers) {
TEST(RTCStatsTest, IncludeAllMembers) {
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(7);
- WhitelistStatsForTesting(TestStats::kType);
+ AllowStatsForTesting(TestStats::kType);
webrtc_report->AddStats(std::make_unique<TestStats>("id", 0));
// Include both standard and non-standard member.
@@ -115,7 +115,7 @@ TEST(RTCStatsTest, IncludeAllMembers) {
TEST(RTCStatsTest, CopyHandle) {
rtc::scoped_refptr<webrtc::RTCStatsReport> webrtc_report =
webrtc::RTCStatsReport::Create(17);
- WhitelistStatsForTesting(TestStats::kType);
+ AllowStatsForTesting(TestStats::kType);
webrtc_report->AddStats(std::make_unique<TestStats>("id", 0));
// Check that filtering options are preserved during copy.
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 5e835676e73..2794082db61 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
@@ -13,6 +13,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
@@ -99,6 +100,8 @@ media::VideoCodecProfile GuessVideoCodecProfile(
switch (vp9_profile) {
case webrtc::VP9Profile::kProfile2:
return media::VP9PROFILE_PROFILE2;
+ case webrtc::VP9Profile::kProfile1:
+ return media::VP9PROFILE_PROFILE1;
case webrtc::VP9Profile::kProfile0:
default:
return media::VP9PROFILE_PROFILE0;
@@ -125,9 +128,32 @@ void OnRequestOverlayInfo(bool decoder_requires_restart_for_overlay,
std::move(overlay_info_cb).Run(media::OverlayInfo());
}
+void RecordInitializationLatency(base::TimeDelta latency) {
+ base::UmaHistogramTimes("Media.RTCVideoDecoderInitializationLatencyMs",
+ latency);
+}
+
+void RecordReinitializationLatency(base::TimeDelta latency) {
+ base::UmaHistogramTimes("Media.RTCVideoDecoderReinitializationLatencyMs",
+ latency);
+}
+
} // namespace
// static
+std::vector<media::VideoDecoderImplementation>
+RTCVideoDecoderAdapter::SupportedImplementations() {
+#if defined(OS_WIN)
+ if (base::FeatureList::IsEnabled(media::kD3D11VideoDecoder)) {
+ // Push alternate ahead of default to prefer D3D11 decoders over DXVA.
+ return {media::VideoDecoderImplementation::kAlternate,
+ media::VideoDecoderImplementation::kDefault};
+ }
+#endif
+ return {media::VideoDecoderImplementation::kDefault};
+}
+
+// static
std::unique_ptr<RTCVideoDecoderAdapter> RTCVideoDecoderAdapter::Create(
media::GpuVideoAcceleratorFactories* gpu_factories,
const webrtc::SdpVideoFormat& format) {
@@ -152,31 +178,36 @@ std::unique_ptr<RTCVideoDecoderAdapter> RTCVideoDecoderAdapter::Create(
media::kNoTransformation, kDefaultSize, gfx::Rect(kDefaultSize),
kDefaultSize, media::EmptyExtraData(),
media::EncryptionScheme::kUnencrypted);
- if (gpu_factories->IsDecoderConfigSupported(kImplementation, config) ==
- media::GpuVideoAcceleratorFactories::Supported::kFalse) {
- return nullptr;
- }
- // Synchronously verify that the decoder can be initialized.
- std::unique_ptr<RTCVideoDecoderAdapter> rtc_video_decoder_adapter =
- base::WrapUnique(
- new RTCVideoDecoderAdapter(gpu_factories, config, format));
- if (!rtc_video_decoder_adapter->InitializeSync(config)) {
- gpu_factories->GetTaskRunner()->DeleteSoon(
- FROM_HERE, std::move(rtc_video_decoder_adapter));
- return nullptr;
+ for (auto impl : SupportedImplementations()) {
+ std::unique_ptr<RTCVideoDecoderAdapter> rtc_video_decoder_adapter;
+ if (gpu_factories->IsDecoderConfigSupported(impl, config) !=
+ media::GpuVideoAcceleratorFactories::Supported::kFalse) {
+ // Synchronously verify that the decoder can be initialized.
+ rtc_video_decoder_adapter = base::WrapUnique(
+ new RTCVideoDecoderAdapter(gpu_factories, config, format, impl));
+ if (rtc_video_decoder_adapter->InitializeSync(config)) {
+ return rtc_video_decoder_adapter;
+ }
+ // Initialization failed - post delete task and try next supported
+ // implementation, if any.
+ gpu_factories->GetTaskRunner()->DeleteSoon(
+ FROM_HERE, std::move(rtc_video_decoder_adapter));
+ }
}
- return rtc_video_decoder_adapter;
+ return nullptr;
}
RTCVideoDecoderAdapter::RTCVideoDecoderAdapter(
media::GpuVideoAcceleratorFactories* gpu_factories,
const media::VideoDecoderConfig& config,
- const webrtc::SdpVideoFormat& format)
+ const webrtc::SdpVideoFormat& format,
+ media::VideoDecoderImplementation implementation)
: media_task_runner_(gpu_factories->GetTaskRunner()),
gpu_factories_(gpu_factories),
format_(format),
+ implementation_(implementation),
config_(config) {
DVLOG(1) << __func__;
DETACH_FROM_SEQUENCE(decoding_sequence_checker_);
@@ -193,6 +224,7 @@ bool RTCVideoDecoderAdapter::InitializeSync(
DVLOG(3) << __func__;
// Can be called on |worker_thread_| or |decoding_thread_|.
DCHECK(!media_task_runner_->BelongsToCurrentThread());
+ base::TimeTicks start_time = base::TimeTicks::Now();
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
bool result = false;
@@ -207,8 +239,12 @@ bool RTCVideoDecoderAdapter::InitializeSync(
CrossThreadUnretained(this), config,
std::move(init_cb)))) {
// TODO(crbug.com/1076817) Remove if a root cause is found.
- if (!waiter.TimedWait(base::TimeDelta::FromSeconds(10)))
+ if (!waiter.TimedWait(base::TimeDelta::FromSeconds(10))) {
+ RecordInitializationLatency(base::TimeTicks::Now() - start_time);
return false;
+ }
+
+ RecordInitializationLatency(base::TimeTicks::Now() - start_time);
}
return result;
}
@@ -374,7 +410,7 @@ void RTCVideoDecoderAdapter::InitializeOnMediaThread(
media_log_ = std::make_unique<media::NullMediaLog>();
video_decoder_ = gpu_factories_->CreateVideoDecoder(
- media_log_.get(), kImplementation,
+ media_log_.get(), implementation_,
WTF::BindRepeating(&OnRequestOverlayInfo));
if (!video_decoder_) {
@@ -507,6 +543,7 @@ bool RTCVideoDecoderAdapter::ReinitializeSync(
const media::VideoDecoderConfig& config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoding_sequence_checker_);
+ base::TimeTicks start_time = base::TimeTicks::Now();
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
bool result = false;
base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::MANUAL,
@@ -526,6 +563,7 @@ bool RTCVideoDecoderAdapter::ReinitializeSync(
weak_this_, std::move(flush_success_cb),
std::move(flush_fail_cb)))) {
waiter.Wait();
+ RecordReinitializationLatency(base::TimeTicks::Now() - start_time);
}
return result;
}
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 9d67fb60e8b..ded3110791a 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
@@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_VIDEO_DECODER_ADAPTER_H_
#include <memory>
+#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
@@ -53,12 +54,11 @@ 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;
+ // Lists which implementations can be queried, this can vary based on platform
+ // and enabled features.
+ static std::vector<media::VideoDecoderImplementation>
+ SupportedImplementations();
+
// Creates and initializes an RTCVideoDecoderAdapter. Returns nullptr if
// |format| cannot be supported.
// Called on the worker thread.
@@ -95,7 +95,8 @@ class PLATFORM_EXPORT RTCVideoDecoderAdapter : public webrtc::VideoDecoder {
// Called on the worker thread.
RTCVideoDecoderAdapter(media::GpuVideoAcceleratorFactories* gpu_factories,
const media::VideoDecoderConfig& config,
- const webrtc::SdpVideoFormat& format);
+ const webrtc::SdpVideoFormat& format,
+ media::VideoDecoderImplementation implementation);
bool InitializeSync(const media::VideoDecoderConfig& config);
void InitializeOnMediaThread(const media::VideoDecoderConfig& config,
@@ -113,9 +114,10 @@ class PLATFORM_EXPORT RTCVideoDecoderAdapter : public webrtc::VideoDecoder {
FlushDoneCB flush_fail_cb);
// Construction parameters.
- scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
- media::GpuVideoAcceleratorFactories* gpu_factories_;
- webrtc::SdpVideoFormat format_;
+ const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+ media::GpuVideoAcceleratorFactories* const gpu_factories_;
+ const webrtc::SdpVideoFormat format_;
+ const media::VideoDecoderImplementation implementation_;
media::VideoDecoderConfig config_;
// Media thread members.
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 cb12d36124c..70ff644a016 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
@@ -32,9 +32,10 @@ struct CodecConfig {
media::VideoCodecProfile profile;
};
-constexpr std::array<CodecConfig, 6> kCodecConfigs = {{
+constexpr std::array<CodecConfig, 7> kCodecConfigs = {{
{media::kCodecVP8, media::VP8PROFILE_ANY},
{media::kCodecVP9, media::VP9PROFILE_PROFILE0},
+ {media::kCodecVP9, media::VP9PROFILE_PROFILE1},
{media::kCodecVP9, media::VP9PROFILE_PROFILE2},
{media::kCodecH264, media::H264PROFILE_BASELINE},
{media::kCodecH264, media::H264PROFILE_MAIN},
@@ -54,6 +55,9 @@ base::Optional<webrtc::SdpVideoFormat> VdcToWebRtcFormat(
case media::VP9PROFILE_PROFILE0:
vp9_profile = webrtc::VP9Profile::kProfile0;
break;
+ case media::VP9PROFILE_PROFILE1:
+ vp9_profile = webrtc::VP9Profile::kProfile1;
+ break;
case media::VP9PROFILE_PROFILE2:
vp9_profile = webrtc::VP9Profile::kProfile2;
break;
@@ -185,12 +189,15 @@ RTCVideoDecoderFactory::GetSupportedFormats() const {
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);
+ for (auto impl : RTCVideoDecoderAdapter::SupportedImplementations()) {
+ if (gpu_factories_->IsDecoderConfigSupported(impl, config) ==
+ media::GpuVideoAcceleratorFactories::Supported::kTrue) {
+ base::Optional<webrtc::SdpVideoFormat> format =
+ VdcToWebRtcFormat(config);
+ if (format) {
+ supported_formats.push_back(*format);
+ }
+ break;
}
}
}
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
index 4be94202c58..df4f1272c0a 100644
--- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
@@ -5,6 +5,7 @@
#include <stdint.h>
#include "base/bind.h"
+#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
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 9376feb9ced..e773bec36d4 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(Visitor* visitor) {}
+ virtual void Trace(Visitor* visitor) const {}
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 75e3561bd2b..40601e6ef57 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
@@ -8,7 +8,7 @@
#include <memory>
#include <utility>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/optional.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
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 3ed4b1a517a..a9923b0eaee 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
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h"
@@ -129,24 +130,22 @@ void WebRtcVideoTrackSource::OnFrameCaptured(
// rtc::AdaptedVideoTrackSource::OnFrame(). This region is going to be
// relative to the coded frame data, i.e.
// [0, 0, frame->coded_size().width(), frame->coded_size().height()].
- gfx::Rect update_rect;
- int capture_counter = 0;
- bool has_capture_counter = frame->metadata()->GetInteger(
- media::VideoFrameMetadata::CAPTURE_COUNTER, &capture_counter);
- bool has_update_rect = frame->metadata()->GetRect(
- media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &update_rect);
+ base::Optional<int> capture_counter = frame->metadata()->capture_counter;
+ base::Optional<gfx::Rect> update_rect =
+ frame->metadata()->capture_update_rect;
+
const bool has_valid_update_rect =
- has_update_rect && has_capture_counter &&
+ update_rect.has_value() && capture_counter.has_value() &&
previous_capture_counter_.has_value() &&
- (capture_counter == (previous_capture_counter_.value() + 1));
+ (*capture_counter == (*previous_capture_counter_ + 1));
DVLOG(3) << "has_valid_update_rect = " << has_valid_update_rect;
- if (has_capture_counter)
+ if (capture_counter)
previous_capture_counter_ = capture_counter;
if (has_valid_update_rect) {
if (!accumulated_update_rect_) {
accumulated_update_rect_ = update_rect;
} else {
- accumulated_update_rect_->Union(update_rect);
+ accumulated_update_rect_->Union(*update_rect);
}
} else {
accumulated_update_rect_ = base::nullopt;
diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
index bd1958dee5b..585da18ffce 100644
--- a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
+++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
@@ -65,10 +65,8 @@ class WebRtcVideoTrackSourceTest
media::VideoFrame::StorageType storage_type) {
scoped_refptr<media::VideoFrame> frame =
CreateTestFrame(coded_size, visible_rect, natural_size, storage_type);
- frame->metadata()->SetInteger(media::VideoFrameMetadata::CAPTURE_COUNTER,
- capture_counter);
- frame->metadata()->SetRect(media::VideoFrameMetadata::CAPTURE_UPDATE_RECT,
- update_rect);
+ frame->metadata()->capture_counter = capture_counter;
+ frame->metadata()->capture_update_rect = update_rect;
track_source_->OnFrameCaptured(frame);
}
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 ae97eb1994b..4be714dcc95 100644
--- a/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -175,13 +175,6 @@
origin_trial_feature_name: "AppCache",
},
{
- // Use an aspect ratio from the HTML attributes even when we use sizing
- // from CSS.
- // https://github.com/WICG/intrinsicsize-attribute/issues/16
- name: "AspectRatioFromWidthAndHeight",
- status: "stable",
- },
- {
name: "AudioOutputDevices",
// Android does not yet support switching of audio output devices
status: {"Android": "", "default": "stable"},
@@ -259,6 +252,7 @@
},
{
name: "BlockFlowHandlesWebkitLineClamp",
+ status: "stable",
},
{
name: "BlockHTMLParserOnStyleSheets",
@@ -321,15 +315,6 @@
status: "experimental",
},
{
- name: "CloneableNativeFileSystemHandles",
- status: {"Android": "test", "default": "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: "CompositeAfterPaint",
},
{
@@ -361,7 +346,8 @@
},
{
name: "ConversionMeasurement",
- status: "test",
+ origin_trial_feature_name: "ConversionMeasurement",
+ status: "experimental",
},
{
name: "CookieDeprecationMessages",
@@ -388,12 +374,12 @@
name: "CorsRFC1918",
},
{
- name: "CSS3Text",
+ name: "CrossOriginIsolation",
status: "experimental",
},
{
- name: "CSS3TextBreakAnywhere",
- status: "stable",
+ name: "CSS3Text",
+ status: "experimental",
},
{
name: "CSSAspectRatioProperty",
@@ -423,17 +409,9 @@
},
{
// The main content-visibility feature.
- // https://wicg.github.io/display-locking/
+ // https://drafts.csswg.org/css-contain/#content-visibility
name: "CSSContentVisibility",
- status: "experimental",
- implied_by: ["CSSContentVisibilityHiddenMatchable"]
- },
- {
- // The content-visibility activation event which will be replaced by
- // the beforematch event. When beforematch is available, this feaure
- // will be removed.
- name: "CSSContentVisibilityActivationEvent",
- implied_by: ["CSSContentVisibility"]
+ status: "stable",
},
{
// The content-visibility: hidden-matchable feature. This is a planned
@@ -466,13 +444,6 @@
status: "experimental",
},
{
- // Support for CSS contain-intrinsic-size property.
- // https://wicg.github.io/display-locking/contain-intrinsic-size.html
- name: "CSSIntrinsicSize",
- implied_by: ["CSSContentVisibility"],
- status: "stable",
- },
- {
name: "CSSLayoutAPI",
status: "experimental",
},
@@ -486,12 +457,18 @@
status: "test",
},
{
- name: "CSSMarkerPseudoElement",
+ name: "CSSMarkerNestedPseudoElement",
status: "experimental",
},
{
- name: "CSSMaskSourceType",
+ name: "CSSMarkerPseudoElement",
status: "experimental",
+ implied_by: ["CSSMarkerNestedPseudoElement"],
+ },
+ // Enables dependency support for the MatchedPropertiesCache.
+ {
+ name: "CSSMatchedPropertiesCacheDependencies",
+ depends_on: ["CSSCascade"]
},
{
name: "CSSMathStyle",
@@ -549,7 +526,7 @@
// Perform partial style invalidation on web font loading.
// See https://crbug.com/441925 and https://bit.ly/35JjPmq for details.
name: "CSSReducedFontLoadingInvalidations",
- status: "test",
+ status: "stable",
implied_by: ["CSSReducedFontLoadingLayoutInvalidations"],
},
{
@@ -563,6 +540,14 @@
status: "stable",
depends_on: ["CSSCascade"],
},
+ // Support for declarative parts of scroll-animations-1, i.e.
+ // the animation-timeline property and the @scroll-timeline rule.
+ //
+ // https://drafts.csswg.org/scroll-animations-1/#animation-timeline
+ // https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule
+ {
+ name: "CSSScrollTimeline"
+ },
{
name: "CSSSnapSize",
status: "experimental",
@@ -574,7 +559,7 @@
// Support for @property rules.
{
name: "CSSVariables2AtProperty",
- status: "test",
+ status: "stable",
},
// Support for registered custom properties with <image> syntax.
{
@@ -610,6 +595,7 @@
},
{
name: "DeclarativeShadowDOM",
+ origin_trial_feature_name: "DeclarativeShadowDOM",
status: "experimental",
},
{
@@ -621,6 +607,12 @@
status: "test",
},
{
+ name: "DelayAsyncScriptExecutionUntilFinishedParsing",
+ },
+ {
+ name: "DelayAsyncScriptExecutionUntilFirstPaintOrFinishedParsing",
+ },
+ {
name: "DelegatedInkTrails",
status: "test",
},
@@ -629,6 +621,10 @@
status: "experimental",
},
{
+ name: "DigitalGoods",
+ status: "experimental",
+ },
+ {
name: "DisableHardwareNoiseSuppression",
origin_trial_feature_name: "DisableHardwareNoiseSuppression",
status: "experimental",
@@ -679,7 +675,7 @@
},
{
name: "EncryptedMediaPersistentUsageRecordSession",
- status: "test",
+ status: "experimental",
},
{
name: "EnterKeyHintAttribute",
@@ -687,7 +683,7 @@
},
{
name: "EventTiming",
- status: "experimental",
+ status: "stable",
},
{
name: "ExecCommandInJavaScript",
@@ -751,10 +747,6 @@
status: "experimental",
},
{
- name: "FeaturePolicyJavaScriptInterface",
- status: "stable"
- },
- {
name: "FeaturePolicyReporting",
implied_by: ["ExperimentalProductivityFeatures"],
origin_trial_feature_name: "FeaturePolicyReporting",
@@ -764,6 +756,11 @@
name: "FeaturePolicyVibrateFeature"
},
{
+ name: "FetchUploadStreaming",
+ origin_trial_feature_name: "FetchUploadStreaming",
+ status: "experimental",
+ },
+ {
// Also enabled when blink::features::kFileHandlingAPI is overridden
// on the command line (or via chrome://flags).
name: "FileHandling",
@@ -907,10 +904,6 @@
status: "stable",
},
{
- name: "IntersectionObserverV2",
- status: "stable",
- },
- {
// Launched by default. TODO(mythria): cleanup virtual tests and
// other hooks in blink.
name: "IsolatedCodeCache",
@@ -939,7 +932,6 @@
// provides a convenient way for testing legacy layout code path in blink
// unit tests.
name: "LayoutNG",
- // Keep this list in sync with the one in LayoutNGFlexBox below.
implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFragmentItem", "LayoutNGGrid", "EditingNG", "BidiCaretAffinity", "LayoutNGTable", "LayoutNGFragmentTraversal"],
status: "stable",
},
@@ -951,8 +943,7 @@
},
{
name: "LayoutNGFlexBox",
- implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFragmentItem", "LayoutNGGrid", "EditingNG", "BidiCaretAffinity", "LayoutNGTable", "LayoutNGFragmentTraversal"],
- status: "experimental",
+ status: "stable",
},
{
name: "LayoutNGForControls",
@@ -962,6 +953,7 @@
{
name: "LayoutNGFragmentItem",
implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFragmentTraversal"],
+ status: "test",
},
{
// Traverse the fragment tree when painting and hit-testing, instead of
@@ -975,6 +967,7 @@
{
name: "LayoutNGRuby",
depends_on: ["LayoutNG"],
+ status: "experimental",
},
{
name: "LayoutNGTable",
@@ -1004,10 +997,26 @@
// This is enabled by features::kLazyInitializeMediaControls.
},
{
+ // Also enabled when blink::features::kNativeFileSystemAPI is overridden
+ // on the command line (or via chrome://flags).
+ // Used for API surface that will be removed when the Native File System
+ // API is no longer guarded by an origin trial.
+ name: "LegacyNativeFileSystem",
+ status: {"Android": "test", "default": "experimental"},
+ origin_trial_feature_name: "NativeFileSystem2",
+ origin_trial_os: ["win", "macosx", "linux", "chromeos"],
+ },
+ {
name: "LegacyWindowsDWriteFontFallback",
// Enabled by features::kLegacyWindowsDWriteFontFallback;
},
{
+ // TODO(crbug.com/1087043): Remove this once the feature has
+ // landed and no compat issues are reported.
+ name: "LinkDisabledNewSpecBehavior",
+ status: "stable",
+ },
+ {
name:"ManualSlotting",
status:"experimental",
},
@@ -1062,7 +1071,8 @@
status: {"Android": "stable"},
},
{
- name: "MediaControlsUseCutOutByDefault"
+ name: "MediaControlsUseCutOutByDefault",
+ status: "stable",
},
{
name: "MediaDocumentDownloadButton",
@@ -1074,16 +1084,22 @@
{
name: "MediaEngagementBypassAutoplayPolicies",
},
+ // Media Feeds: https://wicg.github.io/media-feeds/
+ // Set to reflect the kMediaFeeds feature.
+ {
+ name: "MediaFeeds",
+ status: "experimental",
+ },
{
name: "MediaLatencyHint",
status: "test",
},
{
- name: "MediaQueryNavigationControls",
+ name: "MediaPreservesPitch",
+ status: "experimental",
},
{
- name: "MediaQueryShape",
- status: "experimental",
+ name: "MediaQueryNavigationControls",
},
{
name: "MediaSession",
@@ -1094,10 +1110,6 @@
status: "stable",
},
{
- name: "MediaSessionSeeking",
- status: "stable",
- },
- {
name: "MediaSourceExperimental",
status: "experimental",
},
@@ -1159,7 +1171,7 @@
{
// Named pages for pagination (the "page" CSS property).
name: "NamedPages",
- status: "experimental",
+ status: "stable",
},
{
// Also enabled when blink::features::kNativeFileSystemAPI is overridden
@@ -1336,6 +1348,11 @@
settable_from_internals: true,
},
{
+ // This flag enables the Manifest parser to handle URL Protocols.
+ name: "ParseUrlProtocolHandler",
+ status: "test",
+ },
+ {
name: "PassPaintVisualRectToCompositor",
},
// This is to add an option to enable the Reveal button on password inputs while waiting ::reveal gets standardized.
@@ -1437,11 +1454,19 @@
origin_trial_feature_name: "Portals",
},
{
- name: "PostAnimationFrame",
- status: "experimental",
+ name: "PreciseMemoryInfo",
},
+ // Prefer not using composited scrolling. Composited scrolling will still
+ // be used if there are other reasons forcing compositing. For consistency,
+ // any code calling Settings::GetPreferCompositingToLCDTextEnabled() should
+ // ensure that this flag overrides the setting.
{
- name: "PreciseMemoryInfo",
+ name: "PreferNonCompositedScrolling",
+ settable_from_internals: true,
+ },
+ {
+ name: 'PrefersReducedData',
+ status: 'experimental',
},
// This feature is deprecated and we are evangelizing affected sites.
// See https://crbug.com/346236 for current status.
@@ -1513,6 +1538,11 @@
status: "stable",
},
{
+ name: "RTCAdaptivePtime",
+ origin_trial_feature_name: "RTCAdaptivePtime",
+ status: "experimental",
+ },
+ {
name: "RtcAudioJitterBufferMaxPackets",
origin_trial_feature_name: "RtcAudioJitterBufferMaxPackets",
status: "experimental",
@@ -1581,6 +1611,10 @@
status: "stable",
},
{
+ name: "ScrollbarGutter",
+ status: "test",
+ },
+ {
name: "ScrollCustomization",
},
{
@@ -1590,7 +1624,7 @@
{
name: "ScrollTimeline",
status: "experimental",
- implied_by: ['AnimationWorklet']
+ implied_by: ['AnimationWorklet', 'CSSScrollTimeline']
},
// Implements documentElement.scrollTop/Left and bodyElement.scrollTop/Left
// as per the spec, matching other Web engines.
@@ -1626,10 +1660,6 @@
name: "ServiceWorkerFetchEventWorkerTiming",
status: "experimental",
},
- {
- name: "SetRootScroller",
- status: "experimental",
- },
// TODO(937746): Web Components v0 is disabled by default, and will be
// removed after M87.
{
@@ -1639,11 +1669,7 @@
origin_trial_allows_insecure: true,
status: "test",
},
- {
- name: "ShadowPiercingDescendantCombinator",
- status: "experimental",
- },
- {
+ {
name: "SharedArrayBuffer",
status: "stable",
},
@@ -1700,6 +1726,10 @@
status: "experimental"
},
{
+ name: "SubresourceWebBundles",
+ status: "experimental"
+ },
+ {
name: "SurfaceEmbeddingFeatures",
status: "stable",
},
@@ -1731,6 +1761,10 @@
status: "stable",
},
{
+ name: "ThirdPartyOriginTrials",
+ status: "test",
+ },
+ {
name: "TimerThrottlingForBackgroundTabs",
status: "stable",
},
@@ -1763,6 +1797,11 @@
origin_trial_feature_name: "RTCInsertableStreams",
implied_by: ["RTCInsertableStreams"],
},
+ // When enabled, enforces new interoperable semantics for 3D transforms.
+ // See crbug.com/1008483.
+ {
+ name: "TransformInterop",
+ },
// This is conditionally set if the platform supports translation.
{
name: "TranslateService"
@@ -1774,6 +1813,7 @@
{
name: "TrustTokens",
origin_trial_feature_name: "TrustTokens",
+ origin_trial_allows_third_party: true,
status: "test",
},
{
@@ -1793,14 +1833,6 @@
status: "test",
},
{
- name: "UnifiedPointerCaptureInBlink",
- status: "stable",
- },
- {
- name: "UnifiedTouchAdjustment",
- status: "stable",
- },
- {
name: "UnoptimizedImagePolicies",
status: "experimental",
origin_trial_feature_name: "UnoptimizedImagePolicies",
@@ -1813,10 +1845,6 @@
implied_by: ["ExperimentalProductivityFeatures"]
},
{
- name: "UserActivationAPI",
- status: "stable",
- },
- {
name: "UserActivationPostMessageTransfer",
},
{
@@ -1827,10 +1855,6 @@
status: "stable"
},
{
- name: "UseWindowsSystemColors",
- status: "stable",
- },
- {
name: "V8IdleTasks",
},
{
@@ -1910,10 +1934,23 @@
status: "experimental",
},
{
+ name: "WebBluetoothRemoteCharacteristicNewWriteValue",
+ status: {
+ "Android": "stable",
+ "ChromeOS": "stable",
+ "MacOSX": "stable",
+ "default": "experimental",
+ },
+ },
+ {
name: "WebBluetoothScanning",
status: "experimental",
},
{
+ name: "WebBluetoothWatchAdvertisements",
+ status: "experimental",
+ },
+ {
name: "WebCodecs",
status: "test",
},
@@ -1967,7 +2004,6 @@
},
{
name: "WebSocketStream",
- origin_trial_feature_name: "WebSocketStream",
status: "experimental",
},
{
@@ -1990,7 +2026,7 @@
{
name: "WebXRAnchors",
depends_on: ["WebXRARModule", "WebXRHitTest"],
- status: "experimental"
+ status: "stable"
},
{
name: "WebXRARModule",
@@ -1998,6 +2034,11 @@
status: "stable",
},
{
+ name: "WebXRCameraAccess",
+ depends_on: ["WebXRARModule"],
+ status: "experimental",
+ },
+ {
name: "WebXRHitTest",
depends_on: ["WebXRARModule"],
status: "stable",
@@ -2008,15 +2049,20 @@
status: "experimental"
},
{
- name: "WebXRIncubations",
+ name: "WebXRLightEstimation",
depends_on: ["WebXRARModule"],
status: "experimental",
},
{
- name: "WebXRLightEstimation",
+ name: "WebXRPlaneDetection",
depends_on: ["WebXRARModule"],
status: "experimental",
},
+ {
+ name: "WebXRReflectionEstimation",
+ depends_on: ["WebXRARModule", "WebXRLightEstimation"],
+ status: "experimental",
+ },
// Extends window placement functionality for multi-screen devices. Also
// exposes requisite information about displays connected to the device.
{
@@ -2024,13 +2070,8 @@
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: "WindowSegments",
+ status: "test"
},
{
name: "XSLT",
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 8cdf3203e6f..249cc560e6b 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -14,6 +14,7 @@ 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",
@@ -62,6 +63,8 @@ blink_platform_sources("scheduler") {
"common/worker_pool.cc",
"main_thread/agent_interference_recorder.cc",
"main_thread/agent_interference_recorder.h",
+ "main_thread/agent_scheduling_strategy.cc",
+ "main_thread/agent_scheduling_strategy.h",
"main_thread/attribution_group.h",
"main_thread/auto_advancing_virtual_time_domain.cc",
"main_thread/auto_advancing_virtual_time_domain.h",
@@ -223,6 +226,7 @@ jumbo_source_set("unit_tests") {
"common/ukm_task_sampler_unittest.cc",
"common/worker_pool_unittest.cc",
"main_thread/agent_interference_recorder_unittest.cc",
+ "main_thread/agent_scheduling_strategy_unittest.cc",
"main_thread/auto_advancing_virtual_time_domain_unittest.cc",
"main_thread/deadline_task_runner_unittest.cc",
"main_thread/frame_scheduler_impl_unittest.cc",
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/DEPS
index 22f963d0b9d..db83e7f5537 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/DEPS
+++ b/chromium/third_party/blink/renderer/platform/scheduler/DEPS
@@ -9,7 +9,6 @@ include_rules = [
"+base/atomic_sequence_num.h",
"+base/atomicops.h",
"+base/bind_helpers.h",
- "+base/callback.h",
"+base/cancelable_callback.h",
"+base/command_line.h",
"+base/compiler_specific.h",
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 c456a695bac..6714ad7244d 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
@@ -54,6 +54,7 @@ class DummyFrameScheduler : public FrameScheduler {
}
void OnFirstContentfulPaint() override {}
void OnFirstMeaningfulPaint() override {}
+ void OnLoad() override {}
bool IsExemptFromBudgetBasedThrottling() const override { return false; }
std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
GetPauseSubresourceLoadingHandle() override {
@@ -100,6 +101,7 @@ class DummyPageScheduler : public PageScheduler {
return std::make_unique<DummyFrameScheduler>(this);
}
+ void OnTitleOrFaviconUpdated() override {}
void SetPageVisible(bool) override {}
void SetPageFrozen(bool) override {}
void SetKeepActive(bool) override {}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/features.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/features.cc
new file mode 100644
index 00000000000..caa1cd847ef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/features.cc
@@ -0,0 +1,125 @@
+// 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/common/features.h"
+
+#include "base/command_line.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/switches.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+namespace scheduler {
+
+namespace {
+
+enum class PolicyOverride { NO_OVERRIDE, FORCE_DISABLE, FORCE_ENABLE };
+
+bool g_intensive_wake_up_throttling_policy_override_cached_ = false;
+
+// Returns the IntensiveWakeUpThrottling policy settings. This is checked once
+// on first access and cached. Note that that this is *not* thread-safe!
+PolicyOverride GetIntensiveWakeUpThrottlingPolicyOverride() {
+ static PolicyOverride policy = PolicyOverride::NO_OVERRIDE;
+ if (g_intensive_wake_up_throttling_policy_override_cached_)
+ return policy;
+
+ // Otherwise, check the command-line. Only values of "0" and "1" are valid,
+ // anything else is ignored (and allows the base::Feature to control the
+ // feature). This slow path will only be hit once per renderer process.
+ g_intensive_wake_up_throttling_policy_override_cached_ = true;
+ std::string value =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kIntensiveWakeUpThrottlingPolicy);
+ if (value == switches::kIntensiveWakeUpThrottlingPolicy_ForceEnable) {
+ policy = PolicyOverride::FORCE_ENABLE;
+ } else if (value == switches::kIntensiveWakeUpThrottlingPolicy_ForceDisable) {
+ policy = PolicyOverride::FORCE_DISABLE;
+ } else {
+ // Necessary in testing configurations, as the policy can be parsed
+ // repeatedly.
+ policy = PolicyOverride::NO_OVERRIDE;
+ }
+
+ return policy;
+}
+
+} // namespace
+
+void ClearIntensiveWakeUpThrottlingPolicyOverrideCacheForTesting() {
+ g_intensive_wake_up_throttling_policy_override_cached_ = false;
+}
+
+bool IsIntensiveWakeUpThrottlingEnabled() {
+ // If policy is present then respect it.
+ auto policy = GetIntensiveWakeUpThrottlingPolicyOverride();
+ if (policy != PolicyOverride::NO_OVERRIDE)
+ return policy == PolicyOverride::FORCE_ENABLE;
+ // Otherwise respect the base::Feature.
+ return base::FeatureList::IsEnabled(features::kIntensiveWakeUpThrottling);
+}
+
+// If a policy override is specified then stick to the published defaults so
+// that admins get consistent behaviour that clients can't override. Otherwise
+// use the base::FeatureParams.
+
+base::TimeDelta GetIntensiveWakeUpThrottlingDurationBetweenWakeUps() {
+ DCHECK(IsIntensiveWakeUpThrottlingEnabled());
+
+ // Controls the period during which at most 1 wake up from throttleable
+ // TaskQueues in a page can take place.
+ static const base::FeatureParam<int>
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds{
+ &features::kIntensiveWakeUpThrottling,
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Name,
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Default};
+
+ int seconds =
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Default;
+ if (GetIntensiveWakeUpThrottlingPolicyOverride() ==
+ PolicyOverride::NO_OVERRIDE) {
+ seconds = kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds.Get();
+ }
+ return base::TimeDelta::FromSeconds(seconds);
+}
+
+base::TimeDelta GetIntensiveWakeUpThrottlingGracePeriod() {
+ DCHECK(IsIntensiveWakeUpThrottlingEnabled());
+
+ // Controls the time that elapses after a page is backgrounded before the
+ // throttling policy takes effect.
+ static const base::FeatureParam<int>
+ kIntensiveWakeUpThrottling_GracePeriodSeconds{
+ &features::kIntensiveWakeUpThrottling,
+ kIntensiveWakeUpThrottling_GracePeriodSeconds_Name,
+ kIntensiveWakeUpThrottling_GracePeriodSeconds_Default};
+
+ int seconds = kIntensiveWakeUpThrottling_GracePeriodSeconds_Default;
+ if (GetIntensiveWakeUpThrottlingPolicyOverride() ==
+ PolicyOverride::NO_OVERRIDE) {
+ seconds = kIntensiveWakeUpThrottling_GracePeriodSeconds.Get();
+ }
+ return base::TimeDelta::FromSeconds(seconds);
+}
+
+base::TimeDelta GetTimeToInhibitIntensiveThrottlingOnTitleOrFaviconUpdate() {
+ DCHECK(IsIntensiveWakeUpThrottlingEnabled());
+
+ constexpr int kDefaultSeconds = 3;
+
+ static const base::FeatureParam<int> kFeatureParam{
+ &features::kIntensiveWakeUpThrottling,
+ "inhibit_seconds_on_title_or_favicon_update_seconds", kDefaultSeconds};
+
+ int seconds = kDefaultSeconds;
+ if (GetIntensiveWakeUpThrottlingPolicyOverride() ==
+ PolicyOverride::NO_OVERRIDE) {
+ seconds = kFeatureParam.Get();
+ }
+
+ return base::TimeDelta::FromSeconds(seconds);
+}
+
+} // 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 ff3089f2169..25c8656bdaa 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/features.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/features.h
@@ -7,6 +7,7 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
+#include "base/time/time.h"
#include "third_party/blink/renderer/platform/platform_export.h"
namespace blink {
@@ -85,7 +86,7 @@ constexpr base::FeatureParam<double> kCompositorBudgetRecoveryRate{
// compositor is a BeginMainFrame task instead of any compositor task.
const base::Feature kPrioritizeCompositingUntilBeginMainFrame{
"BlinkSchedulerPrioritizeCompositingUntilBeginMainFrame",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
// LOAD PRIORITY EXPERIMENT CONTROLS
@@ -176,10 +177,127 @@ const base::Feature kPrioritizeCompositingAndLoadingDuringEarlyLoading{
"PrioritizeCompositingAndLoadingDuringEarlyLoading",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Prioritizes one BeginMainFrame after input.
+const base::Feature kPrioritizeCompositingAfterInput{
+ "PrioritizeCompositingAfterInput", base::FEATURE_DISABLED_BY_DEFAULT};
+
// Enable setting high priority database task type from field trial parameters.
const base::Feature kHighPriorityDatabaseTaskType{
"HighPriorityDatabaseTaskType", base::FEATURE_DISABLED_BY_DEFAULT};
+// When features::kIntensiveWakeUpThrottling is enabled, wake ups from
+// throttleable TaskQueues are limited to 1 per
+// GetIntensiveWakeUpThrottlingDurationBetweenWakeUp() in a page that has been
+// backgrounded for GetIntensiveWakeUpThrottlingGracePeriod().
+//
+// Intensive wake up throttling is enforced in addition to other throttling
+// mechanisms:
+// - 1 wake up per second in a background page or hidden cross-origin frame
+// - 1% CPU time in a page that has been backgrounded for 10 seconds
+//
+// Feature tracking bug: https://crbug.com/1075553
+//
+//
+// Note that features::kIntensiveWakeUpThrottling should not be read from;
+// rather the provided accessors should be used, which also take into account
+// the managed policy override of the feature.
+//
+// Parameter name and default values, exposed for testing.
+constexpr int kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Default =
+ 60;
+constexpr const char*
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Name =
+ "duration_between_wake_ups_seconds";
+
+constexpr int kIntensiveWakeUpThrottling_GracePeriodSeconds_Default = 5 * 60;
+constexpr const char* kIntensiveWakeUpThrottling_GracePeriodSeconds_Name =
+ "grace_period_seconds";
+
+// Exposed so that multiple tests can tinker with the policy override.
+PLATFORM_EXPORT void
+ClearIntensiveWakeUpThrottlingPolicyOverrideCacheForTesting();
+// Determines if the feature is enabled, taking into account base::Feature
+// settings and policy overrides.
+PLATFORM_EXPORT bool IsIntensiveWakeUpThrottlingEnabled();
+// Duration between wake ups for the kIntensiveWakeUpThrottling feature.
+PLATFORM_EXPORT base::TimeDelta
+GetIntensiveWakeUpThrottlingDurationBetweenWakeUps();
+// Grace period after backgrounding a page during which there is no intensive
+// wake up throttling for the kIntensiveWakeUpThrottling feature.
+PLATFORM_EXPORT base::TimeDelta GetIntensiveWakeUpThrottlingGracePeriod();
+// The duration for which intensive throttling should be inhibited for
+// same-origin frames when the page title or favicon is updated. 0 seconds means
+// that updating the title or favicon has no effect on intensive throttling.
+PLATFORM_EXPORT base::TimeDelta
+GetTimeToInhibitIntensiveThrottlingOnTitleOrFaviconUpdate();
+
+// Per-agent scheduling experiments.
+constexpr base::Feature kPerAgentSchedulingExperiments{
+ "BlinkSchedulerPerAgentSchedulingExperiments",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Queues the per-agent scheduling experiment should affect.
+enum class PerAgentAffectedQueues {
+ // Strategy only applies to non-main agent timer queues. These can be safely
+ // disabled/deprioritized without causing any known issues.
+ kTimerQueues,
+ // Strategy applies to all non-main agent queues. This may cause some task
+ // ordering issues.
+ kAllQueues,
+};
+
+constexpr base::FeatureParam<PerAgentAffectedQueues>::Option
+ kPerAgentQueuesOptions[] = {
+ {PerAgentAffectedQueues::kTimerQueues, "timer-queues"},
+ {PerAgentAffectedQueues::kAllQueues, "all-queues"}};
+
+constexpr base::FeatureParam<PerAgentAffectedQueues> kPerAgentQueues{
+ &kPerAgentSchedulingExperiments, "queues",
+ PerAgentAffectedQueues::kTimerQueues, &kPerAgentQueuesOptions};
+
+// Effect the per-agent scheduling strategy should have.
+enum class PerAgentSlowDownMethod {
+ // Affected queues will be disabled.
+ kDisable,
+ // Affected queues will have their priority reduced to |kBestEffortPriority|.
+ kBestEffort,
+};
+
+constexpr base::FeatureParam<PerAgentSlowDownMethod>::Option
+ kPerAgentMethodOptions[] = {
+ {PerAgentSlowDownMethod::kDisable, "disable"},
+ {PerAgentSlowDownMethod::kBestEffort, "best-effort"}};
+
+constexpr base::FeatureParam<PerAgentSlowDownMethod> kPerAgentMethod{
+ &kPerAgentSchedulingExperiments, "method", PerAgentSlowDownMethod::kDisable,
+ &kPerAgentMethodOptions};
+
+// Delay to wait after the signal is reached, before "stopping" the strategy.
+constexpr base::FeatureParam<int> kPerAgentDelayMs{
+ &kPerAgentSchedulingExperiments, "delay_ms", 0};
+
+// Signal the per-agent scheduling strategy should wait for.
+enum class PerAgentSignal {
+ // Strategy will be active until all main frames reach First Meaningful Paint
+ // (+delay, if set).
+ kFirstMeaningfulPaint,
+ // Strategy will be active until all main frames finish loading (+delay, if
+ // set).
+ kOnLoad,
+ // Strategy will be active until the delay has passed since all main frames
+ // were created (or navigated).
+ kDelayOnly,
+};
+
+constexpr base::FeatureParam<PerAgentSignal>::Option kPerAgentSignalOptions[] =
+ {{PerAgentSignal::kFirstMeaningfulPaint, "fmp"},
+ {PerAgentSignal::kOnLoad, "onload"},
+ {PerAgentSignal::kDelayOnly, "delay"}};
+
+constexpr base::FeatureParam<PerAgentSignal> kPerAgentSignal{
+ &kPerAgentSchedulingExperiments, "signal",
+ PerAgentSignal::kFirstMeaningfulPaint, &kPerAgentSignalOptions};
+
} // namespace scheduler
} // namespace blink
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 4bb8d2d2d38..aed8c83bd2e 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
@@ -25,6 +25,9 @@ bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) {
case Feature::kWebHID:
case Feature::kWebShare:
case Feature::kWebDatabase:
+ case Feature::kPortal:
+ case Feature::kSpeechRecognizer:
+ case Feature::kSpeechSynthesis:
return false;
case Feature::kMainResourceHasCacheControlNoStore:
case Feature::kMainResourceHasCacheControlNoCache:
@@ -54,6 +57,9 @@ bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) {
case Feature::kAppBanner:
case Feature::kPrinting:
case Feature::kPictureInPicture:
+ case Feature::kIdleManager:
+ case Feature::kPaymentManager:
+ case Feature::kKeyboardLock:
return true;
}
}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
index be744ad6b1b..f6fa54a39e9 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h
@@ -9,7 +9,6 @@
#include <random>
-#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/common/single_thread_idle_task_runner.h"
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc
index f0958af5d67..a02d0938fee 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.cc
@@ -61,7 +61,7 @@ void BudgetPool::EnableThrottling(base::sequence_manager::LazyNow* lazy_now) {
TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling");
- BlockThrottledQueues(lazy_now->Now());
+ UpdateThrottlingStateForAllQueues(lazy_now->Now());
}
void BudgetPool::DisableThrottling(base::sequence_manager::LazyNow* lazy_now) {
@@ -90,7 +90,7 @@ void BudgetPool::Close() {
budget_pool_controller_->UnregisterBudgetPool(this);
}
-void BudgetPool::BlockThrottledQueues(base::TimeTicks now) {
+void BudgetPool::UpdateThrottlingStateForAllQueues(base::TimeTicks now) {
for (TaskQueue* queue : associated_task_queues_)
budget_pool_controller_->UpdateQueueSchedulingLifecycleState(now, queue);
}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
index f921c9c94d5..400b07f0f01 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
@@ -65,7 +65,10 @@ class PLATFORM_EXPORT BudgetPool {
base::TimeTicks now,
base::TimeTicks desired_run_time) = 0;
- // Notifies budget pool that wakeup has happened.
+ // Invoked as part of a global wake up if any of the task queues associated
+ // with the budget pool has reached its next allowed run time. The next
+ // allowed run time of a queue is the maximum value returned from
+ // GetNextAllowedRunTime() among all the budget pools it is part of.
virtual void OnWakeUp(base::TimeTicks now) = 0;
// Specify how this budget pool should block affected queues.
@@ -103,8 +106,10 @@ class PLATFORM_EXPORT BudgetPool {
// All queues should be removed before calling Close().
void Close();
- // Block all associated queues and schedule them to run when appropriate.
- void BlockThrottledQueues(base::TimeTicks now);
+ // Ensures that a pump is scheduled and that a fence is installed for all
+ // queues in this pool, based on state of those queues and latest values from
+ // CanRunTasksAt/GetTimeTasksCanRunUntil/GetNextAllowedRunTime.
+ void UpdateThrottlingStateForAllQueues(base::TimeTicks now);
protected:
BudgetPool(const char* name, BudgetPoolController* budget_pool_controller);
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
index 1adb70195e2..551e5490a60 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_unittest.cc
@@ -132,7 +132,7 @@ TEST_F(BudgetPoolTest, WakeUpBudgetPool) {
scheduler_->NewTimerTaskQueue(
MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
- pool->SetWakeUpRate(0.1);
+ pool->SetWakeUpInterval(base::TimeTicks(), base::TimeDelta::FromSeconds(10));
pool->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(10));
// Can't run tasks until a wake-up.
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
index 121a90cdf81..c1beca5583e 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
@@ -116,7 +116,7 @@ void CPUTimeBudgetPool::RecordTaskRunTime(TaskQueue* queue,
}
if (current_budget_level_->InSecondsF() < 0)
- BlockThrottledQueues(end_time);
+ UpdateThrottlingStateForAllQueues(end_time);
}
void CPUTimeBudgetPool::OnQueueNextWakeUpChanged(
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 e43c1c603b8..45eaaaa2664 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
@@ -17,6 +17,7 @@
#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/throttled_time_domain.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
namespace scheduler {
@@ -180,7 +181,7 @@ void TaskQueueThrottler::OnQueueNextWakeUpChanged(
// TODO(altimin): This probably can be removed —- budget pools should
// schedule this.
base::TimeTicks next_allowed_run_time =
- GetNextAllowedRunTime(queue, next_wake_up);
+ UpdateNextAllowedRunTime(queue, next_wake_up);
MaybeSchedulePumpThrottledTasks(
FROM_HERE, now, std::max(next_wake_up, next_allowed_run_time));
}
@@ -191,9 +192,26 @@ void TaskQueueThrottler::PumpThrottledTasks() {
LazyNow lazy_now(tick_clock_);
- for (const auto& pair : budget_pools_)
- pair.key->OnWakeUp(lazy_now.Now());
+ // Collect BudgetPools for which at least one queue has reached its next
+ // granted run time.
+ HashSet<BudgetPool*> budget_pools_at_next_granted_run_time;
+ for (const TaskQueueMap::value_type& map_entry : queue_details_) {
+ const base::TimeTicks next_granted_run_time =
+ map_entry.value->next_granted_run_time();
+ if (next_granted_run_time <= lazy_now.Now()) {
+ budget_pools_at_next_granted_run_time.ReserveCapacityForSize(
+ map_entry.value->budget_pools().size());
+ for (BudgetPool* budget_pool : map_entry.value->budget_pools())
+ budget_pools_at_next_granted_run_time.insert(budget_pool);
+ }
+ }
+
+ // Notify BudgetPools for which at least one queue has reached its next
+ // granted run time about the wake up.
+ for (BudgetPool* budget_pool : budget_pools_at_next_granted_run_time)
+ budget_pool->OnWakeUp(lazy_now.Now());
+ // Update throttling state for all queues.
for (const TaskQueueMap::value_type& map_entry : queue_details_) {
TaskQueue* task_queue = map_entry.key;
UpdateQueueSchedulingLifecycleStateInternal(lazy_now.Now(), task_queue,
@@ -282,6 +300,13 @@ void TaskQueueThrottler::UpdateQueueSchedulingLifecycleStateInternal(
base::TimeTicks now,
TaskQueue* queue,
bool is_wake_up) {
+ // Clear the next granted run time, to ensure that the queue's BudgetPools
+ // aren't incorrectly informed of a wake up at the next PumpThrottledTasks().
+ // If necessary, an up-to-date next granted run time will be set below.
+ auto find_it = queue_details_.find(queue);
+ if (find_it != queue_details_.end())
+ find_it->value->set_next_granted_run_time(base::TimeTicks::Max());
+
if (!queue->IsQueueEnabled() || !IsThrottled(queue)) {
return;
}
@@ -319,7 +344,8 @@ void TaskQueueThrottler::UpdateQueueSchedulingLifecycleStateInternal(
// mentioned in the bug.
if (next_wake_up) {
MaybeSchedulePumpThrottledTasks(
- FROM_HERE, now, GetNextAllowedRunTime(queue, next_wake_up.value()));
+ FROM_HERE, now,
+ UpdateNextAllowedRunTime(queue, next_wake_up.value()));
}
return;
@@ -328,8 +354,8 @@ void TaskQueueThrottler::UpdateQueueSchedulingLifecycleStateInternal(
if (!next_desired_run_time)
return;
- base::TimeTicks next_run_time =
- GetNextAllowedRunTime(queue, next_desired_run_time.value());
+ const base::TimeTicks next_run_time =
+ UpdateNextAllowedRunTime(queue, next_desired_run_time.value());
// Insert a fence of an approriate type.
base::Optional<QueueBlockType> block_type = GetQueueBlockType(now, queue);
@@ -442,7 +468,7 @@ void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) {
budget_pools_.erase(budget_pool);
}
-base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
+base::TimeTicks TaskQueueThrottler::UpdateNextAllowedRunTime(
TaskQueue* queue,
base::TimeTicks desired_run_time) {
base::TimeTicks next_run_time = desired_run_time;
@@ -456,6 +482,8 @@ base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
}
+ find_it->value->set_next_granted_run_time(next_run_time);
+
return next_run_time;
}
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 49186d1da95..26ae54958ff 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
@@ -5,13 +5,13 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_
-#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/time_domain.h"
#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
@@ -151,12 +151,27 @@ class PLATFORM_EXPORT TaskQueueThrottler : public BudgetPoolController {
HashSet<BudgetPool*>& budget_pools() { return budget_pools_; }
+ base::TimeTicks next_granted_run_time() const {
+ return next_granted_run_time_;
+ }
+ void set_next_granted_run_time(base::TimeTicks next_granted_run_time) {
+ next_granted_run_time_ = next_granted_run_time;
+ }
+
private:
base::sequence_manager::TaskQueue* const queue_;
TaskQueueThrottler* const throttler_;
size_t throttling_ref_count_ = 0;
HashSet<BudgetPool*> budget_pools_;
+ // The next granted run time for |queue_|. Is TimeTicks::Max() when there is
+ // no next granted run time, for example when:
+ // - The queue didn't request a wake up.
+ // - The queue only has immediate tasks which are currently allowed to run.
+ // - A wake up just happened and the next granted run time is about to be
+ // re-evaluated.
+ base::TimeTicks next_granted_run_time_ = base::TimeTicks::Max();
+
DISALLOW_COPY_AND_ASSIGN(Metadata);
};
@@ -172,9 +187,10 @@ class PLATFORM_EXPORT TaskQueueThrottler : public BudgetPoolController {
base::TimeTicks now,
base::TimeTicks runtime);
- // Return next possible time when queue is allowed to run in accordance
- // with throttling policy.
- base::TimeTicks GetNextAllowedRunTime(
+ // Evaluate the next possible time when |queue| is allowed to run in
+ // accordance with throttling policy. Returns it and stores it in |queue|'s
+ // metadata.
+ base::TimeTicks UpdateNextAllowedRunTime(
base::sequence_manager::TaskQueue* queue,
base::TimeTicks desired_run_time);
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
index 868d0d550a4..38718b5b205 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
+#include "base/test/bind_test_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -81,15 +82,22 @@ class TaskQueueThrottlerTest : public testing::Test {
base::sequence_manager::SequenceManagerForTest::Create(
nullptr, test_task_runner_, GetTickClock()),
base::nullopt));
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
- base::TimeDelta());
task_queue_throttler_ = scheduler_->task_queue_throttler();
- timer_queue_ = scheduler_->NewTimerTaskQueue(
- MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+ wake_up_budget_pool_ =
+ task_queue_throttler_->CreateWakeUpBudgetPool("Wake Up Budget Pool");
+ wake_up_budget_pool_->SetWakeUpDuration(base::TimeDelta());
+ timer_queue_ = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
+ wake_up_budget_pool_->AddQueue(base::TimeTicks(), timer_queue_.get());
timer_task_runner_ = timer_queue_->task_runner();
}
void TearDown() override {
+ wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
+ timer_queue_.get());
+ wake_up_budget_pool_->Close();
scheduler_->Shutdown();
scheduler_.reset();
}
@@ -124,6 +132,13 @@ class TaskQueueThrottlerTest : public testing::Test {
return task_queue->BlockedByFence();
}
+ void ForwardTimeToNextMinute() {
+ test_task_runner_->FastForwardBy(
+ test_task_runner_->NowTicks().SnappedToNextTick(
+ base::TimeTicks(), base::TimeDelta::FromMinutes(1)) -
+ test_task_runner_->NowTicks());
+ }
+
protected:
virtual const base::TickClock* GetTickClock() const {
return test_task_runner_->GetMockTickClock();
@@ -131,9 +146,13 @@ class TaskQueueThrottlerTest : public testing::Test {
scoped_refptr<TestMockTimeTaskRunner> test_task_runner_;
std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
+
+ // A queue that is subject to |wake_up_budget_pool_|.
scoped_refptr<TaskQueue> timer_queue_;
+
scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_;
- TaskQueueThrottler* task_queue_throttler_; // NOT OWNED
+ TaskQueueThrottler* task_queue_throttler_ = nullptr;
+ WakeUpBudgetPool* wake_up_budget_pool_ = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottlerTest);
@@ -770,8 +789,10 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TwoQueuesTimeBudgetThrottling) {
Vector<base::TimeTicks> run_times;
- scoped_refptr<TaskQueue> second_queue = scheduler_->NewTimerTaskQueue(
- MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+ scoped_refptr<TaskQueue> second_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
@@ -779,6 +800,7 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), timer_queue_.get());
pool->AddQueue(base::TimeTicks(), second_queue.get());
+ wake_up_budget_pool_->AddQueue(base::TimeTicks(), second_queue.get());
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get());
@@ -801,6 +823,8 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
pool->RemoveQueue(test_task_runner_->NowTicks(), timer_queue_.get());
pool->RemoveQueue(test_task_runner_->NowTicks(), second_queue.get());
+ wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
+ second_queue.get());
pool->Close();
}
@@ -1099,9 +1123,10 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
DisabledQueueThenEnabledQueue) {
Vector<base::TimeTicks> run_times;
- scoped_refptr<MainThreadTaskQueue> second_queue =
- scheduler_->NewTimerTaskQueue(
- MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+ scoped_refptr<MainThreadTaskQueue> second_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
task_queue_throttler_->IncreaseThrottleRefCount(second_queue.get());
@@ -1142,8 +1167,12 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TwoBudgetPools) {
Vector<base::TimeTicks> run_times;
- scoped_refptr<TaskQueue> second_queue = scheduler_->NewTimerTaskQueue(
- MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+ scoped_refptr<TaskQueue> second_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
+
+ wake_up_budget_pool_->AddQueue(base::TimeTicks(), second_queue.get());
CPUTimeBudgetPool* pool1 =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
@@ -1181,6 +1210,9 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TwoBudgetPools) {
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(26000)));
+
+ wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
+ second_queue.get());
}
namespace {
@@ -1209,7 +1241,7 @@ void RunChainedTask(Deque<base::TimeDelta> task_durations,
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_ChainedTasks_Instantaneous) {
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
@@ -1239,7 +1271,7 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_ImmediateTasks_Fast) {
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
@@ -1272,7 +1304,7 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_DelayedTasks) {
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
@@ -1301,14 +1333,299 @@ TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3003)));
}
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_MultiplePoolsWithDifferentIntervals) {
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ WakeUpBudgetPool* one_minute_pool = wake_up_budget_pool_;
+ scoped_refptr<base::SingleThreadTaskRunner> one_minute_task_runner =
+ timer_task_runner_;
+
+ // Create another TaskQueue, throttled by another WakeUpBudgetPool.
+ WakeUpBudgetPool* two_minutes_pool =
+ task_queue_throttler_->CreateWakeUpBudgetPool(
+ "Two Minutes Interval Pool");
+ two_minutes_pool->SetWakeUpDuration(base::TimeDelta());
+ two_minutes_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(2));
+ scoped_refptr<TaskQueue> two_minutes_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
+ two_minutes_pool->AddQueue(base::TimeTicks(), two_minutes_queue.get());
+ scoped_refptr<base::SingleThreadTaskRunner> two_minutes_task_runner =
+ two_minutes_queue->task_runner();
+ task_queue_throttler_->IncreaseThrottleRefCount(two_minutes_queue.get());
+
+ // Post tasks with a short delay to both queues.
+ constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
+
+ Vector<base::TimeTicks> run_times;
+ one_minute_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ kShortDelay);
+ two_minutes_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ kShortDelay);
+
+ // Pools do not observe wake ups yet.
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
+
+ // The first task should run after 1 minute, which is the wake up interval of
+ // |one_minute_pool|. The second task should run after 2 minutes, which is the
+ // wake up interval of |two_minutes_pool|.
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(2));
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(2)));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ base::TimeTicks() + base::TimeDelta::FromMinutes(2));
+
+ // Clean up.
+ two_minutes_pool->RemoveQueue(test_task_runner_->NowTicks(),
+ two_minutes_queue.get());
+ two_minutes_pool->Close();
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_MultiplePoolsWithUnalignedWakeUps) {
+ // Snap the time to the next minute to simplify expectations.
+ ForwardTimeToNextMinute();
+ const base::TimeTicks start_time = test_task_runner_->NowTicks();
+
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ WakeUpBudgetPool* one_minute_pool = wake_up_budget_pool_;
+ scoped_refptr<base::SingleThreadTaskRunner> one_minute_task_runner =
+ timer_task_runner_;
+
+ // Create another TaskQueue, throttled by another WakeUpBudgetPool.
+ WakeUpBudgetPool* two_minutes_pool =
+ task_queue_throttler_->CreateWakeUpBudgetPool(
+ "Two Minutes Interval Pool");
+ two_minutes_pool->SetWakeUpDuration(base::TimeDelta());
+ two_minutes_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ two_minutes_pool->AllowUnalignedWakeUpIfNoRecentWakeUp();
+ scoped_refptr<TaskQueue> two_minutes_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
+ two_minutes_pool->AddQueue(base::TimeTicks(), two_minutes_queue.get());
+ scoped_refptr<base::SingleThreadTaskRunner> two_minutes_task_runner =
+ two_minutes_queue->task_runner();
+ task_queue_throttler_->IncreaseThrottleRefCount(two_minutes_queue.get());
+
+ // Post tasks with short delays to both queues. They should run unaligned. The
+ // wake up in |one_minute_pool| should not be taken into account when
+ // evaluating whether there was a recent wake up in
+ // |two_minutes_pool_|.
+ Vector<base::TimeTicks> run_times;
+ one_minute_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(2));
+ two_minutes_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(3));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(2)));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(2),
+ start_time + base::TimeDelta::FromSeconds(3)));
+
+ // Post extra tasks with long unaligned wake ups. They should run unaligned,
+ // since their desired run time is more than 1 minute after the last wake up
+ // in their respective pools.
+ run_times.clear();
+ one_minute_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(602));
+ two_minutes_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(603));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(601));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times, ElementsAre());
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(605));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(605)));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(605));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(606));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(605),
+ start_time + base::TimeDelta::FromSeconds(606)));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(605));
+ EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(606));
+
+ // Clean up.
+ two_minutes_pool->RemoveQueue(test_task_runner_->NowTicks(),
+ two_minutes_queue.get());
+ two_minutes_pool->Close();
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_MultiplePoolsWithAllignedAndUnalignedWakeUps) {
+ // Snap the time to the next minute to simplify expectations.
+ ForwardTimeToNextMinute();
+ const base::TimeTicks start_time = test_task_runner_->NowTicks();
+
+ // The 1st WakeUpBudgetPool doesn't allow unaligned wake ups.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ WakeUpBudgetPool* aligned_pool = wake_up_budget_pool_;
+ scoped_refptr<base::SingleThreadTaskRunner> aligned_task_runner =
+ timer_task_runner_;
+
+ // Create another TaskQueue, throttled by another WakeUpBudgetPool. This 2nd
+ // WakeUpBudgetPool allows unaligned wake ups.
+ WakeUpBudgetPool* unaligned_pool =
+ task_queue_throttler_->CreateWakeUpBudgetPool(
+ "Other Wake Up Budget Pool");
+ unaligned_pool->SetWakeUpDuration(base::TimeDelta());
+ unaligned_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ unaligned_pool->AllowUnalignedWakeUpIfNoRecentWakeUp();
+ scoped_refptr<TaskQueue> unaligned_queue = scheduler_->NewTaskQueue(
+ MainThreadTaskQueue::QueueCreationParams(
+ MainThreadTaskQueue::QueueType::kFrameThrottleable)
+ .SetCanBeThrottled(true));
+ unaligned_pool->AddQueue(base::TimeTicks(), unaligned_queue.get());
+ scoped_refptr<base::SingleThreadTaskRunner> unaligned_task_runner =
+ unaligned_queue->task_runner();
+ task_queue_throttler_->IncreaseThrottleRefCount(unaligned_queue.get());
+
+ // Post tasks with short delays to both queues. The 1st task should run
+ // aligned, while the 2nd task should run unaligned.
+ Vector<base::TimeTicks> run_times;
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(2));
+ unaligned_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(3));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_THAT(run_times, ElementsAre());
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(), base::nullopt);
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(3)));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(57));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(3),
+ start_time + base::TimeDelta::FromMinutes(1)));
+
+ // Post extra tasks with long unaligned wake ups. The 1st task should run
+ // aligned, while the 2nd task should run unaligned.
+ run_times.clear();
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(602));
+ unaligned_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(603));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(601));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(3));
+ EXPECT_THAT(run_times, ElementsAre());
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromMinutes(1));
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(663));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(663)));
+
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(117));
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromMinutes(12));
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(663));
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(663),
+ start_time + base::TimeDelta::FromMinutes(12)));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromMinutes(12));
+ EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
+ start_time + base::TimeDelta::FromSeconds(663));
+
+ // Clean up.
+ unaligned_pool->RemoveQueue(test_task_runner_->NowTicks(),
+ unaligned_queue.get());
+ unaligned_pool->Close();
+}
+
TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
constexpr base::TimeDelta kDelay = base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kTimeBetweenWakeUps =
base::TimeDelta::FromMinutes(1);
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpRate(
- 1.0 / kTimeBetweenWakeUps.InSeconds());
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
- base::TimeDelta::FromMilliseconds(1));
+ wake_up_budget_pool_->SetWakeUpInterval(base::TimeTicks(),
+ kTimeBetweenWakeUps);
+ wake_up_budget_pool_->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(1));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
@@ -1328,7 +1645,7 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
// Disable throttling. All tasks can run.
LazyNow lazy_now_1(test_task_runner_->GetMockTickClock());
- scheduler_->GetWakeUpBudgetPoolForTesting()->DisableThrottling(&lazy_now_1);
+ wake_up_budget_pool_->DisableThrottling(&lazy_now_1);
test_task_runner_->FastForwardBy(5 * kDelay);
EXPECT_THAT(
@@ -1342,7 +1659,7 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
// Throttling is enabled. Only 1 task runs per |kTimeBetweenWakeUps|.
LazyNow lazy_now_2(test_task_runner_->GetMockTickClock());
- scheduler_->GetWakeUpBudgetPoolForTesting()->EnableThrottling(&lazy_now_2);
+ wake_up_budget_pool_->EnableThrottling(&lazy_now_2);
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
@@ -1353,8 +1670,207 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
base::TimeTicks() + base::TimeDelta::FromSeconds(300)));
}
+TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_UnalignedWakeUps) {
+ // All throttled wake ups are aligned on 1-second intervals by
+ // TaskQueueThrottler, irrespective of BudgetPools. Start the test at a time
+ // aligned on a 1-minute interval, to simplify expectations.
+ ForwardTimeToNextMinute();
+ const base::TimeTicks start_time = test_task_runner_->NowTicks();
+
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
+
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(90));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+
+ EXPECT_THAT(run_times,
+ ElementsAre(start_time + base::TimeDelta::FromSeconds(90)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_UnalignedWakeUps_MultipleTasks) {
+ // Start at a 1-minute aligned time to simplify expectations.
+ ForwardTimeToNextMinute();
+ const base::TimeTicks initial_time = test_task_runner_->NowTicks();
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+ // Task delay: Expected run time: Reason:
+ // 30 seconds 30 seconds >= 60 seconds after last wake up
+ // 80 seconds 90 seconds >= 60 seconds after last wake up
+ // 95 seconds 120 seconds Aligned
+ // 100 seconds 120 seconds Aligned
+ // 130 seconds 180 seconds Aligned
+ // 251 seconds 251 seconds >= 60 seconds after last wake up
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(30));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(80));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(95));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(100));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(130));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(251));
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ ElementsAre(initial_time + base::TimeDelta::FromSeconds(30),
+ initial_time + base::TimeDelta::FromSeconds(90),
+ initial_time + base::TimeDelta::FromSeconds(120),
+ initial_time + base::TimeDelta::FromSeconds(120),
+ initial_time + base::TimeDelta::FromSeconds(180),
+ initial_time + base::TimeDelta::FromSeconds(251)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_IncreaseWakeUpIntervalBeforeWakeUp) {
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post 2 delayed tasks when the wake up interval is 1 minute. The delay of
+ // the 2nd task is such that it won't be ready when the 1st task completes.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromMilliseconds(1));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromMinutes(2));
+
+ // Update the wake up interval to 1 hour.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromHours(1));
+
+ // Tasks run after 1 hour, which is the most up to date wake up interval.
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromHours(1),
+ base::TimeTicks() + base::TimeDelta::FromHours(1)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_DecreaseWakeUpIntervalBeforeWakeUp) {
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post a delayed task when the wake up interval is 1 hour.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromHours(1));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromMilliseconds(1));
+
+ // Update the wake up interval to 1 minute.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+
+ // The delayed task should run after 1 minute, which is the most up to date
+ // wake up interval.
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_IncreaseWakeUpIntervalDuringWakeUp) {
+ wake_up_budget_pool_->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post a 1st delayed task when the wake up interval is 1 minute.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromMinutes(1));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ TestTask(&run_times, test_task_runner_);
+ // Post a 2nd delayed task when the wake up interval is still 1 minute.
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ TestTask(&run_times, test_task_runner_);
+ // Post a 3rd task when the wake up interval is 1 hour.
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(1));
+ }),
+ base::TimeDelta::FromSeconds(1));
+ // Increase the wake up interval. This should affect the 2nd and 3rd
+ // tasks, which haven't run yet.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromHours(1));
+ }),
+ base::TimeDelta::FromSeconds(1));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1),
+ base::TimeTicks() + base::TimeDelta::FromHours(1),
+ base::TimeTicks() + base::TimeDelta::FromHours(2)));
+}
+
+TEST_F(TaskQueueThrottlerTest,
+ WakeUpBasedThrottling_DecreaseWakeUpIntervalDuringWakeUp) {
+ wake_up_budget_pool_->SetWakeUpDuration(
+ base::TimeDelta::FromMilliseconds(10));
+
+ Vector<base::TimeTicks> run_times;
+ task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
+
+ // Post a 1st delayed task when the wake up interval is 1 hour.
+ wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
+ base::TimeDelta::FromHours(1));
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ TestTask(&run_times, test_task_runner_);
+ // Post a 2nd delayed task when the wake up interval is still 1 hour.
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ TestTask(&run_times, test_task_runner_);
+ // Post a 3rd task when the wake up interval is 1 minute.
+ timer_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&TestTask, &run_times, test_task_runner_),
+ base::TimeDelta::FromSeconds(1));
+ }),
+ base::TimeDelta::FromSeconds(1));
+ // Decrease the wake up interval. This immediately reschedules the wake
+ // up for the 2nd task.
+ wake_up_budget_pool_->SetWakeUpInterval(
+ test_task_runner_->NowTicks(), base::TimeDelta::FromMinutes(1));
+ }),
+ base::TimeDelta::FromSeconds(1));
+
+ test_task_runner_->FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ ElementsAre(base::TimeTicks() + base::TimeDelta::FromHours(1),
+ base::TimeTicks() + base::TimeDelta::FromHours(1) +
+ base::TimeDelta::FromMinutes(1),
+ base::TimeTicks() + base::TimeDelta::FromHours(1) +
+ base::TimeDelta::FromMinutes(2)));
+}
+
TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottlingWithCPUBudgetThrottling) {
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
CPUTimeBudgetPool* pool =
@@ -1396,7 +1912,7 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottlingWithCPUBudgetThrottling) {
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottlingWithCPUBudgetThrottling_OnAndOff) {
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
CPUTimeBudgetPool* pool =
@@ -1453,7 +1969,7 @@ TEST_F(TaskQueueThrottlerTest,
// This test checks that a new task should run during the wake-up window
// when time budget allows that and should be blocked when time budget is
// exhausted.
- scheduler_->GetWakeUpBudgetPoolForTesting()->SetWakeUpDuration(
+ wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
CPUTimeBudgetPool* pool =
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc
index 5505763b03c..a5603642a2c 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
+#include <algorithm>
#include <cstdint>
#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
@@ -18,7 +19,7 @@ WakeUpBudgetPool::WakeUpBudgetPool(const char* name,
BudgetPoolController* budget_pool_controller,
base::TimeTicks now)
: BudgetPool(name, budget_pool_controller),
- wake_up_interval_(base::TimeDelta::FromSecondsD(1.0)) {}
+ wake_up_interval_(base::TimeDelta::FromSeconds(1)) {}
WakeUpBudgetPool::~WakeUpBudgetPool() = default;
@@ -26,14 +27,20 @@ QueueBlockType WakeUpBudgetPool::GetBlockType() const {
return QueueBlockType::kNewTasksOnly;
}
-void WakeUpBudgetPool::SetWakeUpRate(double wake_ups_per_second) {
- wake_up_interval_ = base::TimeDelta::FromSecondsD(1 / wake_ups_per_second);
+void WakeUpBudgetPool::SetWakeUpInterval(base::TimeTicks now,
+ base::TimeDelta interval) {
+ wake_up_interval_ = interval;
+ UpdateThrottlingStateForAllQueues(now);
}
void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) {
wake_up_duration_ = duration;
}
+void WakeUpBudgetPool::AllowUnalignedWakeUpIfNoRecentWakeUp() {
+ allow_unaligned_wake_up_is_no_recent_wake_up_ = true;
+}
+
void WakeUpBudgetPool::RecordTaskRunTime(TaskQueue* queue,
base::TimeTicks start_time,
base::TimeTicks end_time) {
@@ -47,9 +54,9 @@ bool WakeUpBudgetPool::CanRunTasksAt(base::TimeTicks moment,
if (!last_wake_up_)
return false;
// |is_wake_up| flag means that we're in the beginning of the wake-up and
- // |OnWakeUp| has just been called. This is needed to support backwards
- // compability with old throttling mechanism (when |wake_up_duration| is zero)
- // and allow only one task to run.
+ // |OnWakeUp| has just been called. This is needed to support
+ // backwards compatibility with old throttling mechanism (when
+ // |wake_up_duration| is zero) and allow only one task to run.
if (last_wake_up_ == moment && is_wake_up)
return true;
return moment < last_wake_up_.value() + wake_up_duration_;
@@ -71,13 +78,35 @@ base::TimeTicks WakeUpBudgetPool::GetNextAllowedRunTime(
base::TimeTicks desired_run_time) const {
if (!is_enabled_)
return desired_run_time;
- if (!last_wake_up_) {
- return desired_run_time.SnappedToNextTick(base::TimeTicks(),
- wake_up_interval_);
- }
- if (desired_run_time < last_wake_up_.value() + wake_up_duration_)
+
+ // Do not throttle if the desired run time is still within the duration of the
+ // last wake up.
+ if (last_wake_up_.has_value() &&
+ desired_run_time < last_wake_up_.value() + wake_up_duration_) {
return desired_run_time;
- DCHECK_GE(desired_run_time, last_wake_up_.value());
+ }
+
+ // Do not throttle if there hasn't been a wake up in the last wake up
+ // interval.
+ if (allow_unaligned_wake_up_is_no_recent_wake_up_) {
+ // If unaligned wake ups are allowed, the first wake up can happen at any
+ // point.
+ if (!last_wake_up_.has_value())
+ return desired_run_time;
+
+ // Unaligned wake ups can happen at most every |wake_up_interval_| after the
+ // last wake up.
+ auto next_unaligned_wake_up =
+ std::max(desired_run_time, last_wake_up_.value() + wake_up_interval_);
+
+ // Aligned wake ups happen every |wake_up_interval_|, snapped to the minute.
+ auto next_aligned_wake_up = desired_run_time.SnappedToNextTick(
+ base::TimeTicks(), wake_up_interval_);
+
+ // Pick the earliest of the two allowed run times.
+ return std::min(next_unaligned_wake_up, next_aligned_wake_up);
+ }
+
return desired_run_time.SnappedToNextTick(base::TimeTicks(),
wake_up_interval_);
}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h
index 689f98b9eab..276ffa349b8 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h
@@ -13,8 +13,8 @@
namespace blink {
namespace scheduler {
-// WakeUpBudgetPool represents a collection of task queues which share a limit
-// on total cpu time.
+// WakeUpBudgetPool represents a collection of task queues which run for a
+// limited time at regular intervals.
class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
public:
WakeUpBudgetPool(const char* name,
@@ -22,14 +22,22 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
base::TimeTicks now);
~WakeUpBudgetPool() override;
- // Note: this does not have an immediate effect and should be called only
- // during initialization of a WakeUpBudgetPool.
- void SetWakeUpRate(double wake_ups_per_second);
+ // Sets the interval between wake ups. This can be invoked at any time. If a
+ // next wake up is already scheduled, it is rescheduled according to the new
+ // |interval| as part of this call.
+ void SetWakeUpInterval(base::TimeTicks now, base::TimeDelta interval);
- // Note: this does not have an immediate effect and should be called only
- // during initialization of a WakeUpBudgetPool.
+ // Sets the duration of wake ups. This does not have an immediate effect and
+ // should be called only during initialization of a WakeUpBudgetPool.
void SetWakeUpDuration(base::TimeDelta duration);
+ // If called, the budget pool allows an unaligned wake up when there hasn't
+ // been a wake up in the last |wake_up_interval_|.
+ //
+ // This does not have an immediate effect and should be called only during
+ // initialization of a WakeUpBudgetPool.
+ void AllowUnalignedWakeUpIfNoRecentWakeUp();
+
// BudgetPool implementation:
void RecordTaskRunTime(base::sequence_manager::TaskQueue* queue,
base::TimeTicks start_time,
@@ -46,6 +54,10 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
void AsValueInto(base::trace_event::TracedValue* state,
base::TimeTicks now) const final;
+ base::Optional<base::TimeTicks> last_wake_up_for_testing() const {
+ return last_wake_up_;
+ }
+
protected:
QueueBlockType GetBlockType() const final;
@@ -53,6 +65,8 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
base::TimeDelta wake_up_interval_;
base::TimeDelta wake_up_duration_;
+ bool allow_unaligned_wake_up_is_no_recent_wake_up_ = false;
+
base::Optional<base::TimeTicks> last_wake_up_;
DISALLOW_COPY_AND_ASSIGN(WakeUpBudgetPool);
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h
index acdef00b9dc..289c2fff2dd 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h
@@ -9,8 +9,8 @@
#include <map>
#include <vector>
+#include "base/check_op.h"
#include "base/containers/flat_map.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc
new file mode 100644
index 00000000000..9db40be1c03
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.cc
@@ -0,0 +1,331 @@
+// 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/agent_scheduling_strategy.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/check.h"
+#include "base/feature_list.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/synchronization/lock.h"
+#include "third_party/blink/renderer/platform/scheduler/common/features.h"
+#include "third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+namespace {
+
+using ::base::sequence_manager::TaskQueue;
+
+using PrioritisationType =
+ ::blink::scheduler::MainThreadTaskQueue::QueueTraits::PrioritisationType;
+
+// Scheduling strategy that does nothing. This emulates the "current" shipped
+// behavior, and is the default unless overridden. Corresponds to the
+// |kNoOpStrategy| feature.
+class NoOpStrategy final : public AgentSchedulingStrategy {
+ public:
+ NoOpStrategy() = default;
+
+ ShouldUpdatePolicy OnFrameAdded(const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnFrameRemoved(const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnMainFrameFirstMeaningfulPaint(
+ const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnInputEvent() override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnDocumentChangedInMainFrame(
+ const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnMainFrameLoad(const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+ ShouldUpdatePolicy OnDelayPassed(const FrameSchedulerImpl&) override {
+ VerifyValidSequence();
+ return ShouldUpdatePolicy::kNo;
+ }
+
+ base::Optional<bool> QueueEnabledState(
+ const MainThreadTaskQueue& task_queue) const override {
+ VerifyValidSequence();
+ return base::nullopt;
+ }
+ base::Optional<TaskQueue::QueuePriority> QueuePriority(
+ const MainThreadTaskQueue& task_queue) const override {
+ VerifyValidSequence();
+ return base::nullopt;
+ }
+
+ bool ShouldNotifyOnInputEvent() const override { return false; }
+};
+
+// Strategy that keeps track of main frames reaching a certain signal to make
+// scheduling decisions. The exact behavior will be determined by parameter
+// values.
+class TrackMainFrameSignal final : public AgentSchedulingStrategy {
+ public:
+ TrackMainFrameSignal(Delegate& delegate,
+ PerAgentAffectedQueues affected_queue_types,
+ PerAgentSlowDownMethod method,
+ PerAgentSignal signal,
+ base::TimeDelta delay)
+ : delegate_(delegate),
+ affected_queue_types_(affected_queue_types),
+ method_(method),
+ signal_(signal),
+ delay_(delay),
+ waiting_for_input_(&waiting_for_input_lock_) {
+ DCHECK(signal != PerAgentSignal::kDelayOnly || !delay.is_zero())
+ << "Delay duration can not be zero when using |kDelayOnly|.";
+ }
+
+ ShouldUpdatePolicy OnFrameAdded(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ return OnNewDocument(frame_scheduler);
+ }
+
+ ShouldUpdatePolicy OnFrameRemoved(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ if (frame_scheduler.GetFrameType() !=
+ FrameScheduler::FrameType::kMainFrame) {
+ return ShouldUpdatePolicy::kNo;
+ }
+
+ main_frames_.erase(&frame_scheduler);
+ main_frames_waiting_for_signal_.erase(&frame_scheduler);
+ if (main_frames_waiting_for_signal_.IsEmpty())
+ SetWaitingForInput(false);
+
+ // TODO(talp): If the frame wasn't in the set to begin with (e.g.: because
+ // it already hit FMP), or if there are still other frames in the set,
+ // then we may not have to trigger a policy update. (But what about cases
+ // where the current agent just changed from main to non-main?)
+ return ShouldUpdatePolicy::kYes;
+ }
+
+ ShouldUpdatePolicy OnMainFrameFirstMeaningfulPaint(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ DCHECK(frame_scheduler.GetFrameType() ==
+ FrameScheduler::FrameType::kMainFrame);
+
+ return OnSignal(frame_scheduler, PerAgentSignal::kFirstMeaningfulPaint);
+ }
+
+ ShouldUpdatePolicy OnInputEvent() override {
+ VerifyValidSequence();
+
+ // We only use input as a fail-safe for FMP, other signals are more
+ // reliable.
+ DCHECK_EQ(signal_, PerAgentSignal::kFirstMeaningfulPaint)
+ << "OnInputEvent should only be called for FMP-based strategies.";
+
+ if (main_frames_waiting_for_signal_.IsEmpty())
+ return ShouldUpdatePolicy::kNo;
+
+ // Ideally we would like to only remove the frame the input event is related
+ // to, but we don't currently have that information. One suggestion (by
+ // altimin@) is to attribute it to a widget, and apply it to all frames on
+ // the page the widget is on.
+ main_frames_waiting_for_signal_.clear();
+ SetWaitingForInput(false);
+ return ShouldUpdatePolicy::kYes;
+ }
+
+ ShouldUpdatePolicy OnDocumentChangedInMainFrame(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ return OnNewDocument(frame_scheduler);
+ }
+
+ ShouldUpdatePolicy OnMainFrameLoad(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ DCHECK(frame_scheduler.GetFrameType() ==
+ FrameScheduler::FrameType::kMainFrame);
+
+ return OnSignal(frame_scheduler, PerAgentSignal::kOnLoad);
+ }
+
+ ShouldUpdatePolicy OnDelayPassed(
+ const FrameSchedulerImpl& frame_scheduler) override {
+ VerifyValidSequence();
+ return SignalReached(frame_scheduler);
+ }
+
+ base::Optional<bool> QueueEnabledState(
+ const MainThreadTaskQueue& task_queue) const override {
+ VerifyValidSequence();
+
+ if (method_ == PerAgentSlowDownMethod::kDisable &&
+ ShouldAffectQueue(task_queue)) {
+ return false;
+ }
+
+ return base::nullopt;
+ }
+
+ base::Optional<TaskQueue::QueuePriority> QueuePriority(
+ const MainThreadTaskQueue& task_queue) const override {
+ VerifyValidSequence();
+
+ if (method_ == PerAgentSlowDownMethod::kBestEffort &&
+ ShouldAffectQueue(task_queue)) {
+ return TaskQueue::QueuePriority::kBestEffortPriority;
+ }
+
+ return base::nullopt;
+ }
+
+ bool ShouldNotifyOnInputEvent() const override {
+ if (signal_ != PerAgentSignal::kFirstMeaningfulPaint)
+ return false;
+
+ return waiting_for_input_.IsSet();
+ }
+
+ private:
+ ShouldUpdatePolicy OnNewDocument(const FrameSchedulerImpl& frame_scheduler) {
+ // For now we *always* return kYes here. It might be possible to optimize
+ // this, but there are a number of tricky cases that need to be taken into
+ // account here: (i) a non-main frame could have navigated between a main
+ // and a non-main agent, possibly requiring policy update for that frame, or
+ // (ii) main frame navigated to a different agent, potentially changing the
+ // main/non-main classification for both the "previous" and "current" agents
+ // and requiring their policies be updated.
+
+ if (frame_scheduler.GetFrameType() !=
+ FrameScheduler::FrameType::kMainFrame) {
+ return ShouldUpdatePolicy::kYes;
+ }
+
+ if (signal_ == PerAgentSignal::kDelayOnly) {
+ delegate_.OnSetTimer(frame_scheduler, delay_);
+ }
+
+ main_frames_.insert(&frame_scheduler);
+
+ // Only add ordinary page frames to the set of waiting frames, as
+ // non-ordinary ones don't report any signals.
+ if (frame_scheduler.IsOrdinary())
+ main_frames_waiting_for_signal_.insert(&frame_scheduler);
+
+ if (signal_ == PerAgentSignal::kFirstMeaningfulPaint)
+ SetWaitingForInput(true);
+
+ return ShouldUpdatePolicy::kYes;
+ }
+
+ bool ShouldAffectQueue(const MainThreadTaskQueue& task_queue) const {
+ // Queues that don't have a frame scheduler are, by definition, not
+ // associated with a frame (or agent).
+ if (!task_queue.GetFrameScheduler())
+ return false;
+
+ if (affected_queue_types_ == PerAgentAffectedQueues::kTimerQueues &&
+ task_queue.GetPrioritisationType() !=
+ PrioritisationType::kJavaScriptTimer) {
+ return false;
+ }
+
+ // Don't do anything if all main frames have reached the signal.
+ if (main_frames_waiting_for_signal_.IsEmpty())
+ return false;
+
+ // Otherwise, affect the queue only if it doesn't belong to any main agent.
+ base::UnguessableToken agent_cluster_id =
+ task_queue.GetFrameScheduler()->GetAgentClusterId();
+ return std::all_of(main_frames_.begin(), main_frames_.end(),
+ [agent_cluster_id](const FrameSchedulerImpl* frame) {
+ return frame->GetAgentClusterId() != agent_cluster_id;
+ });
+ }
+
+ ShouldUpdatePolicy OnSignal(const FrameSchedulerImpl& frame_scheduler,
+ PerAgentSignal signal) {
+ if (signal != signal_)
+ return ShouldUpdatePolicy::kNo;
+
+ // If there is no delay, then we have reached the awaited signal.
+ if (delay_.is_zero()) {
+ return SignalReached(frame_scheduler);
+ }
+
+ // No need to update policy if we have to wait for a delay.
+ delegate_.OnSetTimer(frame_scheduler, delay_);
+ return ShouldUpdatePolicy::kNo;
+ }
+
+ ShouldUpdatePolicy SignalReached(const FrameSchedulerImpl& frame_scheduler) {
+ main_frames_waiting_for_signal_.erase(&frame_scheduler);
+ if (main_frames_waiting_for_signal_.IsEmpty())
+ SetWaitingForInput(false);
+
+ // TODO(talp): If the frame wasn't in the set to begin with (e.g.: because
+ // an input event cleared it), or if there are still other frames in the
+ // set, then we may not have to trigger a policy update.
+ return ShouldUpdatePolicy::kYes;
+ }
+
+ Delegate& delegate_;
+ const PerAgentAffectedQueues affected_queue_types_;
+ const PerAgentSlowDownMethod method_;
+ const PerAgentSignal signal_;
+ const base::TimeDelta delay_;
+
+ WTF::HashSet<const FrameSchedulerImpl*> main_frames_;
+ WTF::HashSet<const FrameSchedulerImpl*> main_frames_waiting_for_signal_;
+
+ base::Lock waiting_for_input_lock_;
+ PollableThreadSafeFlag waiting_for_input_;
+ void SetWaitingForInput(bool waiting_for_input) {
+ if (waiting_for_input_.IsSet() != waiting_for_input) {
+ base::AutoLock lock(waiting_for_input_lock_);
+ waiting_for_input_.SetWhileLocked(waiting_for_input);
+ }
+ }
+};
+} // namespace
+
+AgentSchedulingStrategy::~AgentSchedulingStrategy() {
+ VerifyValidSequence();
+}
+
+std::unique_ptr<AgentSchedulingStrategy> AgentSchedulingStrategy::Create(
+ Delegate& delegate) {
+ if (!base::FeatureList::IsEnabled(kPerAgentSchedulingExperiments))
+ return std::make_unique<NoOpStrategy>();
+
+ return std::make_unique<TrackMainFrameSignal>(
+ delegate, kPerAgentQueues.Get(), kPerAgentMethod.Get(),
+ kPerAgentSignal.Get(),
+ base::TimeDelta::FromMilliseconds(kPerAgentDelayMs.Get()));
+}
+
+void AgentSchedulingStrategy::VerifyValidSequence() const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(main_thread_sequence_checker_);
+}
+
+} // namespace scheduler
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h
new file mode 100644
index 00000000000..5319c02f22a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h
@@ -0,0 +1,97 @@
+// 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_AGENT_SCHEDULING_STRATEGY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_AGENT_SCHEDULING_STRATEGY_H_
+
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "base/task/sequence_manager/task_queue.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.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_task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
+
+namespace blink {
+namespace scheduler {
+
+// Abstract class that can be consulted to determine task queue priorities and
+// scheduling policies, that are based on the queue's Agent.
+// Strategies should only be accessed from the main thread.
+class PLATFORM_EXPORT AgentSchedulingStrategy {
+ public:
+ enum class ShouldUpdatePolicy {
+ kNo,
+ kYes,
+ };
+
+ class Delegate {
+ public:
+ Delegate() = default;
+ virtual ~Delegate() = default;
+
+ // Delegate should call OnDelayPassed after |delay| has passed, and pass
+ // |frame_scheduler| as a parameter.
+ virtual void OnSetTimer(const FrameSchedulerImpl& frame_scheduler,
+ base::TimeDelta delay) = 0;
+ };
+
+ AgentSchedulingStrategy(const AgentSchedulingStrategy&) = delete;
+ AgentSchedulingStrategy(AgentSchedulingStrategy&&) = delete;
+
+ virtual ~AgentSchedulingStrategy();
+
+ static std::unique_ptr<AgentSchedulingStrategy> Create(Delegate& delegate);
+
+ // The following functions need to be called as appropriate to manage the
+ // strategy's internal state. Will return |kYes| when a policy update should
+ // be triggered.
+ virtual ShouldUpdatePolicy OnFrameAdded(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+ virtual ShouldUpdatePolicy OnFrameRemoved(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+ virtual ShouldUpdatePolicy OnMainFrameFirstMeaningfulPaint(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+ // FMP is not reported consistently, so input events are used as a failsafe
+ // to make sure frames aren't considered waiting for FMP indefinitely. Should
+ // not be called for mouse move events.
+ virtual ShouldUpdatePolicy OnInputEvent() WARN_UNUSED_RESULT = 0;
+ virtual ShouldUpdatePolicy OnDocumentChangedInMainFrame(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+ virtual ShouldUpdatePolicy OnMainFrameLoad(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+ // OnDelayPassed should be called by Delegate after the appropriate delay.
+ virtual ShouldUpdatePolicy OnDelayPassed(
+ const FrameSchedulerImpl& frame_scheduler) WARN_UNUSED_RESULT = 0;
+
+ // The following functions should be consulted when making scheduling
+ // decisions. Will return |base::Optional| containing the desired value, or
+ // |nullopt| to signify that the original scheduler's decision should not be
+ // changed.
+ virtual base::Optional<bool> QueueEnabledState(
+ const MainThreadTaskQueue& task_queue) const = 0;
+ virtual base::Optional<base::sequence_manager::TaskQueue::QueuePriority>
+ QueuePriority(const MainThreadTaskQueue& task_queue) const = 0;
+
+ // Returns true if the strategy is interested in getting input event
+ // notifications. This is *the only* method that may be called from different
+ // threads.
+ virtual bool ShouldNotifyOnInputEvent() const = 0;
+
+ protected:
+ AgentSchedulingStrategy() = default;
+
+ // Check that the strategy is used from the right (= main) thread. Should be
+ // called from all public methods except ShouldNotifyOnInput().
+ void VerifyValidSequence() const;
+
+ private:
+ SEQUENCE_CHECKER(main_thread_sequence_checker_);
+};
+
+} // namespace scheduler
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_AGENT_SCHEDULING_STRATEGY_H_
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc
new file mode 100644
index 00000000000..ed6d6d38ead
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy_unittest.cc
@@ -0,0 +1,527 @@
+#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.h"
+
+#include <memory>
+#include "base/memory/scoped_refptr.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/optional.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/unguessable_token.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/scheduler/main_thread/frame_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
+
+namespace blink {
+namespace scheduler {
+
+using FeatureAndParams = ::base::test::ScopedFeatureList::FeatureAndParams;
+using ShouldUpdatePolicy =
+ ::blink::scheduler::AgentSchedulingStrategy::ShouldUpdatePolicy;
+using PrioritisationType =
+ ::blink::scheduler::MainThreadTaskQueue::QueueTraits::PrioritisationType;
+
+using ::base::FieldTrialParams;
+using ::base::sequence_manager::TaskQueue;
+using ::base::test::ScopedFeatureList;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::Test;
+
+namespace {
+
+class MockDelegate : public AgentSchedulingStrategy::Delegate {
+ public:
+ MOCK_METHOD(void,
+ OnSetTimer,
+ (const FrameSchedulerImpl& frame_scheduler,
+ base::TimeDelta delay));
+};
+
+class MockFrameSchedulerDelegate : public FrameScheduler::Delegate {
+ public:
+ MockFrameSchedulerDelegate() {
+ ON_CALL(*this, GetAgentClusterId)
+ .WillByDefault(ReturnRef(agent_cluster_id_));
+ }
+
+ MOCK_METHOD(const base::UnguessableToken&,
+ GetAgentClusterId,
+ (),
+ (const, override));
+ MOCK_METHOD(ukm::UkmRecorder*, GetUkmRecorder, ());
+ MOCK_METHOD(ukm::SourceId, GetUkmSourceId, ());
+ MOCK_METHOD(void, UpdateTaskTime, (base::TimeDelta));
+ MOCK_METHOD(void, UpdateActiveSchedulerTrackedFeatures, (uint64_t));
+
+ private:
+ base::UnguessableToken agent_cluster_id_ = base::UnguessableToken::Create();
+};
+
+class MockFrameScheduler : public FrameSchedulerImpl {
+ public:
+ explicit MockFrameScheduler(FrameScheduler::FrameType frame_type)
+ : FrameSchedulerImpl(/*main_thread_scheduler=*/nullptr,
+ /*parent_page_scheduler=*/nullptr,
+ /*delegate=*/&delegate_,
+ /*blame_context=*/nullptr,
+ /*frame_type=*/frame_type) {
+ ON_CALL(*this, IsOrdinary).WillByDefault(Return(true));
+ }
+
+ MOCK_METHOD(bool, IsOrdinary, (), (const));
+
+ private:
+ NiceMock<MockFrameSchedulerDelegate> delegate_;
+};
+
+} // namespace
+
+class PerAgentSchedulingBaseTest : public Test {
+ public:
+ explicit PerAgentSchedulingBaseTest(
+ const FieldTrialParams experiment_params) {
+ feature_list_.InitWithFeaturesAndParameters(
+ {{kPerAgentSchedulingExperiments, experiment_params}}, {});
+ strategy_ = AgentSchedulingStrategy::Create(delegate_);
+ timer_queue_->SetFrameSchedulerForTest(&subframe_);
+ non_timer_queue_->SetFrameSchedulerForTest(&subframe_);
+ }
+
+ protected:
+ ScopedFeatureList feature_list_;
+ NiceMock<MockDelegate> delegate_{};
+ std::unique_ptr<AgentSchedulingStrategy> strategy_;
+ NiceMock<MockFrameScheduler> main_frame_{
+ FrameScheduler::FrameType::kMainFrame};
+ NiceMock<MockFrameScheduler> subframe_{FrameScheduler::FrameType::kSubframe};
+ scoped_refptr<MainThreadTaskQueueForTest> timer_queue_{
+ new MainThreadTaskQueueForTest(PrioritisationType::kJavaScriptTimer)};
+ scoped_refptr<MainThreadTaskQueueForTest> non_timer_queue_{
+ new MainThreadTaskQueueForTest(PrioritisationType::kRegular)};
+};
+
+class PerAgentDisableTimersUntilTimeoutStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentDisableTimersUntilTimeoutStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "disable"},
+ {"signal", "delay"},
+ {"delay_ms", "50"}}) {}
+};
+
+TEST_F(PerAgentDisableTimersUntilTimeoutStrategyTest, RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnDelayPassed(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+}
+
+TEST_F(PerAgentDisableTimersUntilTimeoutStrategyTest, InitiatesTimer) {
+ EXPECT_CALL(delegate_, OnSetTimer(_, base::TimeDelta::FromMilliseconds(50)))
+ .Times(1);
+
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+}
+
+TEST_F(PerAgentDisableTimersUntilTimeoutStrategyTest,
+ DisablesTimerQueueUntilTimeout) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+ ignore_result(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_));
+
+ EXPECT_THAT(strategy_->QueueEnabledState(*timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnDelayPassed(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentDisableTimersUntilFMPStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentDisableTimersUntilFMPStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "disable"},
+ {"signal", "fmp"}}) {}
+};
+
+TEST_F(PerAgentDisableTimersUntilFMPStrategyTest, RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ // Only the first input event (since a main frame document was added) should
+ // cause a policy update. This is necessary as we may get several input event
+ // notifications, but we don't want them to re-calculate priorities as nothing
+ // will change.
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kNo);
+}
+
+TEST_F(PerAgentDisableTimersUntilFMPStrategyTest, DisablesTimerQueueUntilFMP) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_THAT(strategy_->QueueEnabledState(*timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentBestEffortPriorityTimersUntilFMPStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentBestEffortPriorityTimersUntilFMPStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "best-effort"},
+ {"signal", "fmp"}}) {}
+};
+
+TEST_F(PerAgentBestEffortPriorityTimersUntilFMPStrategyTest,
+ RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ // Only the first input event (since a main frame document was added) should
+ // cause a policy update. This is necessary as we may get several input event
+ // notifications, but we don't want them to re-calculate priorities as nothing
+ // will change.
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kNo);
+}
+
+TEST_F(PerAgentBestEffortPriorityTimersUntilFMPStrategyTest,
+ LowersTimerQueuePriorityUntilFMP) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentDisableTimersUntilLoadStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentDisableTimersUntilLoadStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "disable"},
+ {"signal", "onload"}}) {}
+};
+
+TEST_F(PerAgentDisableTimersUntilLoadStrategyTest, RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+}
+
+TEST_F(PerAgentDisableTimersUntilLoadStrategyTest, DisablesTimerQueue) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_THAT(strategy_->QueueEnabledState(*timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameLoad(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentBestEffortPriorityTimersUntilLoadStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentBestEffortPriorityTimersUntilLoadStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "best-effort"},
+ {"signal", "onload"}}) {}
+};
+
+TEST_F(PerAgentBestEffortPriorityTimersUntilLoadStrategyTest,
+ RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+}
+
+TEST_F(PerAgentBestEffortPriorityTimersUntilLoadStrategyTest,
+ LowersTimerQueuePriority) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameLoad(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentDisableAllUntilFMPStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentDisableAllUntilFMPStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "all-queues"},
+ {"method", "disable"},
+ {"signal", "fmp"}}) {}
+};
+
+TEST_F(PerAgentDisableAllUntilFMPStrategyTest, RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ // Only the first input event (since a main frame document was added) should
+ // cause a policy update. This is necessary as we may get several input event
+ // notifications, but we don't want them to re-calculate priorities as nothing
+ // will change.
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kNo);
+}
+
+TEST_F(PerAgentDisableAllUntilFMPStrategyTest, DisablesTimerQueueUntilFMP) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_THAT(strategy_->QueueEnabledState(*timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueueEnabledState(*non_timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
+class PerAgentBestEffortPriorityAllUntilFMPStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentBestEffortPriorityAllUntilFMPStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "all-queues"},
+ {"method", "best-effort"},
+ {"signal", "fmp"}}) {}
+};
+
+TEST_F(PerAgentBestEffortPriorityAllUntilFMPStrategyTest,
+ RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+ // Only the first input event (since a main frame document was added) should
+ // cause a policy update. This is necessary as we may get several input event
+ // notifications, but we don't want them to re-calculate priorities as nothing
+ // will change.
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kNo);
+}
+
+TEST_F(PerAgentBestEffortPriorityAllUntilFMPStrategyTest,
+ LowersTimerQueuePriorityUntilFMP) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*non_timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+
+ ignore_result(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
+class PerAgentDisableAllUntilLoadStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentDisableAllUntilLoadStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "all-queues"},
+ {"method", "disable"},
+ {"signal", "onload"}}) {}
+};
+
+TEST_F(PerAgentDisableAllUntilLoadStrategyTest, RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+}
+
+TEST_F(PerAgentDisableAllUntilLoadStrategyTest, DisablesTimerQueue) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_THAT(strategy_->QueueEnabledState(*timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueueEnabledState(*non_timer_queue_),
+ testing::Optional(false));
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+
+ ignore_result(strategy_->OnMainFrameLoad(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
+class PerAgentBestEffortPriorityAllUntilLoadStrategyTest
+ : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentBestEffortPriorityAllUntilLoadStrategyTest()
+ : PerAgentSchedulingBaseTest({{"queues", "all-queues"},
+ {"method", "best-effort"},
+ {"signal", "onload"}}) {}
+};
+
+TEST_F(PerAgentBestEffortPriorityAllUntilLoadStrategyTest,
+ RequestsPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameLoad(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kYes);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kYes);
+}
+
+TEST_F(PerAgentBestEffortPriorityAllUntilLoadStrategyTest,
+ LowersTimerQueuePriority) {
+ ignore_result(strategy_->OnFrameAdded(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_THAT(strategy_->QueuePriority(*non_timer_queue_),
+ testing::Optional(TaskQueue::QueuePriority::kBestEffortPriority));
+
+ ignore_result(strategy_->OnMainFrameLoad(main_frame_));
+
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
+class PerAgentDefaultIsNoOpStrategyTest : public Test {
+ public:
+ PerAgentDefaultIsNoOpStrategyTest() {
+ timer_queue_->SetFrameSchedulerForTest(&subframe_);
+ }
+
+ protected:
+ NiceMock<MockDelegate> delegate_{};
+ std::unique_ptr<AgentSchedulingStrategy> strategy_ =
+ AgentSchedulingStrategy::Create(delegate_);
+ MockFrameScheduler main_frame_{FrameScheduler::FrameType::kMainFrame};
+ NiceMock<MockFrameScheduler> subframe_{FrameScheduler::FrameType::kSubframe};
+ scoped_refptr<MainThreadTaskQueueForTest> timer_queue_{
+ new MainThreadTaskQueueForTest(PrioritisationType::kJavaScriptTimer)};
+};
+
+TEST_F(PerAgentDefaultIsNoOpStrategyTest, DoesntRequestPolicyUpdate) {
+ EXPECT_EQ(strategy_->OnFrameAdded(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnMainFrameFirstMeaningfulPaint(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnFrameRemoved(main_frame_), ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnDocumentChangedInMainFrame(main_frame_),
+ ShouldUpdatePolicy::kNo);
+ EXPECT_EQ(strategy_->OnInputEvent(), ShouldUpdatePolicy::kNo);
+}
+
+TEST_F(PerAgentDefaultIsNoOpStrategyTest, DoesntModifyPolicyDecisions) {
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+}
+
+class PerAgentNonOrdinaryPageTest : public PerAgentSchedulingBaseTest {
+ public:
+ PerAgentNonOrdinaryPageTest()
+ : PerAgentSchedulingBaseTest({{"queues", "timer-queues"},
+ {"method", "disable"},
+ {"signal", "onload"}}) {
+ ON_CALL(non_ordinary_frame_scheduler_, IsOrdinary)
+ .WillByDefault(Return(false));
+ }
+
+ protected:
+ NiceMock<MockFrameScheduler> non_ordinary_frame_scheduler_{
+ FrameScheduler::FrameType::kMainFrame};
+};
+
+TEST_F(PerAgentNonOrdinaryPageTest, DoesntWaitForNonOrdinaryFrames) {
+ EXPECT_EQ(strategy_->OnFrameAdded(non_ordinary_frame_scheduler_),
+ ShouldUpdatePolicy::kYes);
+ EXPECT_FALSE(strategy_->QueueEnabledState(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueueEnabledState(*non_timer_queue_).has_value());
+ EXPECT_FALSE(strategy_->QueuePriority(*non_timer_queue_).has_value());
+}
+
+} // namespace scheduler
+} // namespace blink
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 3e44df92bbc..666ce8698f2 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
@@ -8,6 +8,8 @@
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
+#include "base/task/sequence_manager/lazy_now.h"
+#include "base/time/time.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"
@@ -95,17 +97,16 @@ FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl::
frame_scheduler_->RemovePauseSubresourceLoadingHandle();
}
-std::unique_ptr<FrameSchedulerImpl> FrameSchedulerImpl::Create(
+FrameSchedulerImpl::FrameSchedulerImpl(
PageSchedulerImpl* parent_page_scheduler,
FrameScheduler::Delegate* delegate,
base::trace_event::BlameContext* blame_context,
- FrameScheduler::FrameType frame_type) {
- std::unique_ptr<FrameSchedulerImpl> frame_scheduler(new FrameSchedulerImpl(
- parent_page_scheduler->GetMainThreadScheduler(), parent_page_scheduler,
- delegate, blame_context, frame_type));
- parent_page_scheduler->RegisterFrameSchedulerImpl(frame_scheduler.get());
- return frame_scheduler;
-}
+ FrameScheduler::FrameType frame_type)
+ : FrameSchedulerImpl(parent_page_scheduler->GetMainThreadScheduler(),
+ parent_page_scheduler,
+ delegate,
+ blame_context,
+ frame_type) {}
FrameSchedulerImpl::FrameSchedulerImpl(
MainThreadSchedulerImpl* main_thread_scheduler,
@@ -154,7 +155,13 @@ FrameSchedulerImpl::FrameSchedulerImpl(
this,
&tracing_controller_,
YesNoStateToString),
- aggressive_throttling_opt_out_count(0),
+ all_throttling_opt_out_count_(0),
+ aggressive_throttling_opt_out_count_(0),
+ opted_out_from_all_throttling_(false,
+ "FrameScheduler.AllThrottlingDisabled",
+ this,
+ &tracing_controller_,
+ YesNoStateToString),
opted_out_from_aggressive_throttling_(
false,
"FrameScheduler.AggressiveThrottlingDisabled",
@@ -227,8 +234,7 @@ FrameSchedulerImpl::~FrameSchedulerImpl() {
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
if (task_queue_and_voter.first->CanBeThrottled()) {
- RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(
- task_queue_and_voter.first);
+ RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
}
CleanUpQueue(task_queue_and_voter.first);
}
@@ -236,8 +242,10 @@ FrameSchedulerImpl::~FrameSchedulerImpl() {
if (parent_page_scheduler_) {
parent_page_scheduler_->Unregister(this);
- if (opted_out_from_aggressive_throttling())
- parent_page_scheduler_->OnAggressiveThrottlingStatusUpdated();
+ if (opted_out_from_all_throttling() ||
+ opted_out_from_aggressive_throttling()) {
+ parent_page_scheduler_->OnThrottlingStatusUpdated();
+ }
}
// Can be null in tests.
@@ -249,15 +257,14 @@ void FrameSchedulerImpl::DetachFromPageScheduler() {
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
if (task_queue_and_voter.first->CanBeThrottled()) {
- RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(
- task_queue_and_voter.first);
+ RemoveThrottleableQueueFromBudgetPools(task_queue_and_voter.first);
}
}
parent_page_scheduler_ = nullptr;
}
-void FrameSchedulerImpl::RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(
+void FrameSchedulerImpl::RemoveThrottleableQueueFromBudgetPools(
MainThreadTaskQueue* task_queue) {
DCHECK(task_queue);
DCHECK(task_queue->CanBeThrottled());
@@ -265,20 +272,22 @@ void FrameSchedulerImpl::RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(
if (!parent_page_scheduler_)
return;
- CPUTimeBudgetPool* time_budget_pool =
- parent_page_scheduler_->BackgroundCPUTimeBudgetPool();
-
- if (!time_budget_pool)
- return;
+ CPUTimeBudgetPool* cpu_time_budget_pool =
+ parent_page_scheduler_->background_cpu_time_budget_pool();
// On tests, the scheduler helper might already be shut down and tick is not
// available.
- base::TimeTicks now;
- if (main_thread_scheduler_->tick_clock())
- now = main_thread_scheduler_->tick_clock()->NowTicks();
- else
- now = base::TimeTicks::Now();
- time_budget_pool->RemoveQueue(now, task_queue);
+ base::sequence_manager::LazyNow lazy_now =
+ main_thread_scheduler_->tick_clock()
+ ? base::sequence_manager::LazyNow(
+ main_thread_scheduler_->tick_clock())
+ : base::sequence_manager::LazyNow(base::TimeTicks::Now());
+
+ if (cpu_time_budget_pool)
+ cpu_time_budget_pool->RemoveQueue(lazy_now.Now(), task_queue);
+
+ parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(
+ task_queue, frame_origin_type_, &lazy_now);
}
void FrameSchedulerImpl::SetFrameVisible(bool frame_visible) {
@@ -300,11 +309,40 @@ void FrameSchedulerImpl::SetCrossOriginToMainFrame(bool cross_origin) {
DCHECK(!cross_origin);
return;
}
+
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+
+ // Remove throttleable TaskQueues from their current WakeUpBudgetPool.
+ //
+ // The WakeUpBudgetPool is selected based on origin. TaskQueues are reinserted
+ // in the appropriate WakeUpBudgetPool at the end of this method, after the
+ // |frame_origin_type_| is updated.
+ for (const auto& task_queue_and_voter :
+ frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
+ if (task_queue_and_voter.first->CanBeThrottled()) {
+ parent_page_scheduler_->RemoveQueueFromWakeUpBudgetPool(
+ task_queue_and_voter.first, frame_origin_type_, &lazy_now);
+ }
+ }
+
+ // Update the FrameOriginType.
if (cross_origin) {
frame_origin_type_ = FrameOriginType::kCrossOriginToMainFrame;
} else {
frame_origin_type_ = FrameOriginType::kSameOriginToMainFrame;
}
+
+ // Add throttleable TaskQueues to WakeUpBudgetPool that corresponds to the
+ // updated |frame_origin_type_|.
+ for (const auto& task_queue_and_voter :
+ frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
+ if (task_queue_and_voter.first->CanBeThrottled()) {
+ parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
+ task_queue_and_voter.first, frame_origin_type_, &lazy_now);
+ }
+ }
+
UpdatePolicy();
}
@@ -353,7 +391,8 @@ QueueTraits FrameSchedulerImpl::CreateQueueTraitsForTaskType(TaskType type) {
return ThrottleableTaskQueueTraits().SetPrioritisationType(
QueueTraits::PrioritisationType::kBestEffort);
case TaskType::kJavascriptTimer:
- return ThrottleableTaskQueueTraits();
+ return ThrottleableTaskQueueTraits().SetPrioritisationType(
+ QueueTraits::PrioritisationType::kJavaScriptTimer);
case TaskType::kInternalLoading:
case TaskType::kNetworking:
case TaskType::kNetworkingWithURLLoaderAnnotation:
@@ -555,12 +594,24 @@ void FrameSchedulerImpl::DidCommitProvisionalLoad(
bool is_web_history_inert_commit,
NavigationType navigation_type) {
bool is_main_frame = GetFrameType() == FrameType::kMainFrame;
- if (is_main_frame && navigation_type != NavigationType::kSameDocument)
+ bool is_same_document = navigation_type == NavigationType::kSameDocument;
+
+ if (!is_same_document) {
+ waiting_for_contentful_paint_ = true;
+ waiting_for_meaningful_paint_ = true;
+ }
+ if (is_main_frame && !is_same_document) {
task_time_ = base::TimeDelta();
+ // Ignore result here, based on the assumption that
+ // MTSI::DidCommitProvisionalLoad will trigger an update policy.
+ ignore_result(main_thread_scheduler_->agent_scheduling_strategy()
+ .OnDocumentChangedInMainFrame(*this));
+ }
+
main_thread_scheduler_->DidCommitProvisionalLoad(
is_web_history_inert_commit, navigation_type == NavigationType::kReload,
is_main_frame);
- if (navigation_type != NavigationType::kSameDocument)
+ if (!is_same_document)
ResetForNavigation();
}
@@ -590,6 +641,8 @@ void FrameSchedulerImpl::OnStartedUsingFeature(
const SchedulingPolicy& policy) {
uint64_t old_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
+ if (policy.disable_all_throttling)
+ OnAddedAllThrottlingOptOut();
if (policy.disable_aggressive_throttling)
OnAddedAggressiveThrottlingOptOut();
if (policy.disable_back_forward_cache) {
@@ -613,6 +666,8 @@ void FrameSchedulerImpl::OnStoppedUsingFeature(
const SchedulingPolicy& policy) {
uint64_t old_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask();
+ if (policy.disable_all_throttling)
+ OnRemovedAllThrottlingOptOut();
if (policy.disable_aggressive_throttling)
OnRemovedAggressiveThrottlingOptOut();
if (policy.disable_back_forward_cache)
@@ -655,21 +710,42 @@ base::WeakPtr<FrameScheduler> FrameSchedulerImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
+base::WeakPtr<const FrameSchedulerImpl> FrameSchedulerImpl::GetWeakPtr() const {
+ return weak_factory_.GetWeakPtr();
+}
+
+void FrameSchedulerImpl::OnAddedAllThrottlingOptOut() {
+ ++all_throttling_opt_out_count_;
+ opted_out_from_all_throttling_ =
+ static_cast<bool>(all_throttling_opt_out_count_);
+ if (parent_page_scheduler_)
+ parent_page_scheduler_->OnThrottlingStatusUpdated();
+}
+
+void FrameSchedulerImpl::OnRemovedAllThrottlingOptOut() {
+ DCHECK_GT(all_throttling_opt_out_count_, 0);
+ --all_throttling_opt_out_count_;
+ opted_out_from_all_throttling_ =
+ static_cast<bool>(all_throttling_opt_out_count_);
+ if (parent_page_scheduler_)
+ parent_page_scheduler_->OnThrottlingStatusUpdated();
+}
+
void FrameSchedulerImpl::OnAddedAggressiveThrottlingOptOut() {
- ++aggressive_throttling_opt_out_count;
+ ++aggressive_throttling_opt_out_count_;
opted_out_from_aggressive_throttling_ =
- static_cast<bool>(aggressive_throttling_opt_out_count);
+ static_cast<bool>(aggressive_throttling_opt_out_count_);
if (parent_page_scheduler_)
- parent_page_scheduler_->OnAggressiveThrottlingStatusUpdated();
+ parent_page_scheduler_->OnThrottlingStatusUpdated();
}
void FrameSchedulerImpl::OnRemovedAggressiveThrottlingOptOut() {
- DCHECK_GT(aggressive_throttling_opt_out_count, 0);
- --aggressive_throttling_opt_out_count;
+ DCHECK_GT(aggressive_throttling_opt_out_count_, 0);
+ --aggressive_throttling_opt_out_count_;
opted_out_from_aggressive_throttling_ =
- static_cast<bool>(aggressive_throttling_opt_out_count);
+ static_cast<bool>(aggressive_throttling_opt_out_count_);
if (parent_page_scheduler_)
- parent_page_scheduler_->OnAggressiveThrottlingStatusUpdated();
+ parent_page_scheduler_->OnThrottlingStatusUpdated();
}
void FrameSchedulerImpl::OnAddedBackForwardCacheOptOut(
@@ -730,11 +806,6 @@ bool FrameSchedulerImpl::IsPageVisible() const {
: true;
}
-bool FrameSchedulerImpl::IsAudioPlaying() const {
- return parent_page_scheduler_ ? parent_page_scheduler_->IsAudioPlaying()
- : false;
-}
-
void FrameSchedulerImpl::SetPaused(bool frame_paused) {
DCHECK(parent_page_scheduler_);
if (frame_paused_ == frame_paused)
@@ -823,7 +894,8 @@ SchedulingLifecycleState FrameSchedulerImpl::CalculateLifecycleState(
parent_page_scheduler_->OptedOutFromAggressiveThrottling()) {
return SchedulingLifecycleState::kNotThrottled;
}
- if (parent_page_scheduler_->IsThrottled())
+ // Note: The scheduling lifecycle state ignores wake up rate throttling.
+ if (parent_page_scheduler_->IsCPUTimeThrottled())
return SchedulingLifecycleState::kThrottled;
if (!parent_page_scheduler_->IsPageVisible())
return SchedulingLifecycleState::kHidden;
@@ -833,13 +905,31 @@ SchedulingLifecycleState FrameSchedulerImpl::CalculateLifecycleState(
void FrameSchedulerImpl::OnFirstContentfulPaint() {
waiting_for_contentful_paint_ = false;
if (GetFrameType() == FrameScheduler::FrameType::kMainFrame)
- main_thread_scheduler_->OnMainFramePaint();
+ main_thread_scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
}
void FrameSchedulerImpl::OnFirstMeaningfulPaint() {
waiting_for_meaningful_paint_ = false;
- if (GetFrameType() == FrameScheduler::FrameType::kMainFrame)
- main_thread_scheduler_->OnMainFramePaint();
+
+ bool force_policy_update = false;
+ if (GetFrameType() == FrameScheduler::FrameType::kMainFrame) {
+ if (main_thread_scheduler_->agent_scheduling_strategy()
+ .OnMainFrameFirstMeaningfulPaint(*this) ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
+ force_policy_update = true;
+ }
+ }
+
+ main_thread_scheduler_->OnMainFramePaint(force_policy_update);
+}
+
+void FrameSchedulerImpl::OnLoad() {
+ if (GetFrameType() == FrameScheduler::FrameType::kMainFrame) {
+ // TODO(talp): Once MTSI::UpdatePolicyLocked is refactored, this can notify
+ // the agent strategy directly and, if necessary, trigger the queue priority
+ // update.
+ main_thread_scheduler_->OnMainFrameLoad(*this);
+ }
}
bool FrameSchedulerImpl::IsWaitingForContentfulPaint() const {
@@ -850,6 +940,12 @@ bool FrameSchedulerImpl::IsWaitingForMeaningfulPaint() const {
return waiting_for_meaningful_paint_;
}
+bool FrameSchedulerImpl::IsOrdinary() const {
+ if (!parent_page_scheduler_)
+ return true;
+ return parent_page_scheduler_->IsOrdinary();
+}
+
bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const {
// TODO(crbug.com/1078387): Convert the CHECK to a DCHECK once enough time has
// passed to confirm that it is correct. (November 2020).
@@ -859,6 +955,8 @@ bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const {
return false;
if (parent_page_scheduler_->IsAudioPlaying())
return false;
+ if (parent_page_scheduler_->OptedOutFromAllThrottling())
+ return false;
if (!parent_page_scheduler_->IsPageVisible())
return true;
return RuntimeEnabledFeatures::TimerThrottlingForHiddenFramesEnabled() &&
@@ -998,6 +1096,14 @@ TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority(
}
}
+ // Consult per-agent scheduling strategy to see if it wants to affect queue
+ // priority. Done here to avoid interfering with other policy decisions.
+ base::Optional<TaskQueue::QueuePriority> per_agent_priority =
+ main_thread_scheduler_->agent_scheduling_strategy().QueuePriority(
+ *task_queue);
+ if (per_agent_priority.has_value())
+ return per_agent_priority.value();
+
if (task_queue->GetPrioritisationType() ==
MainThreadTaskQueue::QueueTraits::PrioritisationType::kLoadingControl) {
return main_thread_scheduler_
@@ -1079,12 +1185,18 @@ void FrameSchedulerImpl::OnTaskQueueCreated(
UpdateQueuePolicy(task_queue, voter);
if (task_queue->CanBeThrottled()) {
- CPUTimeBudgetPool* time_budget_pool =
- parent_page_scheduler_->BackgroundCPUTimeBudgetPool();
- if (time_budget_pool) {
- time_budget_pool->AddQueue(
- main_thread_scheduler_->tick_clock()->NowTicks(), task_queue);
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+
+ CPUTimeBudgetPool* cpu_time_budget_pool =
+ parent_page_scheduler_->background_cpu_time_budget_pool();
+ if (cpu_time_budget_pool) {
+ cpu_time_budget_pool->AddQueue(lazy_now.Now(), task_queue);
}
+
+ parent_page_scheduler_->AddQueueToWakeUpBudgetPool(
+ task_queue, frame_origin_type_, &lazy_now);
+
if (task_queues_throttled_) {
UpdateTaskQueueThrottling(task_queue, true);
}
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 0957d7676f3..3390bd30c3c 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
@@ -73,11 +73,10 @@ class PageSchedulerImplTest;
class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
FrameTaskQueueController::Delegate {
public:
- static std::unique_ptr<FrameSchedulerImpl> Create(
- PageSchedulerImpl* page_scheduler,
- FrameScheduler::Delegate* delegate,
- base::trace_event::BlameContext* blame_context,
- FrameScheduler::FrameType frame_type);
+ FrameSchedulerImpl(PageSchedulerImpl* page_scheduler,
+ FrameScheduler::Delegate* delegate,
+ base::trace_event::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type);
~FrameSchedulerImpl() override;
// FrameOrWorkerScheduler implementation:
@@ -88,7 +87,6 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
bool IsFrameVisible() const override;
bool IsPageVisible() const override;
- bool IsAudioPlaying() const;
void SetPaused(bool frame_paused) override;
void SetShouldReportPostedTasksWhenDisabled(bool should_report) override;
@@ -121,9 +119,15 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
void OnFirstContentfulPaint() override;
void OnFirstMeaningfulPaint() override;
+ void OnLoad() override;
bool IsWaitingForContentfulPaint() const;
bool IsWaitingForMeaningfulPaint() const;
+ // An "ordinary" FrameScheduler is responsible for a frame whose parent page
+ // is a fully-featured page owned by a web view (as opposed to, e.g.: a Page
+ // created by an SVGImage). Virtual for testing.
+ virtual bool IsOrdinary() const;
+
void AsValueInto(base::trace_event::TracedValue* state) const;
bool IsExemptFromBudgetBasedThrottling() const override;
std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
@@ -135,13 +139,21 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
const SchedulingPolicy& policy) override;
base::WeakPtr<FrameScheduler> GetWeakPtr() override;
+ base::WeakPtr<const FrameSchedulerImpl> GetWeakPtr() const;
scoped_refptr<base::SingleThreadTaskRunner> ControlTaskRunner();
void UpdatePolicy();
+ // Whether the frame is opted-out from any kind of throttling.
+ bool opted_out_from_all_throttling() const {
+ return opted_out_from_all_throttling_;
+ }
+ // Whether the frame is opted-out from CPU time throttling and intensive wake
+ // up throttling.
bool opted_out_from_aggressive_throttling() const {
- return opted_out_from_aggressive_throttling_;
+ return opted_out_from_all_throttling_ ||
+ opted_out_from_aggressive_throttling_;
}
void OnTraceLogEnabled() { tracing_controller_.OnTraceLogEnabled(); }
@@ -151,7 +163,7 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
void SetPageFrozenForTracing(bool frozen);
// Computes the priority of |task_queue| if it is associated to this frame
- // scheduler. Note that the main's thread policy should be upto date to
+ // scheduler. Note that the main thread's policy should be upto date to
// compute the correct priority.
base::sequence_manager::TaskQueue::QueuePriority ComputePriority(
MainThreadTaskQueue* task_queue) const;
@@ -209,6 +221,7 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest;
friend class frame_scheduler_impl_unittest::FrameSchedulerImplTest;
friend class page_scheduler_impl_unittest::PageSchedulerImplTest;
+ friend class PerAgentSchedulingBaseTest;
friend class ResourceLoadingTaskRunnerHandleImpl;
friend class ::blink::MainThreadSchedulerTest;
@@ -230,8 +243,7 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
};
void DetachFromPageScheduler();
- void RemoveThrottleableQueueFromBackgroundCPUTimeBudgetPool(
- MainThreadTaskQueue*);
+ void RemoveThrottleableQueueFromBudgetPools(MainThreadTaskQueue*);
void ApplyPolicyToThrottleableQueue();
bool ShouldThrottleTaskQueues() const;
SchedulingLifecycleState CalculateLifecycleState(
@@ -248,6 +260,9 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
void AddPauseSubresourceLoadingHandle();
void RemovePauseSubresourceLoadingHandle();
+ void OnAddedAllThrottlingOptOut();
+ void OnRemovedAllThrottlingOptOut();
+
void OnAddedAggressiveThrottlingOptOut();
void OnRemovedAggressiveThrottlingOptOut();
@@ -323,9 +338,11 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
TraceableState<bool, TracingCategoryName::kInfo> task_queues_throttled_;
TraceableState<bool, TracingCategoryName::kInfo>
preempted_for_cooperative_scheduling_;
- // TODO(kraynov): https://crbug.com/827113
- // Trace the count of aggressive throttling opt outs.
- int aggressive_throttling_opt_out_count;
+ // TODO(https://crbug.com/827113): Trace the count of opt-outs.
+ int all_throttling_opt_out_count_;
+ int aggressive_throttling_opt_out_count_;
+ TraceableState<bool, TracingCategoryName::kInfo>
+ opted_out_from_all_throttling_;
TraceableState<bool, TracingCategoryName::kInfo>
opted_out_from_aggressive_throttling_;
size_t subresource_loading_pause_count_;
@@ -361,7 +378,7 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler,
// and documents.
base::WeakPtrFactory<FrameSchedulerImpl> document_bound_weak_factory_{this};
- base::WeakPtrFactory<FrameSchedulerImpl> weak_factory_{this};
+ mutable base::WeakPtrFactory<FrameSchedulerImpl> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(FrameSchedulerImpl);
};
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 f67f8b93cad..4929170f1d3 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
@@ -15,12 +15,15 @@
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/renderer/platform/scheduler/common/features.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
@@ -28,6 +31,7 @@
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/resource_loading_task_runner_handle_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
@@ -42,9 +46,46 @@ namespace frame_scheduler_impl_unittest {
using FeatureHandle = FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle;
using PrioritisationType = MainThreadTaskQueue::QueueTraits::PrioritisationType;
-
using testing::Return;
+namespace {
+
+constexpr base::TimeDelta kDefaultThrottledWakeUpInterval =
+ PageSchedulerImpl::kDefaultThrottledWakeUpInterval;
+constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromMilliseconds(10);
+
+// This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
+// returns the PageScheduler as a PageSchedulerImpl.
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
+ PageScheduler::Delegate* page_scheduler_delegate,
+ MainThreadSchedulerImpl* scheduler) {
+ std::unique_ptr<PageScheduler> page_scheduler =
+ scheduler->CreatePageScheduler(page_scheduler_delegate);
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+ static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+ return page_scheduler_impl;
+}
+
+// This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
+// returns the FrameScheduler as a FrameSchedulerImpl.
+std::unique_ptr<FrameSchedulerImpl> CreateFrameScheduler(
+ PageSchedulerImpl* page_scheduler,
+ FrameScheduler::Delegate* delegate,
+ blink::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ auto frame_scheduler =
+ page_scheduler->CreateFrameScheduler(delegate, blame_context, frame_type);
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_impl(
+ static_cast<FrameSchedulerImpl*>(frame_scheduler.release()));
+ return frame_scheduler_impl;
+}
+
+void RecordRunTime(std::vector<base::TimeTicks>* run_times) {
+ run_times->push_back(base::TimeTicks::Now());
+}
+
+} // namespace
+
// All TaskTypes that can be passed to
// FrameSchedulerImpl::CreateQueueTraitsForTaskType().
constexpr TaskType kAllFrameTaskTypes[] = {
@@ -105,9 +146,9 @@ void AppendToVectorTestTask(Vector<String>* vector, String value) {
class FrameSchedulerDelegateForTesting : public FrameScheduler::Delegate {
public:
- FrameSchedulerDelegateForTesting() {}
+ FrameSchedulerDelegateForTesting() = default;
- ~FrameSchedulerDelegateForTesting() override {}
+ ~FrameSchedulerDelegateForTesting() override = default;
ukm::UkmRecorder* GetUkmRecorder() override { return nullptr; }
@@ -133,12 +174,23 @@ class FrameSchedulerImplTest : public testing::Test {
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
+ // Constructs a FrameSchedulerImplTest with a list of features to enable and a
+ // list of features to disable.
FrameSchedulerImplTest(std::vector<base::Feature> features_to_enable,
std::vector<base::Feature> features_to_disable)
: FrameSchedulerImplTest() {
feature_list_.InitWithFeatures(features_to_enable, features_to_disable);
}
+ // Constructs a FrameSchedulerImplTest with a list of features to enable and
+ // associated params.
+ explicit FrameSchedulerImplTest(
+ const std::vector<base::test::ScopedFeatureList::FeatureAndParams>&
+ features_to_enable)
+ : FrameSchedulerImplTest() {
+ feature_list_.InitWithFeaturesAndParameters(features_to_enable, {});
+ }
+
~FrameSchedulerImplTest() override = default;
void SetUp() override {
@@ -147,21 +199,20 @@ class FrameSchedulerImplTest : public testing::Test {
nullptr, task_environment_.GetMainThreadTaskRunner(),
task_environment_.GetMockTickClock()),
base::nullopt);
- page_scheduler_ =
- std::make_unique<PageSchedulerImpl>(nullptr, scheduler_.get());
+ page_scheduler_ = CreatePageScheduler(nullptr, scheduler_.get());
frame_scheduler_delegate_ = std::make_unique<
testing::StrictMock<FrameSchedulerDelegateForTesting>>();
- frame_scheduler_ = FrameSchedulerImpl::Create(
+ frame_scheduler_ = CreateFrameScheduler(
page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
FrameScheduler::FrameType::kSubframe);
}
void ResetFrameScheduler(FrameScheduler::FrameType frame_type) {
- frame_scheduler_delegate_ = std::make_unique<
+ auto new_delegate_ = std::make_unique<
testing::StrictMock<FrameSchedulerDelegateForTesting>>();
- frame_scheduler_ = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- frame_type);
+ frame_scheduler_ = CreateFrameScheduler(
+ page_scheduler_.get(), new_delegate_.get(), nullptr, frame_type);
+ frame_scheduler_delegate_ = std::move(new_delegate_);
}
void TearDown() override {
@@ -230,6 +281,15 @@ class FrameSchedulerImplTest : public testing::Test {
frame_scheduler_delegate_->update_task_time_calls_ = 0;
}
+ // Fast-forwards to the next time aligned on |interval|.
+ void FastForwardToAlignedTime(base::TimeDelta interval) {
+ const base::TimeTicks now = base::TimeTicks::Now();
+ const base::TimeTicks aligned =
+ now.SnappedToNextTick(base::TimeTicks(), interval);
+ if (aligned != now)
+ task_environment_.FastForwardBy(aligned - now);
+ }
+
static uint64_t GetActiveFeaturesTrackedForBackForwardCacheMetricsMask(
FrameSchedulerImpl* frame_scheduler) {
return frame_scheduler
@@ -258,6 +318,12 @@ class FrameSchedulerImplTest : public testing::Test {
FrameSchedulerImpl::ThrottleableTaskQueueTraits());
}
+ scoped_refptr<TaskQueue> JavaScriptTimerTaskQueue() {
+ return GetTaskQueue(
+ FrameSchedulerImpl::ThrottleableTaskQueueTraits().SetPrioritisationType(
+ PrioritisationType::kJavaScriptTimer));
+ }
+
scoped_refptr<TaskQueue> LoadingTaskQueue() {
return GetTaskQueue(
FrameSchedulerImpl::LoadingTaskQueueTraits());
@@ -350,6 +416,13 @@ class FrameSchedulerImplStopNonTimersInBackgroundDisabledTest
{blink::features::kStopNonTimersInBackground}) {}
};
+class FrameSchedulerImplStopInBackgroundDisabledTest
+ : public FrameSchedulerImplTest {
+ public:
+ FrameSchedulerImplStopInBackgroundDisabledTest()
+ : FrameSchedulerImplTest({}, {blink::features::kStopInBackground}) {}
+};
+
namespace {
class MockLifecycleObserver final : public FrameScheduler::Observer {
@@ -412,6 +485,50 @@ void RunTaskOfLength(base::test::TaskEnvironment* task_environment,
task_environment->FastForwardBy(length);
}
+class FrameSchedulerImplTestWithIntensiveWakeUpThrottling
+ : public FrameSchedulerImplTest {
+ public:
+ using Super = FrameSchedulerImplTest;
+
+ FrameSchedulerImplTestWithIntensiveWakeUpThrottling()
+ : FrameSchedulerImplTest({features::kIntensiveWakeUpThrottling},
+ {features::kStopInBackground}) {}
+
+ void SetUp() override {
+ Super::SetUp();
+ ClearIntensiveWakeUpThrottlingPolicyOverrideCacheForTesting();
+ }
+
+ void TearDown() override {
+ ClearIntensiveWakeUpThrottlingPolicyOverrideCacheForTesting();
+ Super::TearDown();
+ }
+
+ const int kNumTasks = 5;
+ const base::TimeDelta kGracePeriod =
+ GetIntensiveWakeUpThrottlingGracePeriod();
+ const base::TimeDelta kIntensiveThrottlingDurationBetweenWakeUps =
+ GetIntensiveWakeUpThrottlingDurationBetweenWakeUps();
+};
+
+class FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride
+ : public FrameSchedulerImplTestWithIntensiveWakeUpThrottling {
+ public:
+ // This should only be called once per test, and prior to the
+ // PageSchedulerImpl logic actually parsing the policy switch.
+ void SetPolicyOverride(bool enabled) {
+ DCHECK(!scoped_command_line_.GetProcessCommandLine()->HasSwitch(
+ switches::kIntensiveWakeUpThrottlingPolicy));
+ scoped_command_line_.GetProcessCommandLine()->AppendSwitchASCII(
+ switches::kIntensiveWakeUpThrottlingPolicy,
+ enabled ? switches::kIntensiveWakeUpThrottlingPolicy_ForceEnable
+ : switches::kIntensiveWakeUpThrottlingPolicy_ForceDisable);
+ }
+
+ private:
+ base::test::ScopedCommandLine scoped_command_line_;
+};
+
} // namespace
// Throttleable task queue is initialized lazily, so there're two scenarios:
@@ -581,6 +698,155 @@ TEST_F(FrameSchedulerImplTest, PauseAndResumeForCooperativeScheduling) {
EXPECT_TRUE(UnpausableTaskQueue()->IsQueueEnabled());
}
+namespace {
+
+// A task that re-posts itself with a delay in order until it has run
+// |num_remaining_tasks| times.
+void RePostTask(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ base::TimeDelta delay,
+ int* num_remaining_tasks) {
+ --(*num_remaining_tasks);
+ if (*num_remaining_tasks > 0) {
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RePostTask, task_runner, delay,
+ base::Unretained(num_remaining_tasks)),
+ delay);
+ }
+}
+
+} // namespace
+
+// Verify that tasks in a throttled task queue cause 1 wake up per second, when
+// intensive wake up throttling is disabled. Disable the kStopInBackground
+// feature because it hides the effect of intensive wake up throttling.
+TEST_F(FrameSchedulerImplStopInBackgroundDisabledTest, ThrottledTaskExecution) {
+ // This test posts enough tasks to run past the default intensive wake up
+ // throttling grace period. This allows verifying that intensive wake up
+ // throttling is disabled by default.
+ constexpr int kNumTasks =
+ base::TimeDelta::FromMinutes(10) / base::TimeDelta::FromSeconds(1);
+ // This TaskRunner is throttled.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Hide the page. This enables wake up throttling.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+
+ // Post an initial task.
+ int num_remaining_tasks = kNumTasks;
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RePostTask, task_runner, kShortDelay,
+ base::Unretained(&num_remaining_tasks)),
+ kShortDelay);
+
+ // A task should run every second.
+ while (num_remaining_tasks > 0) {
+ int previous_num_remaining_tasks = num_remaining_tasks;
+ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(previous_num_remaining_tasks - 1, num_remaining_tasks);
+ }
+}
+
+// Verify that tasks in a throttled task queue are not throttled when there is
+// an active opt-out.
+TEST_F(FrameSchedulerImplStopInBackgroundDisabledTest, NoThrottlingWithOptOut) {
+ constexpr int kNumTasks = 3;
+ // |task_runner| is throttled.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+ // |other_task_runner| is throttled. It belongs to a different frame on the
+ // same page.
+ const auto other_frame_scheduler = CreateFrameScheduler(
+ page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kSubframe);
+ const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Fast-forward the time to a multiple of |kDefaultThrottledWakeUpInterval|.
+ // Otherwise, the time at which tasks run will vary.
+ FastForwardToAlignedTime(kDefaultThrottledWakeUpInterval);
+
+ // Hide the page. This enables wake up throttling.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+
+ {
+ // Wake ups are throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval));
+ }
+
+ {
+ // Create an opt-out.
+ auto handle = frame_scheduler_->RegisterFeature(
+ SchedulingPolicy::Feature::kWebRTC,
+ {SchedulingPolicy::DisableAllThrottling()});
+
+ {
+ // A task should run every |kShortDelay|, since there is an opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(scope_start + kShortDelay * 1,
+ scope_start + kShortDelay * 2,
+ scope_start + kShortDelay * 3));
+ }
+
+ {
+ // Same thing for another frame on the same page.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ other_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(scope_start + kShortDelay * 1,
+ scope_start + kShortDelay * 2,
+ scope_start + kShortDelay * 3));
+ }
+ }
+
+ FastForwardToAlignedTime(kDefaultThrottledWakeUpInterval);
+
+ {
+ // Wake ups are throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval));
+ }
+}
+
TEST_F(FrameSchedulerImplTest, FreezeForegroundOnlyTasks) {
int counter = 0;
ForegroundOnlyTaskQueue()->task_runner()->PostTask(
@@ -1137,9 +1403,10 @@ class LowPriorityHiddenFrameDuringLoadingExperimentTest
TEST_F(LowPriorityHiddenFrameDuringLoadingExperimentTest,
FrameQueuesPriorities) {
// Main thread scheduler is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1200,8 +1467,8 @@ TEST_F(LowPrioritySubFrameExperimentTest, FrameQueuesPriorities) {
TaskQueue::QueuePriority::kLowPriority);
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kMainFrame);
// Main Frame Task Queues.
EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(),
@@ -1229,9 +1496,10 @@ class LowPrioritySubFrameDuringLoadingExperimentTest
TEST_F(LowPrioritySubFrameDuringLoadingExperimentTest, FrameQueuesPriorities) {
// Main thread scheduler is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1294,8 +1562,8 @@ TEST_F(LowPrioritySubFrameThrottleableTaskExperimentTest,
TaskQueue::QueuePriority::kNormalPriority);
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kMainFrame);
// Main Frame Task Queues.
EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(),
@@ -1324,9 +1592,10 @@ class LowPrioritySubFrameThrottleableTaskDuringLoadingExperimentTest
TEST_F(LowPrioritySubFrameThrottleableTaskDuringLoadingExperimentTest,
FrameQueuesPriorities) {
// Main thread scheduler is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1388,8 +1657,8 @@ TEST_F(LowPriorityThrottleableTaskExperimentTest, FrameQueuesPriorities) {
TaskQueue::QueuePriority::kNormalPriority);
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kMainFrame);
// Main Frame Task Queues.
EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(),
@@ -1418,9 +1687,10 @@ class LowPriorityThrottleableTaskDuringLoadingExperimentTest
TEST_F(LowPriorityThrottleableTaskDuringLoadingExperimentTest,
SubFrameQueuesPriorities) {
// Main thread is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1463,8 +1733,8 @@ TEST_F(LowPriorityThrottleableTaskDuringLoadingExperimentTest,
frame_scheduler_->OnFirstMeaningfulPaint();
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kMainFrame);
// Main thread is in the loading use case.
frame_scheduler_->OnFirstContentfulPaint();
@@ -1558,9 +1828,10 @@ TEST_F(LowPriorityAdFrameDuringLoadingExperimentTest, FrameQueuesPriorities) {
EXPECT_TRUE(frame_scheduler_->IsAdFrame());
// Main thread scheduler is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1654,9 +1925,10 @@ TEST_F(BestEffortPriorityAdFrameDuringLoadingExperimentTest,
EXPECT_TRUE(frame_scheduler_->IsAdFrame());
// Main thread scheduler is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1744,9 +2016,10 @@ class ResourceFetchPriorityExperimentOnlyWhenLoadingTest
};
TEST_F(ResourceFetchPriorityExperimentOnlyWhenLoadingTest, DidChangePriority) {
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
std::unique_ptr<ResourceLoadingTaskRunnerHandleImpl> handle =
GetResourceLoadingTaskRunnerHandleImpl();
@@ -1846,9 +2119,10 @@ class LowPriorityCrossOriginTaskDuringLoadingExperimentTest
TEST_F(LowPriorityCrossOriginTaskDuringLoadingExperimentTest,
FrameQueuesPriorities) {
// Main thread is in the loading use case.
- auto main_frame_scheduler = FrameSchedulerImpl::Create(
- page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kMainFrame);
main_frame_scheduler->OnFirstContentfulPaint();
ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
@@ -1905,7 +2179,8 @@ TEST_F(FrameSchedulerImplTest, TaskTypeToTaskQueueMapping) {
// Make sure the queue lookup and task type to queue traits map works as
// expected. This test will fail if these task types are moved to different
// default queues.
- EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimer), ThrottleableTaskQueue());
+ EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimer),
+ JavaScriptTimerTaskQueue());
EXPECT_EQ(GetTaskQueue(TaskType::kWebSocket), DeferrableTaskQueue());
EXPECT_EQ(GetTaskQueue(TaskType::kDatabaseAccess), PausableTaskQueue());
EXPECT_EQ(GetTaskQueue(TaskType::kPostedMessage), PausableTaskQueue());
@@ -2308,22 +2583,12 @@ TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrder) {
testing::ElementsAre("V1", "V2", "B1", "B2", "U1", "U2"));
}
-namespace {
-void RecordRunTime(std::vector<base::TimeTicks>* run_times) {
- run_times->push_back(base::TimeTicks::Now());
-}
-} // namespace
-
-// Verified that tasks posted with TaskType::kJavascriptTimer run at the
-// expected time when throttled.
+// Verify that tasks posted with TaskType::kJavascriptTimer run at the expected
+// time when throttled.
TEST_F(FrameSchedulerImplTest, ThrottledJSTimerTasksRunTime) {
// Snap the time to a multiple of 1 second. Otherwise, the exact run time
- // of throttled tasks after hidding the page will vary.
- const base::TimeTicks start_time = base::TimeTicks::Now();
- const base::TimeTicks aligned_start_time = start_time.SnappedToNextTick(
- base::TimeTicks(), base::TimeDelta::FromSeconds(1));
- if (aligned_start_time != start_time)
- task_environment_.FastForwardBy(aligned_start_time - start_time);
+ // of throttled tasks after hiding the page will vary.
+ FastForwardToAlignedTime(base::TimeDelta::FromSeconds(1));
const base::TimeTicks start = base::TimeTicks::Now();
// Hide the page to start throttling JS Timers.
@@ -2366,6 +2631,737 @@ TEST_F(FrameSchedulerImplTest, ThrottledJSTimerTasksRunTime) {
start + base::TimeDelta::FromMilliseconds(6000)));
}
+// Verify that tasks run at the expected time in frame that is same-origin with
+// the main frame with intensive wake up throttling.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
+ TaskExecutionSameOriginFrame) {
+ ASSERT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
+
+ // Throttled TaskRunner to which tasks are posted in this test.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Snap the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
+ // tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+ const base::TimeTicks test_start = base::TimeTicks::Now();
+
+ // Hide the page. This starts the delay to throttle background wake ups.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+
+ // Initially, wake ups are not throttled.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start);
+ std::vector<base::TimeTicks> run_times;
+
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kDefaultThrottledWakeUpInterval);
+ }
+
+ task_environment_.FastForwardBy(kGracePeriod);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromSeconds(0),
+ scope_start + base::TimeDelta::FromSeconds(1),
+ scope_start + base::TimeDelta::FromSeconds(2),
+ scope_start + base::TimeDelta::FromSeconds(3),
+ scope_start + base::TimeDelta::FromSeconds(4)));
+ }
+
+ // After |kGracePeriod|, a wake up can occur
+ // |kIntensiveThrottlingDurationBetweenWakeUps| after the last wake up, or at
+ // a time aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+
+ // Test waking up |kIntensiveThrottlingDurationBetweenWakeUps| after the last
+ // wake up.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5));
+ std::vector<base::TimeTicks> run_times;
+
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromSeconds(1)));
+ }
+
+ // Test waking up at a time aligned on
+ // ||kIntensiveThrottlingDurationBetweenWakeUps|.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5) +
+ base::TimeDelta::FromSeconds(1));
+ std::vector<base::TimeTicks> run_times;
+
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ (i + 1) * kDefaultThrottledWakeUpInterval);
+ }
+
+ // // All tasks should run at the next aligned time.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromSeconds(59),
+ scope_start + base::TimeDelta::FromSeconds(59),
+ scope_start + base::TimeDelta::FromSeconds(59),
+ scope_start + base::TimeDelta::FromSeconds(59),
+ scope_start + base::TimeDelta::FromSeconds(59)));
+ }
+
+ // Post an extra task with a short delay. It should run at the next time
+ // aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(6));
+ std::vector<base::TimeTicks> run_times;
+
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromMinutes(1)));
+ }
+
+ // Post an extra task with a delay that is longer than
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. The task should run at its
+ // desired run time, even if it's not aligned on
+ // |kIntensiveThrottlingDurationBetweenWakeUps|.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(7));
+ std::vector<base::TimeTicks> run_times;
+
+ const base::TimeDelta kLongDelay =
+ kIntensiveThrottlingDurationBetweenWakeUps * 5 +
+ kDefaultThrottledWakeUpInterval;
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times), kLongDelay);
+
+ task_environment_.FastForwardBy(kLongDelay);
+ EXPECT_THAT(run_times, testing::ElementsAre(scope_start + kLongDelay));
+ }
+
+ // Post tasks with short delays after the page communicated with the user in
+ // background. They should run aligned on 1-second interval for 5 seconds.
+ // After that, intensive throttling is applied again.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(12) +
+ kDefaultThrottledWakeUpInterval);
+ std::vector<base::TimeTicks> run_times;
+
+ page_scheduler_->OnTitleOrFaviconUpdated();
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ RecordRunTime(&run_times);
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval * (i + 1));
+ }
+ page_scheduler_->OnTitleOrFaviconUpdated();
+ }),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromSeconds(1),
+ scope_start + base::TimeDelta::FromSeconds(2),
+ scope_start + base::TimeDelta::FromSeconds(3),
+ scope_start - kDefaultThrottledWakeUpInterval +
+ base::TimeDelta::FromMinutes(1),
+ scope_start - kDefaultThrottledWakeUpInterval +
+ base::TimeDelta::FromMinutes(1),
+ scope_start - kDefaultThrottledWakeUpInterval +
+ base::TimeDelta::FromMinutes(1)));
+ }
+}
+
+// Verify that tasks run at the expected time in a frame that is cross-origin
+// with the main frame with intensive wake up throttling.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
+ TaskExecutionCrossOriginFrame) {
+ frame_scheduler_->SetCrossOriginToMainFrame(true);
+
+ // Throttled TaskRunner to which tasks are posted in this test.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Snap the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
+ // tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+ const base::TimeTicks test_start = base::TimeTicks::Now();
+
+ // Hide the page. This starts the delay to throttle background wake ups.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+
+ // Initially, wake ups are not throttled.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start);
+ std::vector<base::TimeTicks> run_times;
+
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kDefaultThrottledWakeUpInterval);
+ }
+
+ task_environment_.FastForwardBy(kGracePeriod);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromSeconds(0),
+ scope_start + base::TimeDelta::FromSeconds(1),
+ scope_start + base::TimeDelta::FromSeconds(2),
+ scope_start + base::TimeDelta::FromSeconds(3),
+ scope_start + base::TimeDelta::FromSeconds(4)));
+ }
+
+ // After |kGracePeriod|, a wake up can occur aligned on
+ // |kIntensiveThrottlingDurationBetweenWakeUps| only.
+
+ // Test posting a first task. It should run at the next aligned time (in a
+ // main frame, it would have run kIntensiveThrottlingDurationBetweenWakeUps
+ // after the last wake up).
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(5));
+ std::vector<base::TimeTicks> run_times;
+
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromMinutes(1)));
+ }
+
+ // Test posting many tasks with short delays. They should all run on the next
+ // time aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(6));
+ std::vector<base::TimeTicks> run_times;
+
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ (i + 1) * kDefaultThrottledWakeUpInterval);
+ }
+
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromMinutes(1),
+ scope_start + base::TimeDelta::FromMinutes(1),
+ scope_start + base::TimeDelta::FromMinutes(1),
+ scope_start + base::TimeDelta::FromMinutes(1),
+ scope_start + base::TimeDelta::FromMinutes(1)));
+ }
+
+ // Post an extra task with a short delay. It should run at the next time
+ // aligned on |kIntensiveThrottlingDurationBetweenWakeUps|.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(7));
+ std::vector<base::TimeTicks> run_times;
+
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromMinutes(1)));
+ }
+
+ // Post an extra task with a delay that is longer than
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. The task should run at an
+ // aligned time (in a main frame, it would have run at is desired unaligned
+ // run time).
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(8));
+ std::vector<base::TimeTicks> run_times;
+
+ const base::TimeDelta kLongDelay =
+ kIntensiveThrottlingDurationBetweenWakeUps * 5 +
+ base::TimeDelta::FromSeconds(1);
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times), kLongDelay);
+
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start +
+ kIntensiveThrottlingDurationBetweenWakeUps * 6));
+ }
+
+ // Post tasks with short delays after the page communicated with the user in
+ // background. They should run at an aligned time, since cross-origin
+ // frames are not affected by title or favicon update.
+ {
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ EXPECT_EQ(scope_start, test_start + base::TimeDelta::FromMinutes(14));
+ std::vector<base::TimeTicks> run_times;
+
+ page_scheduler_->OnTitleOrFaviconUpdated();
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ RecordRunTime(&run_times);
+ for (int i = 0; i < kNumTasks; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval * (i + 1));
+ }
+ page_scheduler_->OnTitleOrFaviconUpdated();
+ }),
+ kDefaultThrottledWakeUpInterval);
+
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times, testing::ElementsAre(
+ scope_start + base::TimeDelta::FromMinutes(1),
+ scope_start + base::TimeDelta::FromMinutes(2),
+ scope_start + base::TimeDelta::FromMinutes(2),
+ scope_start + base::TimeDelta::FromMinutes(2),
+ scope_start + base::TimeDelta::FromMinutes(2),
+ scope_start + base::TimeDelta::FromMinutes(2)));
+ }
+}
+
+// Verify that tasks from different frames that are same-origin with the main
+// frame run at the expected time.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
+ ManySameFrameOriginFrames) {
+ ASSERT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Create a FrameScheduler that is same-origin with the main frame, and an
+ // associated throttled TaskRunner.
+ std::unique_ptr<FrameSchedulerImpl> other_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kSubframe);
+ ASSERT_FALSE(other_frame_scheduler->IsCrossOriginToMainFrame());
+ const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
+ other_frame_scheduler->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Snap the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
+ // tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ // Hide the page and wait until the intensive throttling grace period has
+ // elapsed.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+ task_environment_.FastForwardBy(kGracePeriod);
+
+ // Post tasks in both frames, with delays shorter than the wake up interval.
+ int counter = 0;
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&IncrementCounter, base::Unretained(&counter)),
+ kDefaultThrottledWakeUpInterval);
+ int other_counter = 0;
+ other_task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter, base::Unretained(&other_counter)),
+ 2 * kDefaultThrottledWakeUpInterval);
+
+ // The first task should run at an unaligned time, because no wake up occurred
+ // in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
+ EXPECT_EQ(0, counter);
+ task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
+ EXPECT_EQ(1, counter);
+
+ // The second task must run at an aligned time.
+ constexpr base::TimeDelta kEpsilon = base::TimeDelta::FromMicroseconds(1);
+ EXPECT_EQ(0, other_counter);
+ task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
+ EXPECT_EQ(0, other_counter);
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps -
+ 2 * kDefaultThrottledWakeUpInterval -
+ kEpsilon);
+ EXPECT_EQ(0, other_counter);
+ task_environment_.FastForwardBy(kEpsilon);
+ EXPECT_EQ(1, other_counter);
+}
+
+// Verify that intensive throttling is disabled when there is an opt-out for all
+// throttling.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling, ThrottlingOptOut) {
+ constexpr int kNumTasks = 3;
+ // |task_runner| is throttled.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+ // |other_task_runner| is throttled. It belongs to a different frame on the
+ // same page.
+ const auto other_frame_scheduler = CreateFrameScheduler(
+ page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kSubframe);
+ const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Fast-forward the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
+ // the time at which tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ // Hide the page and wait until the intensive throttling grace period has
+ // elapsed.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+ task_environment_.FastForwardBy(kGracePeriod);
+
+ {
+ // Wake ups are intensively throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval + i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ // Note: Intensive throttling does not apply when there hasn't been a wake
+ // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+ }
+
+ {
+ // Create an opt-out.
+ auto handle = frame_scheduler_->RegisterFeature(
+ SchedulingPolicy::Feature::kWebRTC,
+ {SchedulingPolicy::DisableAllThrottling()});
+
+ {
+ // A task should run every |kShortDelay|, since there is an opt-out for
+ // all types of throttling.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(scope_start + kShortDelay * 1,
+ scope_start + kShortDelay * 2,
+ scope_start + kShortDelay * 3));
+ }
+
+ {
+ // Same thing for another frame on the same page.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ other_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(scope_start + kShortDelay * 1,
+ scope_start + kShortDelay * 2,
+ scope_start + kShortDelay * 3));
+ }
+ }
+
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ {
+ // Wake ups are intensively throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval + i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ // Note: Intensive throttling does not apply when there hasn't been a wake
+ // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+ }
+}
+
+// Verify that intensive throttling is disabled when there is an opt-out for
+// aggressive throttling.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
+ AggressiveThrottlingOptOut) {
+ constexpr int kNumTasks = 3;
+ // |task_runner| is throttled.
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+ // |other_task_runner| is throttled. It belongs to a different frame on the
+ // same page.
+ const auto other_frame_scheduler = CreateFrameScheduler(
+ page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kSubframe);
+ const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Fast-forward the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
+ // the time at which tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ // Hide the page and wait until the intensive throttling grace period has
+ // elapsed.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+ task_environment_.FastForwardBy(kGracePeriod);
+
+ {
+ // Wake ups are intensively throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval + i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ // Note: Intensive throttling does not apply when there hasn't been a wake
+ // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+ }
+
+ {
+ // Create an opt-out.
+ auto handle = frame_scheduler_->RegisterFeature(
+ SchedulingPolicy::Feature::kWebRTC,
+ {SchedulingPolicy::DisableAggressiveThrottling()});
+
+ {
+ // Tasks should run after |kDefaultThrottledWakeUpInterval|, since
+ // aggressive throttling is disabled, but default wake up throttling
+ // remains enabled.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(
+ run_times,
+ testing::ElementsAre(scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval));
+ }
+
+ {
+ // Same thing for another frame on the same page.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ other_task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ EXPECT_THAT(
+ run_times,
+ testing::ElementsAre(scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval));
+ }
+ }
+
+ // Fast-forward so that there is no recent wake up. Then, align the time on
+ // |kIntensiveThrottlingDurationBetweenWakeUps| to simplify expectations.
+ task_environment_.FastForwardBy(kIntensiveThrottlingDurationBetweenWakeUps);
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ {
+ // Wake ups are intensively throttled, since there is no throttling opt-out.
+ const base::TimeTicks scope_start = base::TimeTicks::Now();
+ std::vector<base::TimeTicks> run_times;
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(FROM_HERE,
+ base::BindOnce(&RecordRunTime, &run_times),
+ i * kShortDelay);
+ }
+ for (int i = 1; i < kNumTasks + 1; ++i) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&RecordRunTime, &run_times),
+ kDefaultThrottledWakeUpInterval + i * kShortDelay);
+ }
+ task_environment_.FastForwardUntilNoTasksRemain();
+ // Note: Intensive throttling does not apply when there hasn't been a wake
+ // up in the last |kIntensiveThrottlingDurationBetweenWakeUps|.
+ EXPECT_THAT(run_times,
+ testing::ElementsAre(
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kDefaultThrottledWakeUpInterval,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps,
+ scope_start + kIntensiveThrottlingDurationBetweenWakeUps));
+ }
+}
+
+// Verify that tasks run at the same time when a frame switches between being
+// same-origin and cross-origin with the main frame.
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottling,
+ FrameChangesOriginType) {
+ EXPECT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
+ const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+ frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Create a new FrameScheduler that remains cross-origin with the main frame
+ // throughout the test.
+ std::unique_ptr<FrameSchedulerImpl> cross_origin_frame_scheduler =
+ CreateFrameScheduler(page_scheduler_.get(),
+ frame_scheduler_delegate_.get(), nullptr,
+ FrameScheduler::FrameType::kSubframe);
+ cross_origin_frame_scheduler->SetCrossOriginToMainFrame(true);
+ const scoped_refptr<base::SingleThreadTaskRunner> cross_origin_task_runner =
+ cross_origin_frame_scheduler->GetTaskRunner(TaskType::kJavascriptTimer);
+
+ // Snap the time to a multiple of
+ // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
+ // tasks can run after throttling is enabled will vary.
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+
+ // Hide the page and wait until the intensive throttling grace period has
+ // elapsed.
+ EXPECT_TRUE(page_scheduler_->IsPageVisible());
+ page_scheduler_->SetPageVisible(false);
+ task_environment_.FastForwardBy(kGracePeriod);
+
+ {
+ // Post delayed tasks with short delays to both frames. The
+ // main-frame-origin task can run at the desired time, because no wake up
+ // occurred in the last |kIntensiveThrottlingDurationBetweenWakeUps|. The
+ // cross-origin task must run at an aligned time.
+ int counter = 0;
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter, base::Unretained(&counter)),
+ kDefaultThrottledWakeUpInterval);
+ int cross_origin_counter = 0;
+ cross_origin_task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter,
+ base::Unretained(&cross_origin_counter)),
+ kDefaultThrottledWakeUpInterval);
+
+ // Make the |frame_scheduler_| cross-origin. Its task must now run at an
+ // aligned time.
+ frame_scheduler_->SetCrossOriginToMainFrame(true);
+ task_environment_.FastForwardBy(kDefaultThrottledWakeUpInterval);
+ EXPECT_EQ(0, counter);
+ EXPECT_EQ(0, cross_origin_counter);
+
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_EQ(1, counter);
+ EXPECT_EQ(1, cross_origin_counter);
+ }
+
+ {
+ // Post delayed tasks with long delays that aren't aligned with the wake up
+ // interval. They should run at aligned times, since they are cross-origin.
+ const base::TimeDelta kLongUnalignedDelay =
+ 5 * kIntensiveThrottlingDurationBetweenWakeUps +
+ kDefaultThrottledWakeUpInterval;
+ int counter = 0;
+ task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter, base::Unretained(&counter)),
+ kLongUnalignedDelay);
+ int cross_origin_counter = 0;
+ cross_origin_task_runner->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&IncrementCounter,
+ base::Unretained(&cross_origin_counter)),
+ kLongUnalignedDelay);
+
+ // Make the |frame_scheduler_| same-origin. Its task can now run at an
+ // unaligned time.
+ frame_scheduler_->SetCrossOriginToMainFrame(false);
+ task_environment_.FastForwardBy(kLongUnalignedDelay);
+ EXPECT_EQ(1, counter);
+ EXPECT_EQ(0, cross_origin_counter);
+
+ FastForwardToAlignedTime(kIntensiveThrottlingDurationBetweenWakeUps);
+ EXPECT_EQ(1, counter);
+ EXPECT_EQ(1, cross_origin_counter);
+ }
+}
+
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
+ PolicyForceEnable) {
+ SetPolicyOverride(/* enabled = */ true);
+ EXPECT_TRUE(IsIntensiveWakeUpThrottlingEnabled());
+
+ // The parameters should be the defaults, even though they were changed by the
+ // ScopedFeatureList.
+ EXPECT_EQ(base::TimeDelta::FromSeconds(
+ kIntensiveWakeUpThrottling_GracePeriodSeconds_Default),
+ GetIntensiveWakeUpThrottlingGracePeriod());
+ EXPECT_EQ(
+ base::TimeDelta::FromSeconds(
+ kIntensiveWakeUpThrottling_DurationBetweenWakeUpsSeconds_Default),
+ GetIntensiveWakeUpThrottlingDurationBetweenWakeUps());
+}
+
+TEST_F(FrameSchedulerImplTestWithIntensiveWakeUpThrottlingPolicyOverride,
+ PolicyForceDisable) {
+ SetPolicyOverride(/* enabled = */ false);
+ EXPECT_FALSE(IsIntensiveWakeUpThrottlingEnabled());
+}
+
} // namespace frame_scheduler_impl_unittest
} // namespace scheduler
} // namespace blink
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 28f6af4d89a..62467a7ad0b 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
@@ -32,7 +32,7 @@ namespace blink {
namespace scheduler {
class FrameTaskQueueControllerTest : public testing::Test,
- FrameTaskQueueController::Delegate {
+ public FrameTaskQueueController::Delegate {
public:
FrameTaskQueueControllerTest()
: task_environment_(
@@ -43,17 +43,17 @@ class FrameTaskQueueControllerTest : public testing::Test,
~FrameTaskQueueControllerTest() override = default;
void SetUp() override {
- scheduler_.reset(new MainThreadSchedulerImpl(
+ scheduler_ = std::make_unique<MainThreadSchedulerImpl>(
base::sequence_manager::SequenceManagerForTest::Create(
nullptr, task_environment_.GetMainThreadTaskRunner(),
task_environment_.GetMockTickClock()),
- base::nullopt));
- page_scheduler_.reset(new PageSchedulerImpl(nullptr, scheduler_.get()));
- frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
- frame_task_queue_controller_.reset(new FrameTaskQueueController(
- scheduler_.get(), frame_scheduler_.get(), this));
+ base::nullopt);
+ page_scheduler_ = scheduler_->CreatePageScheduler(nullptr);
+ frame_scheduler_ = page_scheduler_->CreateFrameScheduler(
+ nullptr, nullptr, FrameScheduler::FrameType::kSubframe);
+ frame_task_queue_controller_ = std::make_unique<FrameTaskQueueController>(
+ scheduler_.get(),
+ static_cast<FrameSchedulerImpl*>(frame_scheduler_.get()), this);
}
void TearDown() override {
@@ -113,8 +113,8 @@ class FrameTaskQueueControllerTest : public testing::Test,
protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
- std::unique_ptr<PageSchedulerImpl> page_scheduler_;
- std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+ std::unique_ptr<PageScheduler> page_scheduler_;
+ std::unique_ptr<FrameScheduler> frame_scheduler_;
std::unique_ptr<FrameTaskQueueController> frame_task_queue_controller_;
private:
@@ -329,7 +329,8 @@ INSTANTIATE_TEST_SUITE_P(
QueueTraits::PrioritisationType::kLoading,
QueueTraits::PrioritisationType::kLoadingControl,
QueueTraits::PrioritisationType::kFindInPage,
- QueueTraits::PrioritisationType::kExperimentalDatabase));
+ QueueTraits::PrioritisationType::kExperimentalDatabase,
+ QueueTraits::PrioritisationType::kJavaScriptTimer));
TEST_P(TaskQueueCreationFromQueueTraitsTest,
AddAndRetrieveAllTaskQueues) {
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 7cf04501004..7ea66444c46 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
@@ -34,6 +34,7 @@
#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"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.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/frame_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread.h"
@@ -41,6 +42,7 @@
#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 "third_party/blink/renderer/platform/wtf/functional.h"
#include "v8/include/v8.h"
namespace blink {
@@ -60,27 +62,11 @@ constexpr base::TimeDelta kQueueingTimeWindowDuration =
base::TimeDelta::FromSeconds(1);
const int64_t kSecondsPerMinute = 60;
-// Wake-up throttling trial.
-const char kWakeUpThrottlingTrial[] = "RendererSchedulerWakeUpThrottling";
-const char kWakeUpDurationParam[] = "wake_up_duration_ms";
-
-constexpr base::TimeDelta kDefaultWakeUpDuration =
- base::TimeDelta::FromMilliseconds(3);
-
// Name of the finch study that enables using resource fetch priorities to
// schedule tasks on Blink.
constexpr const char kResourceFetchPriorityExperiment[] =
"ResourceFetchPriorityExperiment";
-base::TimeDelta GetWakeUpDuration() {
- int duration_ms;
- if (!base::StringToInt(base::GetFieldTrialParamValue(kWakeUpThrottlingTrial,
- kWakeUpDurationParam),
- &duration_ms))
- return kDefaultWakeUpDuration;
- return base::TimeDelta::FromMilliseconds(duration_ms);
-}
-
v8::RAILMode RAILModeToV8RAILMode(RAILMode rail_mode) {
switch (rail_mode) {
case RAILMode::kResponse:
@@ -237,7 +223,8 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl(
helper_.GetClock(),
helper_.NowTicks()),
any_thread_(this),
- policy_may_need_update_(&any_thread_lock_) {
+ policy_may_need_update_(&any_thread_lock_),
+ notify_agent_strategy_task_posted_(&any_thread_lock_) {
// Compositor task queue and default task queue should be managed by
// WebThreadScheduler. Control task queue should not.
task_runners_.emplace(helper_.DefaultMainThreadTaskQueue(), nullptr);
@@ -278,6 +265,12 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl(
&MainThreadSchedulerImpl::UpdatePolicy, weak_factory_.GetWeakPtr());
end_renderer_hidden_idle_period_closure_.Reset(base::BindRepeating(
&MainThreadSchedulerImpl::EndIdlePeriod, weak_factory_.GetWeakPtr()));
+ notify_agent_strategy_on_input_event_closure_ = base::BindRepeating(
+ &MainThreadSchedulerImpl::NotifyAgentSchedulerOnInputEvent,
+ weak_factory_.GetWeakPtr());
+ agent_strategy_delay_callback_ =
+ base::BindRepeating(&MainThreadSchedulerImpl::OnAgentStrategyDelayPassed,
+ weak_factory_.GetWeakPtr());
TRACE_EVENT_OBJECT_CREATED_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "MainThreadScheduler",
@@ -449,7 +442,6 @@ MainThreadSchedulerImpl::MainThreadOnly::MainThreadOnly(
&main_thread_scheduler_impl->tracing_controller_,
YesNoStateToString),
background_status_changed_at(now),
- wake_up_budget_pool(nullptr),
metrics_helper(
main_thread_scheduler_impl,
main_thread_scheduler_impl->helper_.HasCPUTimingForEachTask(),
@@ -585,6 +577,9 @@ MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() {
base::FeatureList::IsEnabled(
kPrioritizeCompositingAndLoadingDuringEarlyLoading);
+ prioritize_compositing_after_input =
+ base::FeatureList::IsEnabled(kPrioritizeCompositingAfterInput);
+
if (use_resource_fetch_priority ||
use_resource_priorities_only_during_loading) {
base::FieldTrialParams params;
@@ -758,9 +753,6 @@ scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::NewTaskQueue(
task_queue->SetQueuePriority(ComputePriority(task_queue.get()));
- if (task_queue->CanBeThrottled())
- AddQueueToWakeUpBudgetPool(task_queue.get());
-
// If this is a timer queue, and virtual time is enabled and paused, it should
// be suspended by adding a fence to prevent immediate tasks from running when
// they're not supposed to.
@@ -1018,7 +1010,8 @@ void MainThreadSchedulerImpl::SetSchedulerKeepActive(bool keep_active) {
}
void MainThreadSchedulerImpl::OnMainFrameRequestedForInput() {
- SetPrioritizeCompositingAfterInput(true);
+ SetPrioritizeCompositingAfterInput(
+ scheduling_settings().prioritize_compositing_after_input);
}
bool MainThreadSchedulerImpl::SchedulerKeepActive() {
@@ -1266,6 +1259,16 @@ void MainThreadSchedulerImpl::UpdateForInputEventOnCompositorThread(
break;
}
+ // Make sure the per-agent scheduling strategy is notified that there was an
+ // input event.
+ if (type != WebInputEvent::Type::kMouseMove &&
+ !notify_agent_strategy_task_posted_.IsSet() &&
+ agent_scheduling_strategy_->ShouldNotifyOnInputEvent()) {
+ notify_agent_strategy_task_posted_.SetWhileLocked(true);
+ control_task_queue_->task_runner()->PostTask(
+ FROM_HERE, notify_agent_strategy_on_input_event_closure_);
+ }
+
// Avoid unnecessary policy updates if the use case did not change.
UseCase use_case = ComputeCurrentUseCase(now, &unused_policy_duration);
@@ -1277,6 +1280,24 @@ void MainThreadSchedulerImpl::UpdateForInputEventOnCompositorThread(
GetCompositorThreadOnly().last_input_type = type;
}
+void MainThreadSchedulerImpl::NotifyAgentSchedulerOnInputEvent() {
+ helper_.CheckOnValidThread();
+
+ if (agent_scheduling_strategy_->OnInputEvent() ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes &&
+ !policy_may_need_update_.IsSet()) {
+ // MaybeUpdatePolicy() triggers a |kMayEarlyOutIfPolicyUnchanged| update,
+ // which may not account for per-agent strategy decisions correctly.
+ // However, if there is already a posted task to update it, it means that
+ // the use-case has changed, so it is OK to not trigger another update from
+ // here.
+ ForceUpdatePolicy();
+ }
+
+ base::AutoLock lock(any_thread_lock_);
+ notify_agent_strategy_task_posted_.SetWhileLocked(false);
+}
+
void MainThreadSchedulerImpl::WillPostInputEventToMainThread(
WebInputEvent::Type web_input_event_type,
const WebInputEventAttribution& web_input_event_attribution) {
@@ -1569,6 +1590,8 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
CreateTraceEventObjectSnapshotLocked();
// TODO(alexclarke): Can we get rid of force update now?
+ // talp: Can't get rid of this, as per-agent scheduling happens on top of the
+ // policy, based on agent states.
if (update_type == UpdateType::kMayEarlyOutIfPolicyUnchanged &&
new_policy == main_thread_only().current_policy) {
return;
@@ -1607,7 +1630,12 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) {
Policy old_policy = main_thread_only().current_policy;
main_thread_only().current_policy = new_policy;
- if (ShouldUpdateTaskQueuePriorities(old_policy)) {
+ // TODO(talp): Extract the code updating queue policies/priorities to a
+ // separate method that can be called directly without having to recalculate
+ // the policy. Then revert the condition here to only check
+ // ShouldUpdateTaskQueuePriorities.
+ if (update_type == UpdateType::kForceUpdate ||
+ ShouldUpdateTaskQueuePriorities(old_policy)) {
for (const auto& pair : task_runners_) {
MainThreadTaskQueue* task_queue = pair.first.get();
task_queue->SetQueuePriority(ComputePriority(task_queue));
@@ -1623,7 +1651,11 @@ void MainThreadSchedulerImpl::ApplyTaskQueuePolicy(
DCHECK(old_task_queue_policy.IsQueueEnabled(task_queue) ||
task_queue_enabled_voter);
if (task_queue_enabled_voter) {
+ bool is_enabled_for_agent =
+ agent_scheduling_strategy_->QueueEnabledState(*task_queue)
+ .value_or(true);
task_queue_enabled_voter->SetVoteToEnable(
+ is_enabled_for_agent &&
new_task_queue_policy.IsQueueEnabled(task_queue));
}
@@ -1753,11 +1785,6 @@ IdleTimeEstimator* MainThreadSchedulerImpl::GetIdleTimeEstimatorForTesting() {
return &main_thread_only().idle_time_estimator;
}
-WakeUpBudgetPool* MainThreadSchedulerImpl::GetWakeUpBudgetPoolForTesting() {
- InitWakeUpBudgetPoolIfNeeded();
- return main_thread_only().wake_up_budget_pool;
-}
-
base::TimeTicks MainThreadSchedulerImpl::EnableVirtualTime() {
return EnableVirtualTime(main_thread_only().initial_virtual_time.is_null()
? BaseTimeOverridePolicy::DO_NOT_OVERRIDE
@@ -2232,7 +2259,7 @@ void MainThreadSchedulerImpl::DidCommitProvisionalLoad(
}
}
-void MainThreadSchedulerImpl::OnMainFramePaint() {
+void MainThreadSchedulerImpl::OnMainFramePaint(bool force_policy_update) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
"MainThreadSchedulerImpl::OnMainFramePaint");
base::AutoLock lock(any_thread_lock_);
@@ -2240,7 +2267,39 @@ void MainThreadSchedulerImpl::OnMainFramePaint() {
IsAnyMainFrameWaitingForFirstContentfulPaint();
any_thread().waiting_for_any_main_frame_meaningful_paint =
IsAnyMainFrameWaitingForFirstMeaningfulPaint();
- UpdatePolicyLocked(UpdateType::kMayEarlyOutIfPolicyUnchanged);
+ UpdatePolicyLocked(force_policy_update
+ ? UpdateType::kForceUpdate
+ : UpdateType::kMayEarlyOutIfPolicyUnchanged);
+}
+
+void MainThreadSchedulerImpl::OnMainFrameLoad(
+ const FrameSchedulerImpl& frame_scheduler) {
+ helper_.CheckOnValidThread();
+ if (agent_scheduling_strategy_->OnMainFrameLoad(frame_scheduler) ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
+ ForceUpdatePolicy();
+ };
+}
+
+void MainThreadSchedulerImpl::OnSetTimer(
+ const FrameSchedulerImpl& frame_scheduler,
+ base::TimeDelta delay) {
+ helper_.CheckOnValidThread();
+ control_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(agent_strategy_delay_callback_,
+ frame_scheduler.GetWeakPtr()),
+ delay);
+}
+
+void MainThreadSchedulerImpl::OnAgentStrategyDelayPassed(
+ base::WeakPtr<const FrameSchedulerImpl> frame_scheduler) {
+ helper_.CheckOnValidThread();
+ if (frame_scheduler &&
+ agent_scheduling_strategy_->OnDelayPassed(*frame_scheduler) ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
+ ForceUpdatePolicy();
+ }
}
void MainThreadSchedulerImpl::ResetForNavigationLocked() {
@@ -2368,7 +2427,9 @@ MainThreadSchedulerImpl::NonWakingTaskRunner() {
std::unique_ptr<PageScheduler> MainThreadSchedulerImpl::CreatePageScheduler(
PageScheduler::Delegate* delegate) {
- return std::make_unique<PageSchedulerImpl>(delegate, this);
+ auto page_scheduler = std::make_unique<PageSchedulerImpl>(delegate, this);
+ AddPageScheduler(page_scheduler.get());
+ return page_scheduler;
}
std::unique_ptr<ThreadScheduler::RendererPauseHandle>
@@ -2431,6 +2492,22 @@ void MainThreadSchedulerImpl::RemovePageScheduler(
IsAnyMainFrameWaitingForFirstMeaningfulPaint();
}
+void MainThreadSchedulerImpl::OnFrameAdded(
+ const FrameSchedulerImpl& frame_scheduler) {
+ if (agent_scheduling_strategy_->OnFrameAdded(frame_scheduler) ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
+ ForceUpdatePolicy();
+ }
+}
+
+void MainThreadSchedulerImpl::OnFrameRemoved(
+ const FrameSchedulerImpl& frame_scheduler) {
+ if (agent_scheduling_strategy_->OnFrameRemoved(frame_scheduler) ==
+ AgentSchedulingStrategy::ShouldUpdatePolicy::kYes) {
+ ForceUpdatePolicy();
+ }
+}
+
void MainThreadSchedulerImpl::OnPageFrozen() {
memory_purge_manager_.OnPageFrozen();
}
@@ -2703,18 +2780,6 @@ void MainThreadSchedulerImpl::OnQueueingTimeForWindowEstimated(
if (!is_disjoint_window || !ContainsLocalMainFrame())
return;
- UMA_HISTOGRAM_TIMES("RendererScheduler.ExpectedTaskQueueingDuration",
- queueing_time);
- UMA_HISTOGRAM_CUSTOM_COUNTS("RendererScheduler.ExpectedTaskQueueingDuration3",
- base::saturated_cast<base::HistogramBase::Sample>(
- queueing_time.InMicroseconds()),
- kMinExpectedQueueingTimeBucket,
- kMaxExpectedQueueingTimeBucket,
- kNumberExpectedQueueingTimeBuckets);
- TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
- "estimated_queueing_time_for_window",
- queueing_time.InMillisecondsF());
-
if (auto* renderer_resource_coordinator =
RendererResourceCoordinator::Get()) {
renderer_resource_coordinator->SetExpectedTaskQueueingDuration(
@@ -2727,24 +2792,6 @@ MainThreadSchedulerImpl::GetVirtualTimeDomain() {
return virtual_time_domain_.get();
}
-void MainThreadSchedulerImpl::AddQueueToWakeUpBudgetPool(
- MainThreadTaskQueue* queue) {
- InitWakeUpBudgetPoolIfNeeded();
- main_thread_only().wake_up_budget_pool->AddQueue(tick_clock()->NowTicks(),
- queue);
-}
-
-void MainThreadSchedulerImpl::InitWakeUpBudgetPoolIfNeeded() {
- if (main_thread_only().wake_up_budget_pool)
- return;
-
- main_thread_only().wake_up_budget_pool =
- task_queue_throttler()->CreateWakeUpBudgetPool("renderer_wake_up_pool");
- main_thread_only().wake_up_budget_pool->SetWakeUpRate(1);
- main_thread_only().wake_up_budget_pool->SetWakeUpDuration(
- GetWakeUpDuration());
-}
-
TimeDomain* MainThreadSchedulerImpl::GetActiveTimeDomain() {
if (main_thread_only().use_virtual_time) {
return GetVirtualTimeDomain();
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 bfac145a439..5275ee6439b 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
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/platform/scheduler/common/thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_interference_recorder.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/agent_scheduling_strategy.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/compositor_priority_experiments.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h"
@@ -69,15 +70,18 @@ class FrameSchedulerImplTest;
namespace main_thread_scheduler_impl_unittest {
class MainThreadSchedulerImplForTest;
class MainThreadSchedulerImplTest;
+class MockPageSchedulerImpl;
FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest, ShouldIgnoreTaskForUkm);
FORWARD_DECLARE_TEST(MainThreadSchedulerImplTest, Tracing);
} // namespace main_thread_scheduler_impl_unittest
+class FrameSchedulerImpl;
class PageSchedulerImpl;
class TaskQueueThrottler;
class WebRenderWidgetSchedulingState;
class PLATFORM_EXPORT MainThreadSchedulerImpl
: public ThreadSchedulerImpl,
+ public AgentSchedulingStrategy::Delegate,
public IdleHelper::Delegate,
public MainThreadSchedulerHelper::Observer,
public RenderWidgetSignals::Observer,
@@ -131,6 +135,9 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
// (crbug.com/971191)
bool prioritize_compositing_and_loading_during_early_loading;
+ // Prioritise one BeginMainFrame after an input task.
+ bool prioritize_compositing_after_input;
+
// Contains a mapping from net::RequestPriority to TaskQueue::QueuePriority
// when use_resource_fetch_priority is enabled.
std::array<base::sequence_manager::TaskQueue::QueuePriority,
@@ -313,9 +320,15 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
void DecrementVirtualTimePauseCount();
void MaybeAdvanceVirtualTime(base::TimeTicks new_virtual_time);
- void AddPageScheduler(PageSchedulerImpl*);
void RemovePageScheduler(PageSchedulerImpl*);
+ void OnFrameAdded(const FrameSchedulerImpl& frame_scheduler);
+ void OnFrameRemoved(const FrameSchedulerImpl& frame_scheduler);
+
+ AgentSchedulingStrategy& agent_scheduling_strategy() {
+ return *agent_scheduling_strategy_;
+ }
+
// Called by an associated PageScheduler when frozen or resumed.
void OnPageFrozen();
void OnPageResumed();
@@ -353,7 +366,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
void EndIdlePeriodForTesting(base::OnceClosure callback,
base::TimeTicks time_remaining);
bool PolicyNeedsUpdateForTesting();
- WakeUpBudgetPool* GetWakeUpBudgetPoolForTesting();
const base::TickClock* tick_clock() const;
@@ -367,7 +379,8 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
return task_queue_throttler_.get();
}
- void OnMainFramePaint();
+ void OnMainFramePaint(bool force_policy_update);
+ void OnMainFrameLoad(const FrameSchedulerImpl& frame_scheduler);
void OnShutdownTaskQueue(const scoped_refptr<MainThreadTaskQueue>& queue);
@@ -454,6 +467,7 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
friend class frame_scheduler_impl_unittest::FrameSchedulerImplTest;
friend class main_thread_scheduler_impl_unittest::
MainThreadSchedulerImplForTest;
+ friend class main_thread_scheduler_impl_unittest::MockPageSchedulerImpl;
friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest;
friend class CompositorPriorityExperiments;
@@ -473,6 +487,8 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
static const char* TimeDomainTypeToString(TimeDomainType domain_type);
+ void AddPageScheduler(PageSchedulerImpl*);
+
bool ContainsLocalMainFrame();
bool IsAnyMainFrameWaitingForFirstContentfulPaint() const;
@@ -633,6 +649,10 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
MainThreadSchedulerImpl* scheduler_; // NOT OWNED
};
+ // AgentSchedulingStrategy::Delegate implementation:
+ void OnSetTimer(const FrameSchedulerImpl& frame_scheduler,
+ base::TimeDelta delay) override;
+
// IdleHelper::Delegate implementation:
bool CanEnterLongIdlePeriod(
base::TimeTicks now,
@@ -717,6 +737,10 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
void UpdateForInputEventOnCompositorThread(const WebInputEvent& event,
InputEventState input_event_state);
+ // Notifies the per-agent scheduling strategy that an input event occurred.
+ void NotifyAgentSchedulerOnInputEvent();
+ void OnAgentStrategyDelayPassed(base::WeakPtr<const FrameSchedulerImpl>);
+
// The task cost estimators and the UserModel need to be reset upon page
// nagigation. This function does that. Must be called from the main thread.
void ResetForNavigationLocked();
@@ -735,8 +759,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
const TaskQueuePolicy& old_task_queue_policy,
const TaskQueuePolicy& new_task_queue_policy) const;
- void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* queue);
-
void PauseRendererImpl();
void ResumeRendererImpl();
@@ -780,8 +802,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
FrameSchedulerImpl* frame_scheduler,
bool precise_attribution);
- void InitWakeUpBudgetPoolIfNeeded();
-
void SetNumberOfCompositingTasksToPrioritize(int number_of_tasks);
void ShutdownAllQueues();
@@ -854,13 +874,18 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
base::RepeatingClosure update_policy_closure_;
DeadlineTaskRunner delayed_update_policy_runner_;
CancelableClosureHolder end_renderer_hidden_idle_period_closure_;
+ base::RepeatingClosure notify_agent_strategy_on_input_event_closure_;
+ base::RepeatingCallback<void(base::WeakPtr<const FrameSchedulerImpl>)>
+ agent_strategy_delay_callback_;
QueueingTimeEstimator queueing_time_estimator_;
AgentInterferenceRecorder agent_interference_recorder_;
+ std::unique_ptr<AgentSchedulingStrategy> agent_scheduling_strategy_ =
+ AgentSchedulingStrategy::Create(*this);
+
// We have decided to improve thread safety at the cost of some boilerplate
// (the accessors) for the following data members.
-
struct MainThreadOnly {
MainThreadOnly(
MainThreadSchedulerImpl* main_thread_scheduler_impl,
@@ -909,7 +934,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
HashSet<PageSchedulerImpl*> page_schedulers; // Not owned.
base::ObserverList<RAILModeObserver>::Unchecked
rail_mode_observers; // Not owned.
- WakeUpBudgetPool* wake_up_budget_pool; // Not owned.
MainThreadMetricsHelper metrics_helper;
TraceableState<WebRendererProcessType, TracingCategoryName::kTopLevel>
process_type;
@@ -1041,6 +1065,7 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl
}
PollableThreadSafeFlag policy_may_need_update_;
+ PollableThreadSafeFlag notify_agent_strategy_task_posted_;
base::WeakPtrFactory<MainThreadSchedulerImpl> weak_factory_{this};
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 1a47b7a06ab..0a755f53717 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
@@ -12,6 +12,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/task/post_task.h"
@@ -23,10 +24,12 @@
#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 "base/time/time.h"
#include "build/build_config.h"
#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_input_event.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"
@@ -48,14 +51,67 @@ namespace scheduler {
// To avoid symbol collisions in jumbo builds.
namespace main_thread_scheduler_impl_unittest {
-using testing::InSequence;
-using testing::Mock;
-using testing::NiceMock;
-using testing::NotNull;
-using testing::Return;
+namespace {
+using ::base::Feature;
+using ::base::sequence_manager::FakeTask;
+using ::base::sequence_manager::FakeTaskTiming;
+using blink::WebInputEvent;
+using FeatureAndParams = ::base::test::ScopedFeatureList::FeatureAndParams;
+using ::testing::InSequence;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::ReturnRef;
using InputEventState = WebThreadScheduler::InputEventState;
-using base::sequence_manager::FakeTask;
-using base::sequence_manager::FakeTaskTiming;
+
+// This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
+// returns the PageScheduler as a PageSchedulerImpl.
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
+ PageScheduler::Delegate* page_scheduler_delegate,
+ ThreadSchedulerImpl* scheduler) {
+ std::unique_ptr<PageScheduler> page_scheduler =
+ scheduler->CreatePageScheduler(page_scheduler_delegate);
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+ static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+ return page_scheduler_impl;
+}
+
+// This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
+// returns the FrameScheduler as a FrameSchedulerImpl.
+std::unique_ptr<FrameSchedulerImpl> CreateFrameScheduler(
+ PageSchedulerImpl* page_scheduler,
+ FrameScheduler::Delegate* delegate,
+ blink::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ auto frame_scheduler =
+ page_scheduler->CreateFrameScheduler(delegate, blame_context, frame_type);
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_impl(
+ static_cast<FrameSchedulerImpl*>(frame_scheduler.release()));
+ return frame_scheduler_impl;
+}
+
+class MockFrameDelegate : public FrameScheduler::Delegate {
+ public:
+ MockFrameDelegate() {
+ ON_CALL(*this, GetAgentClusterId)
+ .WillByDefault(ReturnRef(agent_cluster_id_));
+ }
+
+ MOCK_METHOD(const base::UnguessableToken&,
+ GetAgentClusterId,
+ (),
+ (const, override));
+ MOCK_METHOD(ukm::UkmRecorder*, GetUkmRecorder, ());
+ MOCK_METHOD(ukm::SourceId, GetUkmSourceId, ());
+ MOCK_METHOD(void, UpdateTaskTime, (base::TimeDelta));
+ MOCK_METHOD(void, UpdateActiveSchedulerTrackedFeatures, (uint64_t));
+
+ private:
+ base::UnguessableToken agent_cluster_id_ = base::UnguessableToken::Create();
+};
+
+} // namespace
class FakeInputEvent : public blink::WebInputEvent {
public:
@@ -247,6 +303,10 @@ class MockPageSchedulerImpl : public PageSchedulerImpl {
public:
explicit MockPageSchedulerImpl(MainThreadSchedulerImpl* scheduler)
: PageSchedulerImpl(nullptr, scheduler) {
+ // This would normally be called by
+ // MainThreadSchedulerImpl::CreatePageScheduler.
+ scheduler->AddPageScheduler(this);
+
ON_CALL(*this, IsWaitingForMainFrameContentfulPaint)
.WillByDefault(Return(true));
ON_CALL(*this, IsWaitingForMainFrameMeaningfulPaint)
@@ -344,11 +404,16 @@ class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl {
class MainThreadSchedulerImplTest : public testing::Test {
public:
- MainThreadSchedulerImplTest(std::vector<base::Feature> features_to_enable,
- std::vector<base::Feature> features_to_disable) {
+ MainThreadSchedulerImplTest(std::vector<Feature> features_to_enable,
+ std::vector<Feature> features_to_disable) {
feature_list_.InitWithFeatures(features_to_enable, features_to_disable);
}
+ explicit MainThreadSchedulerImplTest(
+ std::vector<FeatureAndParams> features_to_enable) {
+ feature_list_.InitWithFeaturesAndParameters(features_to_enable, {});
+ }
+
MainThreadSchedulerImplTest() : MainThreadSchedulerImplTest({}, {}) {}
~MainThreadSchedulerImplTest() override = default;
@@ -392,8 +457,8 @@ class MainThreadSchedulerImplTest : public testing::Test {
page_scheduler_ =
std::make_unique<NiceMock<MockPageSchedulerImpl>>(scheduler_.get());
main_frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kMainFrame);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kMainFrame);
widget_scheduler_ = scheduler_->CreateWidgetScheduler();
input_task_runner_ = widget_scheduler_->InputTaskRunner();
@@ -853,6 +918,20 @@ class MainThreadSchedulerImplTest : public testing::Test {
return frame_task_queue_controller->GetTaskQueue(queue_traits);
}
+ static scoped_refptr<TaskQueue> ForegroundOnlyTaskQueue(
+ FrameSchedulerImpl* scheduler) {
+ auto* frame_task_queue_controller =
+ scheduler->FrameTaskQueueControllerForTest();
+ auto queue_traits = FrameSchedulerImpl::ForegroundOnlyTaskQueueTraits();
+ return frame_task_queue_controller->GetTaskQueue(queue_traits);
+ }
+
+ static scoped_refptr<TaskQueue> QueueForTaskType(
+ FrameSchedulerImpl* scheduler,
+ TaskType task_type) {
+ return scheduler->GetTaskQueue(task_type);
+ }
+
QueueingTimeEstimator* queueing_time_estimator() {
return &scheduler_->queueing_time_estimator_;
}
@@ -1049,6 +1128,7 @@ TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicy) {
}
TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicyWithSlowCompositor) {
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -1352,19 +1432,19 @@ class DefaultUseCaseTest : public MainThreadSchedulerImplTest {
};
TEST_F(DefaultUseCaseTest, InitiallyInEarlyLoadingUseCase) {
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
// Should be early loading by default.
EXPECT_EQ(UseCase::kEarlyLoading, ForceUpdatePolicyAndGetCurrentUseCase());
ON_CALL(*page_scheduler_, IsWaitingForMainFrameContentfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
EXPECT_EQ(UseCase::kLoading, CurrentUseCase());
ON_CALL(*page_scheduler_, IsWaitingForMainFrameMeaningfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
@@ -1401,7 +1481,7 @@ TEST_F(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) {
// UseCase::kLoading.
ON_CALL(*page_scheduler_, IsWaitingForMainFrameContentfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
run_order.clear();
PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2");
@@ -1418,7 +1498,7 @@ TEST_F(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) {
// longer prioritized.
ON_CALL(*page_scheduler_, IsWaitingForMainFrameMeaningfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(150000));
run_order.clear();
@@ -1432,6 +1512,7 @@ TEST_F(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) {
TEST_F(MainThreadSchedulerImplTest,
EventConsumedOnCompositorThread_IgnoresMouseMove_WhenMouseUp) {
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -1449,6 +1530,7 @@ TEST_F(MainThreadSchedulerImplTest,
TEST_F(MainThreadSchedulerImplTest,
EventForwardedToMainThread_IgnoresMouseMove_WhenMouseUp) {
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -1638,6 +1720,7 @@ TEST_F(
TEST_F(MainThreadSchedulerImplTest,
EventConsumedOnCompositorThread_IgnoresKeyboardEvents) {
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -1655,6 +1738,7 @@ TEST_F(MainThreadSchedulerImplTest,
TEST_F(MainThreadSchedulerImplTest,
EventForwardedToMainThread_IgnoresKeyboardEvents) {
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -2524,7 +2608,7 @@ TEST_F(MainThreadSchedulerImplTest,
.WillByDefault(Return(false));
ON_CALL(*page_scheduler_, IsWaitingForMainFrameMeaningfulPaint)
.WillByDefault(Return(true));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
EXPECT_EQ(UseCase::kLoading, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(rails_response_time(),
scheduler_->EstimateLongestJankFreeTaskDuration());
@@ -2670,7 +2754,7 @@ TEST_F(MainThreadSchedulerImplTest,
// helper should /not/ re-enable this queue under any circumstances while
// timers are paused.
if (count > 0 && !paused) {
- EXPECT_EQ(2u, count);
+ EXPECT_EQ(2u, count) << "i = " << i;
paused = scheduler_->PauseRenderer();
}
}
@@ -2933,11 +3017,11 @@ TEST_F(MainThreadSchedulerImplTest, TestLoadRAILMode) {
EXPECT_EQ(UseCase::kEarlyLoading, ForceUpdatePolicyAndGetCurrentUseCase());
ON_CALL(*page_scheduler_, IsWaitingForMainFrameContentfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
EXPECT_EQ(UseCase::kLoading, ForceUpdatePolicyAndGetCurrentUseCase());
ON_CALL(*page_scheduler_, IsWaitingForMainFrameMeaningfulPaint)
.WillByDefault(Return(false));
- scheduler_->OnMainFramePaint();
+ scheduler_->OnMainFramePaint(/*force_policy_update=*/false);
EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
EXPECT_EQ(RAILMode::kAnimation, GetRAILMode());
scheduler_->RemoveRAILModeObserver(&observer);
@@ -3085,13 +3169,11 @@ TEST_F(MainThreadSchedulerImplTest, EnableVirtualTime) {
}
TEST_F(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) {
- std::unique_ptr<PageSchedulerImpl> page_scheduler =
- base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get()));
- scheduler_->AddPageScheduler(page_scheduler.get());
+ auto page_scheduler = CreatePageScheduler(nullptr, scheduler_.get());
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
TaskQueue* timer_tq = ThrottleableTaskQueue(frame_scheduler.get()).get();
@@ -3221,15 +3303,15 @@ TEST_F(MainThreadSchedulerImplTest, Tracing) {
// traced value. This test checks that no internal checks fire during this.
std::unique_ptr<PageSchedulerImpl> page_scheduler1 =
- base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get()));
+ CreatePageScheduler(nullptr, scheduler_.get());
scheduler_->AddPageScheduler(page_scheduler1.get());
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler1.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler1.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
- base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get()));
+ CreatePageScheduler(nullptr, scheduler_.get());
scheduler_->AddPageScheduler(page_scheduler2.get());
CPUTimeBudgetPool* time_budget_pool =
@@ -3378,7 +3460,15 @@ TEST_F(MainThreadSchedulerImplWithInitalVirtualTimeTest, VirtualTimeOverride) {
EXPECT_EQ(base::Time::Now(), base::Time::FromJsTime(1000000.0));
}
-TEST_F(MainThreadSchedulerImplTest, CompositingAfterInput) {
+class MainThreadSchedulerImplWithCompositingAfterInputPrioritizationTest
+ : public MainThreadSchedulerImplTest {
+ public:
+ MainThreadSchedulerImplWithCompositingAfterInputPrioritizationTest()
+ : MainThreadSchedulerImplTest({kPrioritizeCompositingAfterInput}, {}) {}
+};
+
+TEST_F(MainThreadSchedulerImplWithCompositingAfterInputPrioritizationTest,
+ CompositingAfterInput) {
Vector<String> run_order;
PostTestTasks(&run_order, "P1 T1 C1");
base::RunLoop().RunUntilIdle();
@@ -3687,7 +3777,6 @@ TEST_F(VeryHighPriorityForCompositingWhenFastExperimentTest,
testing::ElementsAre("C1", "P1", "C2", "L1", "D1", "D2", "I1"));
EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
}
-
class VeryHighPriorityForCompositingAlternatingExperimentTest
: public MainThreadSchedulerImplTest {
public:
@@ -3702,10 +3791,13 @@ TEST_F(VeryHighPriorityForCompositingAlternatingExperimentTest,
Vector<String> run_order;
PostTestTasks(&run_order, "D1 D2 D3 C1 C2 C3");
+ // Compositor is very high priority, we do a main frame and it is set to
+ // normal priority for a task before being reprioritzed.
+ DoMainFrame();
EnableIdleTasks();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(run_order,
- testing::ElementsAre("C1", "D1", "C2", "D2", "C3", "D3"));
+ testing::ElementsAre("C1", "D1", "C2", "C3", "D2", "D3"));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
@@ -3738,6 +3830,7 @@ TEST_F(VeryHighPriorityForCompositingAfterDelayExperimentTest,
Vector<String> run_order;
PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1");
+ DoMainFrame();
EnableIdleTasks();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(run_order,
@@ -3751,12 +3844,20 @@ TEST_F(VeryHighPriorityForCompositingAfterDelayExperimentTest,
AdvanceTimeWithTask(0.15);
Vector<String> run_order;
- PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1");
+ PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
- EnableIdleTasks();
base::RunLoop().RunUntilIdle();
- EXPECT_THAT(run_order,
- testing::ElementsAre("P1", "C1", "D1", "D2", "C2", "I1"));
+ EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "C2", "D1", "D2"));
+ EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+
+ // Next compositor task is the BeginMainFrame. Afterwhich the priority is
+ // returned to normal.
+ DoMainFrame();
+ run_order.clear();
+ PostTestTasks(&run_order, "C1 D1 D2 C2 P1");
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "D1", "D2", "C2"));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
@@ -3795,7 +3896,9 @@ TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest,
testing::ElementsAre("P1", "C1", "C2", "D1", "D2", "I1"));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
- // 1000ms compositor task will exhaust the budget.
+ // 1000ms BeginMainFrame compositor task will exhaust the budget. Compositor
+ // tasks will be run at normal priority.
+ DoMainFrame();
RunSlowCompositorTask();
run_order.clear();
@@ -3803,8 +3906,6 @@ TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest,
EnableIdleTasks();
base::RunLoop().RunUntilIdle();
- // Compositor is now at kVeryHighPriority, compositing tasks will run
- // before default tasks.
EXPECT_THAT(run_order,
testing::ElementsAre("P1", "D1", "C1", "D2", "C2", "I1"));
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
@@ -3812,7 +3913,8 @@ TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest,
TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest,
TestCompositorPolicy_CompositorPriorityNormalToVeryHigh) {
- // 1000ms compositor task will exhaust the budget.
+ // 1000ms BeginMainFrame compositor task will exhaust the budget.
+ DoMainFrame();
RunSlowCompositorTask();
Vector<String> run_order;
@@ -3840,103 +3942,142 @@ TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest,
EXPECT_EQ(UseCase::kNone, CurrentUseCase());
}
-class VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest
+class DisableNonMainTimerQueuesUntilFMPTest
: public MainThreadSchedulerImplTest {
public:
- VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest()
- : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingAlternating,
- kPrioritizeCompositingUntilBeginMainFrame},
- {}) {}
+ DisableNonMainTimerQueuesUntilFMPTest()
+ : MainThreadSchedulerImplTest({{kPerAgentSchedulingExperiments,
+ {{"queues", "timer-queues"},
+ {"method", "disable"},
+ {"signal", "fmp"}}}}) {}
};
-TEST_F(VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest,
- TestCompositorPolicy_AlternatingCompositorTasks) {
- Vector<String> run_order;
- PostTestTasks(&run_order, "C1 D1 C2 D2");
+TEST_F(DisableNonMainTimerQueuesUntilFMPTest, DisablesOnlyNonMainTimerQueue) {
+ auto page_scheduler = CreatePageScheduler(nullptr, scheduler_.get());
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1", "D2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ NiceMock<MockFrameDelegate> frame_delegate{};
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ CreateFrameScheduler(page_scheduler.get(), &frame_delegate, nullptr,
+ FrameScheduler::FrameType::kSubframe);
- // Next compositor task is the BeginMainFrame. Compositor priority is set
- // to normal for a single task before being prioritized again.
- DoMainFrame();
+ scoped_refptr<TaskQueue> timer_tq =
+ QueueForTaskType(frame_scheduler.get(), TaskType::kJavascriptTimer);
+ ForceUpdatePolicyAndGetCurrentUseCase();
- run_order.clear();
- PostTestTasks(&run_order, "C1 D1 D2 C2");
+ EXPECT_FALSE(timer_tq->IsQueueEnabled());
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(run_order, testing::ElementsAre("C1", "D1", "C2", "D2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ ignore_result(
+ scheduler_->agent_scheduling_strategy().OnMainFrameFirstMeaningfulPaint(
+ *main_frame_scheduler_));
+ ForceUpdatePolicyAndGetCurrentUseCase();
+
+ EXPECT_TRUE(timer_tq->IsQueueEnabled());
}
-class VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest
+TEST_F(DisableNonMainTimerQueuesUntilFMPTest,
+ ShouldNotifyAgentStrategyOnInput) {
+ auto page_scheduler = CreatePageScheduler(nullptr, scheduler_.get());
+
+ NiceMock<MockFrameDelegate> frame_delegate{};
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ CreateFrameScheduler(page_scheduler.get(), &frame_delegate, nullptr,
+ FrameScheduler::FrameType::kSubframe);
+
+ scoped_refptr<TaskQueue> timer_tq =
+ QueueForTaskType(frame_scheduler.get(), TaskType::kJavascriptTimer);
+
+ FakeInputEvent mouse_move_event{WebInputEvent::Type::kMouseMove,
+ blink::WebInputEvent::kLeftButtonDown};
+ FakeInputEvent mouse_down_event{WebInputEvent::Type::kMouseDown,
+ blink::WebInputEvent::kLeftButtonDown};
+ InputEventState event_state{};
+
+ // Mouse move event should be ignored, meaning the queue should be disabled.
+ scheduler_->DidHandleInputEventOnCompositorThread(mouse_move_event,
+ event_state);
+ ForceUpdatePolicyAndGetCurrentUseCase();
+ EXPECT_FALSE(timer_tq->IsQueueEnabled());
+
+ // Mouse down should cause MTSI to notify the agent scheduling strategy, which
+ // should re-enable the timer queue.
+ scheduler_->DidHandleInputEventOnCompositorThread(mouse_down_event,
+ event_state);
+ base::RunLoop().RunUntilIdle(); // Notification is posted to the main thread.
+ EXPECT_TRUE(timer_tq->IsQueueEnabled());
+}
+
+class BestEffortNonMainQueuesUntilOnLoadTest
: public MainThreadSchedulerImplTest {
public:
- VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest()
- : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingAfterDelay,
- kPrioritizeCompositingUntilBeginMainFrame},
- {}) {}
+ BestEffortNonMainQueuesUntilOnLoadTest()
+ : MainThreadSchedulerImplTest({{kPerAgentSchedulingExperiments,
+ {{"queues", "all-queues"},
+ {"method", "best-effort"},
+ {"signal", "onload"}}}}) {}
};
-TEST_F(
- VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest,
- TestCompositorPolicy_FirstCompositorTaskSetToVeryHighPriority) {
- // 150ms task to complete the countdown and prioritze compositing.
- AdvanceTimeWithTask(0.15);
+TEST_F(BestEffortNonMainQueuesUntilOnLoadTest, DeprioritizesAllNonMainQueues) {
+ auto page_scheduler = CreatePageScheduler(nullptr, scheduler_.get());
- Vector<String> run_order;
- PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
+ NiceMock<MockFrameDelegate> frame_delegate{};
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
+ CreateFrameScheduler(page_scheduler.get(), &frame_delegate, nullptr,
+ FrameScheduler::FrameType::kSubframe);
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "C2", "D1", "D2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ scoped_refptr<TaskQueue> non_timer_tq =
+ QueueForTaskType(frame_scheduler.get(), TaskType::kNetworking);
+ ForceUpdatePolicyAndGetCurrentUseCase();
+ EXPECT_EQ(non_timer_tq->GetQueuePriority(),
+ TaskQueue::QueuePriority::kBestEffortPriority);
- // Next compositor task is the BeginMainFrame.
- DoMainFrame();
- run_order.clear();
- PostTestTasks(&run_order, "C1 D1 D2 C2 P1");
+ ignore_result(scheduler_->agent_scheduling_strategy().OnMainFrameLoad(
+ *main_frame_scheduler_));
+ ForceUpdatePolicyAndGetCurrentUseCase();
- base::RunLoop().RunUntilIdle();
- EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "D1", "D2", "C2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ EXPECT_EQ(non_timer_tq->GetQueuePriority(),
+ TaskQueue::QueuePriority::kNormalPriority);
}
-class VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest
+class BestEffortNonMainQueuesUntilOnFMPTimeoutTest
: public MainThreadSchedulerImplTest {
public:
- VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest()
- : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingBudget,
- kPrioritizeCompositingUntilBeginMainFrame},
- {}) {}
+ BestEffortNonMainQueuesUntilOnFMPTimeoutTest()
+ : MainThreadSchedulerImplTest({{kPerAgentSchedulingExperiments,
+ {{"queues", "all-queues"},
+ {"method", "best-effort"},
+ {"signal", "fmp"},
+ {"delay_ms", "1000"}}}}) {}
};
-TEST_F(
- VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest,
- TestCompositorPolicy_CompositorPriorityNonBeginMainFrameDoesntExhaustBudget) {
- // 1000ms compositor task will not exhaust the budget.
- RunSlowCompositorTask();
+TEST_F(BestEffortNonMainQueuesUntilOnFMPTimeoutTest,
+ DeprioritizesNonMainQueues) {
+ auto page_scheduler = CreatePageScheduler(
+ /* page_scheduler_delegate= */ nullptr, scheduler_.get());
- Vector<String> run_order;
- PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
- base::RunLoop().RunUntilIdle();
+ NiceMock<MockFrameDelegate> frame_delegate{};
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler = CreateFrameScheduler(
+ page_scheduler.get(), &frame_delegate, /* blame_context= */ nullptr,
+ FrameScheduler::FrameType::kSubframe);
- EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "C2", "D1", "D2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
-}
+ scoped_refptr<TaskQueue> non_timer_tq =
+ QueueForTaskType(frame_scheduler.get(), TaskType::kNetworking);
+ ForceUpdatePolicyAndGetCurrentUseCase();
+ EXPECT_EQ(non_timer_tq->GetQueuePriority(),
+ TaskQueue::QueuePriority::kBestEffortPriority);
-TEST_F(VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest,
- TestCompositorPolicy_CompositorPriorityBeginMainFrameExhaustsBudget) {
- // 1000ms BeginMainFrame will exhaust the budget.
- DoMainFrame();
- RunSlowCompositorTask();
+ ignore_result(
+ scheduler_->agent_scheduling_strategy().OnMainFrameFirstMeaningfulPaint(
+ *main_frame_scheduler_));
- Vector<String> run_order;
- PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
- base::RunLoop().RunUntilIdle();
+ // Queue should still have lower priority, since we just started counting
+ // towards the timeout.
+ EXPECT_EQ(non_timer_tq->GetQueuePriority(),
+ TaskQueue::QueuePriority::kBestEffortPriority);
- EXPECT_THAT(run_order, testing::ElementsAre("P1", "D1", "C1", "D2", "C2"));
- EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+ test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1000));
+
+ EXPECT_EQ(non_timer_tq->GetQueuePriority(),
+ TaskQueue::QueuePriority::kNormalPriority);
}
} // namespace main_thread_scheduler_impl_unittest
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 b9c51d21ea8..faf7beacddf 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
@@ -130,8 +130,9 @@ class PLATFORM_EXPORT MainThreadTaskQueue
kLoadingControl = 4,
kFindInPage = 5,
kExperimentalDatabase = 6,
+ kJavaScriptTimer = 7,
- kCount = 7
+ kCount = 8
};
// kPrioritisationTypeWidthBits is the number of bits required
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 3dea63c3bbb..9e5bb83e57f 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
@@ -28,9 +28,10 @@ class MemoryPurgeManagerTest : public testing::Test {
memory_purge_manager_(task_environment_.GetMainThreadTaskRunner()) {}
void SetUp() override {
- memory_pressure_listener_ =
- std::make_unique<base::MemoryPressureListener>(base::BindRepeating(
- &MemoryPurgeManagerTest::OnMemoryPressure, base::Unretained(this)));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE,
+ base::BindRepeating(&MemoryPurgeManagerTest::OnMemoryPressure,
+ base::Unretained(this)));
base::MemoryPressureListener::SetNotificationsSuppressed(false);
}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 3fe2f475033..aa4cb19f057 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include <memory>
#include "base/bind.h"
#include "base/check_op.h"
@@ -11,13 +12,18 @@
#include "base/notreached.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.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/common/throttling/task_queue_throttler.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/frame_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
@@ -58,6 +64,10 @@ constexpr base::TimeDelta kDefaultDelayForBackgroundTabFreezing =
constexpr base::TimeDelta kDefaultDelayForBackgroundAndNetworkIdleTabFreezing =
base::TimeDelta::FromMinutes(1);
+// Duration of a throttled wake up.
+constexpr base::TimeDelta kThrottledWakeUpDuration =
+ base::TimeDelta::FromMilliseconds(3);
+
// Values coming from the field trial config are interpreted as follows:
// -1 is "not set". Scheduler should use a reasonable default.
// 0 corresponds to base::nullopt.
@@ -137,6 +147,8 @@ base::TimeDelta GetDelayForBackgroundAndNetworkIdleTabFreezing() {
} // namespace
+constexpr base::TimeDelta PageSchedulerImpl::kDefaultThrottledWakeUpInterval;
+
PageSchedulerImpl::PageSchedulerImpl(
PageScheduler::Delegate* delegate,
MainThreadSchedulerImpl* main_thread_scheduler)
@@ -147,12 +159,17 @@ PageSchedulerImpl::PageSchedulerImpl(
audio_state_(AudioState::kSilent),
is_frozen_(false),
reported_background_throttling_since_navigation_(false),
+ opted_out_from_all_throttling_(false),
opted_out_from_aggressive_throttling_(false),
nested_runloop_(false),
is_main_frame_local_(false),
- is_throttled_(false),
+ is_cpu_time_throttled_(false),
+ are_wake_ups_intensively_throttled_(false),
keep_active_(main_thread_scheduler->SchedulerKeepActive()),
- background_time_budget_pool_(nullptr),
+ had_recent_title_or_favicon_update_(false),
+ cpu_time_budget_pool_(nullptr),
+ same_origin_wake_up_budget_pool_(nullptr),
+ cross_origin_wake_up_budget_pool_(nullptr),
delegate_(delegate),
delay_for_background_tab_freezing_(GetDelayForBackgroundTabFreezing()),
freeze_on_network_idle_enabled_(base::FeatureList::IsEnabled(
@@ -163,9 +180,14 @@ PageSchedulerImpl::PageSchedulerImpl(
this, kDefaultPageVisibility == PageVisibilityState::kVisible
? PageLifecycleState::kActive
: PageLifecycleState::kHiddenBackgrounded));
- main_thread_scheduler->AddPageScheduler(this);
- do_throttle_page_callback_.Reset(base::BindRepeating(
- &PageSchedulerImpl::DoThrottlePage, base::Unretained(this)));
+ do_throttle_cpu_time_callback_.Reset(base::BindRepeating(
+ &PageSchedulerImpl::DoThrottleCPUTime, base::Unretained(this)));
+ do_intensively_throttle_wake_ups_callback_.Reset(
+ base::BindRepeating(&PageSchedulerImpl::DoIntensivelyThrottleWakeUps,
+ base::Unretained(this)));
+ reset_had_recent_title_or_favicon_update_.Reset(base::BindRepeating(
+ &PageSchedulerImpl::ResetHadRecentTitleOrFaviconUpdate,
+ base::Unretained(this)));
on_audio_silent_closure_.Reset(base::BindRepeating(
&PageSchedulerImpl::OnAudioSilent, base::Unretained(this)));
do_freeze_page_callback_.Reset(base::BindRepeating(
@@ -180,8 +202,12 @@ PageSchedulerImpl::~PageSchedulerImpl() {
}
main_thread_scheduler_->RemovePageScheduler(this);
- if (background_time_budget_pool_)
- background_time_budget_pool_->Close();
+ if (cpu_time_budget_pool_)
+ cpu_time_budget_pool_->Close();
+ if (same_origin_wake_up_budget_pool_)
+ same_origin_wake_up_budget_pool_->Close();
+ if (cross_origin_wake_up_budget_pool_)
+ cross_origin_wake_up_budget_pool_->Close();
}
// static
@@ -225,8 +251,7 @@ void PageSchedulerImpl::SetPageVisible(bool page_visible) {
for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_)
frame_scheduler->SetPageVisibilityForTracing(page_visibility_);
- UpdateBackgroundSchedulingLifecycleState(
- NotificationPolicy::kDoNotNotifyFrames);
+ UpdatePolicyOnVisibilityChange(NotificationPolicy::kDoNotNotifyFrames);
NotifyFrames();
}
@@ -286,6 +311,9 @@ void PageSchedulerImpl::SetPageFrozenImpl(
}
main_thread_scheduler_->OnPageResumed();
}
+
+ if (delegate_)
+ delegate_->OnSetPageFrozen(frozen);
}
void PageSchedulerImpl::SetKeepActive(bool keep_active) {
@@ -324,8 +352,14 @@ void PageSchedulerImpl::SetIsMainFrameLocal(bool is_local) {
void PageSchedulerImpl::RegisterFrameSchedulerImpl(
FrameSchedulerImpl* frame_scheduler) {
- MaybeInitializeBackgroundCPUTimeBudgetPool();
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+
+ MaybeInitializeWakeUpBudgetPools(&lazy_now);
+ MaybeInitializeBackgroundCPUTimeBudgetPool(&lazy_now);
+
frame_schedulers_.insert(frame_scheduler);
+ main_thread_scheduler_->OnFrameAdded(*frame_scheduler);
frame_scheduler->UpdatePolicy();
}
@@ -333,12 +367,16 @@ std::unique_ptr<blink::FrameScheduler> PageSchedulerImpl::CreateFrameScheduler(
FrameScheduler::Delegate* delegate,
blink::BlameContext* blame_context,
FrameScheduler::FrameType frame_type) {
- return FrameSchedulerImpl::Create(this, delegate, blame_context, frame_type);
+ auto frame_scheduler = std::make_unique<FrameSchedulerImpl>(
+ this, delegate, blame_context, frame_type);
+ RegisterFrameSchedulerImpl(frame_scheduler.get());
+ return frame_scheduler;
}
void PageSchedulerImpl::Unregister(FrameSchedulerImpl* frame_scheduler) {
DCHECK(frame_schedulers_.find(frame_scheduler) != frame_schedulers_.end());
frame_schedulers_.erase(frame_scheduler);
+ main_thread_scheduler_->OnFrameRemoved(*frame_scheduler);
}
void PageSchedulerImpl::OnNavigation() {
@@ -432,6 +470,10 @@ bool PageSchedulerImpl::IsExemptFromBudgetBasedThrottling() const {
return opted_out_from_aggressive_throttling_;
}
+bool PageSchedulerImpl::OptedOutFromAllThrottling() const {
+ return opted_out_from_all_throttling_;
+}
+
bool PageSchedulerImpl::OptedOutFromAggressiveThrottlingForTest() const {
return OptedOutFromAggressiveThrottling();
}
@@ -459,22 +501,33 @@ bool PageSchedulerImpl::IsFrozen() const {
return is_frozen_;
}
-bool PageSchedulerImpl::IsThrottled() const {
- return is_throttled_;
+bool PageSchedulerImpl::IsCPUTimeThrottled() const {
+ return is_cpu_time_throttled_;
}
-void PageSchedulerImpl::OnAggressiveThrottlingStatusUpdated() {
+void PageSchedulerImpl::OnThrottlingStatusUpdated() {
+ bool opted_out_from_all_throttling = false;
bool opted_out_from_aggressive_throttling = false;
for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
+ opted_out_from_all_throttling |=
+ frame_scheduler->opted_out_from_all_throttling();
opted_out_from_aggressive_throttling |=
frame_scheduler->opted_out_from_aggressive_throttling();
}
+ DCHECK(!opted_out_from_all_throttling ||
+ opted_out_from_aggressive_throttling);
- if (opted_out_from_aggressive_throttling_ !=
- opted_out_from_aggressive_throttling) {
+ if (opted_out_from_all_throttling_ != opted_out_from_all_throttling ||
+ opted_out_from_aggressive_throttling_ !=
+ opted_out_from_aggressive_throttling) {
+ opted_out_from_all_throttling_ = opted_out_from_all_throttling;
opted_out_from_aggressive_throttling_ =
opted_out_from_aggressive_throttling;
- UpdateBackgroundBudgetPoolSchedulingLifecycleState();
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+ UpdateCPUTimeBudgetPool(&lazy_now);
+ UpdateWakeUpBudgetPools(&lazy_now);
+ NotifyFrames();
}
}
@@ -522,40 +575,96 @@ void PageSchedulerImpl::AsValueInto(
state->EndDictionary();
}
-CPUTimeBudgetPool* PageSchedulerImpl::BackgroundCPUTimeBudgetPool() {
- MaybeInitializeBackgroundCPUTimeBudgetPool();
- return background_time_budget_pool_;
+void PageSchedulerImpl::AddQueueToWakeUpBudgetPool(
+ MainThreadTaskQueue* task_queue,
+ FrameOriginType frame_origin_type,
+ base::sequence_manager::LazyNow* lazy_now) {
+ GetWakeUpBudgetPool(frame_origin_type)->AddQueue(lazy_now->Now(), task_queue);
+}
+
+void PageSchedulerImpl::RemoveQueueFromWakeUpBudgetPool(
+ MainThreadTaskQueue* task_queue,
+ FrameOriginType frame_origin_type,
+ base::sequence_manager::LazyNow* lazy_now) {
+ GetWakeUpBudgetPool(frame_origin_type)
+ ->RemoveQueue(lazy_now->Now(), task_queue);
+}
+
+WakeUpBudgetPool* PageSchedulerImpl::GetWakeUpBudgetPool(
+ FrameOriginType frame_origin_type) {
+ switch (frame_origin_type) {
+ case FrameOriginType::kMainFrame:
+ case FrameOriginType::kSameOriginToMainFrame:
+ return same_origin_wake_up_budget_pool_;
+ break;
+ case FrameOriginType::kCrossOriginToMainFrame:
+ return cross_origin_wake_up_budget_pool_;
+ case FrameOriginType::kCount:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+CPUTimeBudgetPool* PageSchedulerImpl::background_cpu_time_budget_pool() {
+ return cpu_time_budget_pool_;
}
-void PageSchedulerImpl::MaybeInitializeBackgroundCPUTimeBudgetPool() {
- if (background_time_budget_pool_)
+void PageSchedulerImpl::MaybeInitializeBackgroundCPUTimeBudgetPool(
+ base::sequence_manager::LazyNow* lazy_now) {
+ if (cpu_time_budget_pool_)
return;
if (!RuntimeEnabledFeatures::ExpensiveBackgroundTimerThrottlingEnabled())
return;
- background_time_budget_pool_ =
+ cpu_time_budget_pool_ =
main_thread_scheduler_->task_queue_throttler()->CreateCPUTimeBudgetPool(
"background");
- base::sequence_manager::LazyNow lazy_now(
- main_thread_scheduler_->tick_clock());
BackgroundThrottlingSettings settings = GetBackgroundThrottlingSettings();
- background_time_budget_pool_->SetMaxBudgetLevel(lazy_now.Now(),
- settings.max_budget_level);
- background_time_budget_pool_->SetMaxThrottlingDelay(
- lazy_now.Now(), settings.max_throttling_delay);
+ cpu_time_budget_pool_->SetMaxBudgetLevel(lazy_now->Now(),
+ settings.max_budget_level);
+ cpu_time_budget_pool_->SetMaxThrottlingDelay(lazy_now->Now(),
+ settings.max_throttling_delay);
- background_time_budget_pool_->SetTimeBudgetRecoveryRate(
- lazy_now.Now(), settings.budget_recovery_rate);
+ cpu_time_budget_pool_->SetTimeBudgetRecoveryRate(
+ lazy_now->Now(), settings.budget_recovery_rate);
if (settings.initial_budget) {
- background_time_budget_pool_->GrantAdditionalBudget(
- lazy_now.Now(), settings.initial_budget.value());
+ cpu_time_budget_pool_->GrantAdditionalBudget(
+ lazy_now->Now(), settings.initial_budget.value());
}
- UpdateBackgroundBudgetPoolSchedulingLifecycleState();
+ UpdateCPUTimeBudgetPool(lazy_now);
+}
+
+void PageSchedulerImpl::MaybeInitializeWakeUpBudgetPools(
+ base::sequence_manager::LazyNow* lazy_now) {
+ DCHECK_EQ(!!same_origin_wake_up_budget_pool_,
+ !!cross_origin_wake_up_budget_pool_);
+ if (same_origin_wake_up_budget_pool_)
+ return;
+
+ same_origin_wake_up_budget_pool_ =
+ main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
+ "Page Wake Up Throttling - Same-Origin as Main Frame");
+ cross_origin_wake_up_budget_pool_ =
+ main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
+ "Page Wake Up Throttling - Cross-Origin to Main Frame");
+
+ // The Wake Up Duration and Unaligned Wake Ups Allowance are constant and set
+ // here. The Wake Up Interval is set in UpdateWakeUpBudgetPools(), based on
+ // current state.
+ same_origin_wake_up_budget_pool_->SetWakeUpDuration(kThrottledWakeUpDuration);
+ if (IsIntensiveWakeUpThrottlingEnabled()) {
+ same_origin_wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
+ }
+
+ cross_origin_wake_up_budget_pool_->SetWakeUpDuration(
+ kThrottledWakeUpDuration);
+
+ UpdateWakeUpBudgetPools(lazy_now);
}
void PageSchedulerImpl::OnThrottlingReported(
@@ -578,42 +687,137 @@ void PageSchedulerImpl::OnThrottlingReported(
delegate_->ReportIntervention(message);
}
-void PageSchedulerImpl::UpdateBackgroundSchedulingLifecycleState(
+void PageSchedulerImpl::UpdatePolicyOnVisibilityChange(
NotificationPolicy notification_policy) {
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+
if (page_visibility_ == PageVisibilityState::kVisible) {
- is_throttled_ = false;
- do_throttle_page_callback_.Cancel();
- UpdateBackgroundBudgetPoolSchedulingLifecycleState();
+ is_cpu_time_throttled_ = false;
+ do_throttle_cpu_time_callback_.Cancel();
+ UpdateCPUTimeBudgetPool(&lazy_now);
+
+ are_wake_ups_intensively_throttled_ = false;
+ do_intensively_throttle_wake_ups_callback_.Cancel();
+ UpdateWakeUpBudgetPools(&lazy_now);
} else {
- main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
- FROM_HERE, do_throttle_page_callback_.GetCallback(),
- kThrottlingDelayAfterBackgrounding);
+ if (cpu_time_budget_pool_) {
+ main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
+ FROM_HERE, do_throttle_cpu_time_callback_.GetCallback(),
+ kThrottlingDelayAfterBackgrounding);
+ }
+ if (IsIntensiveWakeUpThrottlingEnabled()) {
+ main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
+ FROM_HERE, do_intensively_throttle_wake_ups_callback_.GetCallback(),
+ GetIntensiveWakeUpThrottlingGracePeriod());
+ }
}
if (notification_policy == NotificationPolicy::kNotifyFrames)
NotifyFrames();
}
-void PageSchedulerImpl::DoThrottlePage() {
- do_throttle_page_callback_.Cancel();
- is_throttled_ = true;
+void PageSchedulerImpl::DoThrottleCPUTime() {
+ do_throttle_cpu_time_callback_.Cancel();
+ is_cpu_time_throttled_ = true;
- UpdateBackgroundBudgetPoolSchedulingLifecycleState();
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+ UpdateCPUTimeBudgetPool(&lazy_now);
NotifyFrames();
}
-void PageSchedulerImpl::UpdateBackgroundBudgetPoolSchedulingLifecycleState() {
- if (!background_time_budget_pool_)
- return;
+void PageSchedulerImpl::DoIntensivelyThrottleWakeUps() {
+ DCHECK(IsIntensiveWakeUpThrottlingEnabled());
+
+ do_intensively_throttle_wake_ups_callback_.Cancel();
+ are_wake_ups_intensively_throttled_ = true;
base::sequence_manager::LazyNow lazy_now(
main_thread_scheduler_->tick_clock());
- if (is_throttled_ && !opted_out_from_aggressive_throttling_) {
- background_time_budget_pool_->EnableThrottling(&lazy_now);
+ UpdateWakeUpBudgetPools(&lazy_now);
+ NotifyFrames();
+}
+
+void PageSchedulerImpl::UpdateCPUTimeBudgetPool(
+ base::sequence_manager::LazyNow* lazy_now) {
+ if (!cpu_time_budget_pool_)
+ return;
+
+ if (is_cpu_time_throttled_ && !opted_out_from_aggressive_throttling_) {
+ cpu_time_budget_pool_->EnableThrottling(lazy_now);
} else {
- background_time_budget_pool_->DisableThrottling(&lazy_now);
+ cpu_time_budget_pool_->DisableThrottling(lazy_now);
}
}
+void PageSchedulerImpl::OnTitleOrFaviconUpdated() {
+ if (!same_origin_wake_up_budget_pool_)
+ return;
+
+ if (are_wake_ups_intensively_throttled_ &&
+ !opted_out_from_aggressive_throttling_) {
+ // When the title of favicon is updated, intensive throttling is inhibited
+ // for same-origin frames. This enables alternating effects meant to grab
+ // the user's attention. Cross-origin frames are not affected, since they
+ // shouldn't be able to observe that the page title or favicon was updated.
+ base::TimeDelta time_to_inhibit_intensive_throttling =
+ GetTimeToInhibitIntensiveThrottlingOnTitleOrFaviconUpdate();
+
+ if (time_to_inhibit_intensive_throttling.is_zero()) {
+ // No inhibiting to be done.
+ return;
+ }
+
+ had_recent_title_or_favicon_update_ = true;
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+ UpdateWakeUpBudgetPools(&lazy_now);
+
+ // Re-enable intensive throttling from a delayed task.
+ reset_had_recent_title_or_favicon_update_.Cancel();
+ main_thread_scheduler_->ControlTaskRunner()->PostDelayedTask(
+ FROM_HERE, reset_had_recent_title_or_favicon_update_.GetCallback(),
+ time_to_inhibit_intensive_throttling);
+ }
+}
+
+void PageSchedulerImpl::ResetHadRecentTitleOrFaviconUpdate() {
+ had_recent_title_or_favicon_update_ = false;
+
+ base::sequence_manager::LazyNow lazy_now(
+ main_thread_scheduler_->tick_clock());
+ UpdateWakeUpBudgetPools(&lazy_now);
+
+ NotifyFrames();
+}
+
+base::TimeDelta PageSchedulerImpl::GetIntensiveWakeUpThrottlingDuration(
+ bool is_same_origin) {
+ // Title and favicon changes only affect the same_origin wake up budget pool.
+ if (is_same_origin && had_recent_title_or_favicon_update_)
+ return kDefaultThrottledWakeUpInterval;
+
+ if (are_wake_ups_intensively_throttled_ &&
+ !opted_out_from_aggressive_throttling_)
+ return GetIntensiveWakeUpThrottlingDurationBetweenWakeUps();
+ else
+ return kDefaultThrottledWakeUpInterval;
+}
+
+void PageSchedulerImpl::UpdateWakeUpBudgetPools(
+ base::sequence_manager::LazyNow* lazy_now) {
+ DCHECK_EQ(!!same_origin_wake_up_budget_pool_,
+ !!cross_origin_wake_up_budget_pool_);
+
+ if (!same_origin_wake_up_budget_pool_)
+ return;
+
+ same_origin_wake_up_budget_pool_->SetWakeUpInterval(
+ lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(true));
+ cross_origin_wake_up_budget_pool_->SetWakeUpInterval(
+ lazy_now->Now(), GetIntensiveWakeUpThrottlingDuration(false));
+}
+
void PageSchedulerImpl::NotifyFrames() {
for (FrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
frame_scheduler->UpdatePolicy();
@@ -711,8 +915,6 @@ void PageSchedulerImpl::PageLifecycleStateTracker::SetPageLifecycleState(
kHistogramPageLifecycleStateTransition,
static_cast<PageLifecycleStateTransition>(transition.value()));
}
- if (page_scheduler_impl_->delegate_)
- page_scheduler_impl_->delegate_->SetLifecycleState(new_state);
current_state_ = new_state;
}
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 49cb23578b8..e3a3904e19a 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -16,6 +16,7 @@
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
#include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
@@ -42,14 +43,21 @@ class
class CPUTimeBudgetPool;
class FrameSchedulerImpl;
class MainThreadSchedulerImpl;
+class MainThreadTaskQueue;
+class WakeUpBudgetPool;
class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
public:
+ // Interval between throttled wake ups, when intensive throttling is disabled.
+ static constexpr base::TimeDelta kDefaultThrottledWakeUpInterval =
+ base::TimeDelta::FromSeconds(1);
+
PageSchedulerImpl(PageScheduler::Delegate*, MainThreadSchedulerImpl*);
~PageSchedulerImpl() override;
// PageScheduler implementation:
+ void OnTitleOrFaviconUpdated() override;
void SetPageVisible(bool page_visible) override;
void SetPageFrozen(bool) override;
void SetKeepActive(bool) override;
@@ -86,12 +94,11 @@ class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
bool IsPageVisible() const;
bool IsFrozen() const;
- // PageSchedulerImpl::OptedOutFromAggressiveThrottling can be used in non-test
- // code, while PageScheduler::OptedOutFromAggressiveThrottlingForTest can't.
+ bool OptedOutFromAllThrottling() const;
bool OptedOutFromAggressiveThrottling() const;
- // Note that the frame can throttle queues even when the page is not throttled
- // (e.g. for offscreen frames or recently backgrounded pages).
- bool IsThrottled() const;
+ // Returns whether CPU time is throttled for the page. Note: This is
+ // independent from wake up rate throttling.
+ bool IsCPUTimeThrottled() const;
bool KeepActive() const;
bool IsLoading() const;
@@ -100,14 +107,12 @@ class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
// owned by a web view.
bool IsOrdinary() const;
- void RegisterFrameSchedulerImpl(FrameSchedulerImpl* frame_scheduler);
-
MainThreadSchedulerImpl* GetMainThreadScheduler() const;
void Unregister(FrameSchedulerImpl*);
void OnNavigation();
- void OnAggressiveThrottlingStatusUpdated();
+ void OnThrottlingStatusUpdated();
void OnTraceLogEnabled();
@@ -192,9 +197,16 @@ class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
DISALLOW_COPY_AND_ASSIGN(PageLifecycleStateTracker);
};
- // We do not throttle anything while audio is played and shortly after that.
+ void RegisterFrameSchedulerImpl(FrameSchedulerImpl* frame_scheduler);
+
+ // A page cannot be throttled or frozen 30 seconds after playing audio.
+ //
+ // This used to be 5 seconds, which was barely enough to cover the time of
+ // silence during which a logo and button are shown after a YouTube ad. Since
+ // most pages don't play audio in background, it was decided that the delay
+ // can be increased to 30 seconds without significantly affecting performance.
static constexpr base::TimeDelta kRecentAudioDelay =
- base::TimeDelta::FromSeconds(5);
+ base::TimeDelta::FromSeconds(30);
static const char kHistogramPageLifecycleStateTransition[];
@@ -202,29 +214,49 @@ class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
// a part of foregrounding the page.
void SetPageFrozenImpl(bool frozen, NotificationPolicy notification_policy);
- CPUTimeBudgetPool* BackgroundCPUTimeBudgetPool();
- void MaybeInitializeBackgroundCPUTimeBudgetPool();
+ // Adds or removes a |task_queue| from the WakeUpBudgetPool associated with
+ // |frame_origin_type|. When the FrameOriginType of a FrameScheduler changes,
+ // it should remove all its TaskQueues from their current WakeUpBudgetPool and
+ // add them back to the WakeUpBudgetPool appropriate for the new
+ // FrameOriginType.
+ void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* task_queue,
+ FrameOriginType frame_origin_type,
+ base::sequence_manager::LazyNow* lazy_now);
+ void RemoveQueueFromWakeUpBudgetPool(
+ MainThreadTaskQueue* task_queue,
+ FrameOriginType frame_origin_type,
+ base::sequence_manager::LazyNow* lazy_now);
+ // Returns the WakeUpBudgetPool to use for a frame with |frame_origin_type|.
+ WakeUpBudgetPool* GetWakeUpBudgetPool(FrameOriginType frame_origin_type);
+ // Initializes WakeUpBudgetPools, if not already initialized.
+ void MaybeInitializeWakeUpBudgetPools(
+ base::sequence_manager::LazyNow* lazy_now);
+
+ CPUTimeBudgetPool* background_cpu_time_budget_pool();
+ void MaybeInitializeBackgroundCPUTimeBudgetPool(
+ base::sequence_manager::LazyNow* lazy_now);
void OnThrottlingReported(base::TimeDelta throttling_duration);
// Depending on page visibility, either turns throttling off, or schedules a
// call to enable it after a grace period.
- void UpdateBackgroundSchedulingLifecycleState(
- NotificationPolicy notification_policy);
+ void UpdatePolicyOnVisibilityChange(NotificationPolicy notification_policy);
- // As a part of UpdateBackgroundSchedulingLifecycleState set correct
- // background_time_budget_pool_ state depending on page visibility and
- // number of active connections.
- void UpdateBackgroundBudgetPoolSchedulingLifecycleState();
+ // Adjusts settings of budget pools depending on current state of the page.
+ void UpdateCPUTimeBudgetPool(base::sequence_manager::LazyNow* lazy_now);
+ void UpdateWakeUpBudgetPools(base::sequence_manager::LazyNow* lazy_now);
+ base::TimeDelta GetIntensiveWakeUpThrottlingDuration(bool is_same_origin);
// Callback for marking page is silent after a delay since last audible
// signal.
void OnAudioSilent();
- // Callback for enabling throttling in background after specified delay.
+ // Callbacks for adjusting the settings of a budget pool after a delay.
// TODO(altimin): Trigger throttling depending on the loading state
// of the page.
- void DoThrottlePage();
+ void DoThrottleCPUTime();
+ void DoIntensivelyThrottleWakeUps();
+ void ResetHadRecentTitleOrFaviconUpdate();
// Notify frames that the page scheduler state has been updated.
void NotifyFrames();
@@ -252,14 +284,33 @@ class PLATFORM_EXPORT PageSchedulerImpl : public PageScheduler {
AudioState audio_state_;
bool is_frozen_;
bool reported_background_throttling_since_navigation_;
+ bool opted_out_from_all_throttling_;
bool opted_out_from_aggressive_throttling_;
bool nested_runloop_;
bool is_main_frame_local_;
- bool is_throttled_;
+ bool is_cpu_time_throttled_;
+ bool are_wake_ups_intensively_throttled_;
bool keep_active_;
- CPUTimeBudgetPool* background_time_budget_pool_; // Not owned.
- PageScheduler::Delegate* delegate_; // Not owned.
- CancelableClosureHolder do_throttle_page_callback_;
+ bool had_recent_title_or_favicon_update_;
+ CPUTimeBudgetPool* cpu_time_budget_pool_;
+ // Throttles wake ups in throttleable TaskQueues of frames that have the same
+ // origin as the main frame.
+ //
+ // This pool allows aligned wake ups and unaligned wake ups if there hasn't
+ // been a recent wake up.
+ WakeUpBudgetPool* same_origin_wake_up_budget_pool_;
+ // Throttles wake ups in throttleable TaskQueues of frames that are
+ // cross-origin with the main frame.
+ //
+ // This pool only allows aligned wake ups. Because wake ups do not depend on
+ // recent wake ups like in |same_origin_wake_up_budget_pool_|, tasks cannot
+ // easily learn about tasks running in other queues in the same pool. This is
+ // important because this pool can have queues from different origins.
+ WakeUpBudgetPool* cross_origin_wake_up_budget_pool_;
+ PageScheduler::Delegate* delegate_;
+ CancelableClosureHolder do_throttle_cpu_time_callback_;
+ CancelableClosureHolder do_intensively_throttle_wake_ups_callback_;
+ CancelableClosureHolder reset_had_recent_title_or_favicon_update_;
CancelableClosureHolder on_audio_silent_closure_;
CancelableClosureHolder do_freeze_page_callback_;
const base::TimeDelta delay_for_background_tab_freezing_;
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index d9b8a64e36f..6f798db6b37 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -43,6 +43,32 @@ namespace {
void IncrementCounter(int* counter) {
++*counter;
}
+
+// This is a wrapper around MainThreadSchedulerImpl::CreatePageScheduler, that
+// returns the PageScheduler as a PageSchedulerImpl.
+std::unique_ptr<PageSchedulerImpl> CreatePageScheduler(
+ PageScheduler::Delegate* page_scheduler_delegate,
+ MainThreadSchedulerImpl* scheduler) {
+ std::unique_ptr<PageScheduler> page_scheduler =
+ scheduler->CreatePageScheduler(page_scheduler_delegate);
+ std::unique_ptr<PageSchedulerImpl> page_scheduler_impl(
+ static_cast<PageSchedulerImpl*>(page_scheduler.release()));
+ return page_scheduler_impl;
+}
+
+// This is a wrapper around PageSchedulerImpl::CreateFrameScheduler, that
+// returns the FrameScheduler as a FrameSchedulerImpl.
+std::unique_ptr<FrameSchedulerImpl> CreateFrameScheduler(
+ PageSchedulerImpl* page_scheduler,
+ FrameScheduler::Delegate* delegate,
+ blink::BlameContext* blame_context,
+ FrameScheduler::FrameType frame_type) {
+ auto frame_scheduler =
+ page_scheduler->CreateFrameScheduler(delegate, blame_context, frame_type);
+ std::unique_ptr<FrameSchedulerImpl> frame_scheduler_impl(
+ static_cast<FrameSchedulerImpl*>(frame_scheduler.release()));
+ return frame_scheduler_impl;
+}
} // namespace
using base::Bucket;
@@ -58,7 +84,7 @@ class MockPageSchedulerDelegate : public PageScheduler::Delegate {
private:
void ReportIntervention(const WTF::String&) override {}
bool RequestBeginMainFrameNotExpected(bool) override { return false; }
- void SetLifecycleState(PageLifecycleState) override {}
+ void OnSetPageFrozen(bool is_frozen) override {}
bool IsOrdinary() const override { return true; }
bool idle_;
@@ -84,16 +110,16 @@ class PageSchedulerImplTest : public testing::Test {
// A null clock triggers some assertions.
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(5));
- scheduler_.reset(new MainThreadSchedulerImpl(
+ scheduler_ = std::make_unique<MainThreadSchedulerImpl>(
base::sequence_manager::SequenceManagerForTest::Create(
nullptr, test_task_runner_, test_task_runner_->GetMockTickClock()),
- base::nullopt));
- page_scheduler_delegate_.reset(new MockPageSchedulerDelegate());
- page_scheduler_.reset(new PageSchedulerImpl(page_scheduler_delegate_.get(),
- scheduler_.get()));
+ base::nullopt);
+ page_scheduler_delegate_ = std::make_unique<MockPageSchedulerDelegate>();
+ page_scheduler_ =
+ CreatePageScheduler(page_scheduler_delegate_.get(), scheduler_.get());
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
}
void TearDown() override {
@@ -361,11 +387,11 @@ TEST_F(PageSchedulerImplTest, RepeatingLoadingTask_PageInBackground) {
}
TEST_F(PageSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) {
- std::unique_ptr<PageSchedulerImpl> page_scheduler2(
- new PageSchedulerImpl(nullptr, scheduler_.get()));
+ std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
+ CreatePageScheduler(nullptr, scheduler_.get());
std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
- FrameSchedulerImpl::Create(page_scheduler2.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler2.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetPageVisible(true);
page_scheduler2->SetPageVisible(false);
@@ -622,8 +648,8 @@ TEST_F(PageSchedulerImplTest, VirtualTimeSettings_NewFrameScheduler) {
page_scheduler_->EnableVirtualTime();
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
ThrottleableTaskQueueForScheduler(frame_scheduler.get())
->task_runner()
@@ -653,8 +679,8 @@ base::OnceClosure MakeDeletionTask(T* obj) {
TEST_F(PageSchedulerImplTest, DeleteFrameSchedulers_InTask) {
for (int i = 0; i < 10; i++) {
FrameSchedulerImpl* frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe)
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe)
.release();
ThrottleableTaskQueueForScheduler(frame_scheduler)
->task_runner()
@@ -674,8 +700,8 @@ TEST_F(PageSchedulerImplTest, DeleteThrottledQueue_InTask) {
page_scheduler_->SetPageVisible(false);
FrameSchedulerImpl* frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe)
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe)
.release();
scoped_refptr<TaskQueue> timer_task_queue =
ThrottleableTaskQueueForScheduler(frame_scheduler);
@@ -727,8 +753,8 @@ TEST_F(PageSchedulerImplTest,
VirtualTimePolicy::kDeterministicLoading);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
{
WebScopedVirtualTimePauser virtual_time_pauser =
@@ -794,8 +820,8 @@ TEST_F(PageSchedulerImplTest,
base::TimeTicks time_second_task;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
// Pauses and unpauses virtual time, thereby advancing virtual time by an
// additional 10ms due to WebScopedVirtualTimePauser's delay.
@@ -830,8 +856,8 @@ TEST_F(PageSchedulerImplTest,
VirtualTimePolicy::kDeterministicLoading);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
WebScopedVirtualTimePauser virtual_time_pauser1 =
frame_scheduler->CreateWebScopedVirtualTimePauser(
@@ -877,8 +903,8 @@ TEST_F(PageSchedulerImplTest, PauseTimersWhileVirtualTimeIsPaused) {
Vector<int> run_order;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
page_scheduler_->EnableVirtualTime();
@@ -1099,15 +1125,15 @@ TEST_F(PageSchedulerImplTest, BackgroundTimerThrottling) {
budget_background_throttling_enabler(true);
InitializeTrialParams();
- page_scheduler_.reset(new PageSchedulerImpl(nullptr, scheduler_.get()));
- EXPECT_FALSE(page_scheduler_->IsThrottled());
+ page_scheduler_ = CreatePageScheduler(nullptr, scheduler_.get());
+ EXPECT_FALSE(page_scheduler_->IsCPUTimeThrottled());
Vector<base::TimeTicks> run_times;
frame_scheduler_ =
- FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler_.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
page_scheduler_->SetPageVisible(true);
- EXPECT_FALSE(page_scheduler_->IsThrottled());
+ EXPECT_FALSE(page_scheduler_->IsCPUTimeThrottled());
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromMilliseconds(2500));
@@ -1131,11 +1157,11 @@ TEST_F(PageSchedulerImplTest, BackgroundTimerThrottling) {
run_times.clear();
page_scheduler_->SetPageVisible(false);
- EXPECT_FALSE(page_scheduler_->IsThrottled());
+ EXPECT_FALSE(page_scheduler_->IsCPUTimeThrottled());
// Ensure that the page is fully throttled.
FastForwardTo(base::TimeTicks() + base::TimeDelta::FromSeconds(15));
- EXPECT_TRUE(page_scheduler_->IsThrottled());
+ EXPECT_TRUE(page_scheduler_->IsCPUTimeThrottled());
ThrottleableTaskQueue()->task_runner()->PostDelayedTask(
FROM_HERE,
@@ -1162,17 +1188,17 @@ TEST_F(PageSchedulerImplTest, OpenWebSocketExemptsFromBudgetThrottling) {
budget_background_throttling_enabler(true);
InitializeTrialParams();
- std::unique_ptr<PageSchedulerImpl> page_scheduler(
- new PageSchedulerImpl(nullptr, scheduler_.get()));
+ std::unique_ptr<PageSchedulerImpl> page_scheduler =
+ CreatePageScheduler(nullptr, scheduler_.get());
Vector<base::TimeTicks> run_times;
std::unique_ptr<FrameSchedulerImpl> frame_scheduler1 =
- FrameSchedulerImpl::Create(page_scheduler.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
std::unique_ptr<FrameSchedulerImpl> frame_scheduler2 =
- FrameSchedulerImpl::Create(page_scheduler.get(), nullptr, nullptr,
- FrameScheduler::FrameType::kSubframe);
+ CreateFrameScheduler(page_scheduler.get(), nullptr, nullptr,
+ FrameScheduler::FrameType::kSubframe);
page_scheduler->SetPageVisible(false);
@@ -1293,14 +1319,14 @@ TEST_F(PageSchedulerImplTest, AudioState) {
// We are audible for a certain period after raw signal disappearing.
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
- test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(3));
+ test_task_runner_->FastForwardBy(recent_audio_delay() / 2);
page_scheduler_->AudioStateChanged(false);
// We are still audible. A new call to AudioStateChanged shouldn't change
// anything.
EXPECT_TRUE(page_scheduler_->IsAudioPlaying());
- test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(3));
+ test_task_runner_->FastForwardBy(recent_audio_delay() / 2);
// Audio is finally silent.
EXPECT_FALSE(page_scheduler_->IsAudioPlaying());
@@ -1345,7 +1371,7 @@ TEST_F(PageSchedulerImplTest, KeepActiveSetForNewPages) {
scheduler_->SetSchedulerKeepActive(true);
std::unique_ptr<PageSchedulerImpl> page_scheduler2 =
- std::make_unique<PageSchedulerImpl>(nullptr, scheduler_.get());
+ CreatePageScheduler(nullptr, scheduler_.get());
EXPECT_TRUE(page_scheduler_->KeepActive());
EXPECT_TRUE(page_scheduler2->KeepActive());
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 d680bb7669a..6b714382b70 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
@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+#include "base/notreached.h"
+
namespace blink {
namespace {
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 c9f33ee112b..e546e668fd3 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
@@ -154,6 +154,9 @@ class FrameScheduler : public FrameOrWorkerScheduler {
// frame.
virtual void OnFirstMeaningfulPaint() = 0;
+ // Tells the scheduler that the "onload" event has occurred for this frame.
+ virtual void OnLoad() = 0;
+
// Returns true if this frame is should not throttled (e.g. due to an active
// connection).
// Note that this only applies to the current frame,
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
index 3d7dde3229f..5dd1b77eb71 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
@@ -30,7 +30,7 @@ class PLATFORM_EXPORT PageScheduler {
// Returns true if the request has been succcessfully relayed to the
// compositor.
virtual bool RequestBeginMainFrameNotExpected(bool new_state) = 0;
- virtual void SetLifecycleState(PageLifecycleState) = 0;
+ virtual void OnSetPageFrozen(bool is_frozen) = 0;
// Returns true iff the network is idle for the local main frame.
// Always returns false if the main frame is remote.
virtual bool LocalMainFrameNetworkIsAlmostIdle() const { return true; }
@@ -38,6 +38,9 @@ class PLATFORM_EXPORT PageScheduler {
virtual ~PageScheduler() = default;
+ // Signals that communications with the user took place via either a title
+ // updates or a change to the favicon.
+ virtual void OnTitleOrFaviconUpdated() = 0;
// The scheduler may throttle tasks associated with background pages.
virtual void SetPageVisible(bool) = 0;
// The scheduler transitions app to and from FROZEN state in background.
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h b/chromium/third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h
index 9e14bc3e000..791a0244ca8 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h
+++ b/chromium/third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h
@@ -22,10 +22,12 @@ struct PLATFORM_EXPORT SchedulingPolicy {
static bool IsFeatureSticky(Feature feature);
// List of opt-outs which form a policy.
+ struct DisableAllThrottling {};
struct DisableAggressiveThrottling {};
struct RecordMetricsForBackForwardCache {};
struct ValidPolicies {
+ ValidPolicies(DisableAllThrottling);
ValidPolicies(DisableAggressiveThrottling);
ValidPolicies(RecordMetricsForBackForwardCache);
};
@@ -35,7 +37,9 @@ struct PLATFORM_EXPORT SchedulingPolicy {
base::trait_helpers::AreValidTraits<ValidPolicies,
ArgTypes...>::value>>
constexpr SchedulingPolicy(ArgTypes... args)
- : disable_aggressive_throttling(
+ : disable_all_throttling(
+ base::trait_helpers::HasTrait<DisableAllThrottling, ArgTypes...>()),
+ disable_aggressive_throttling(
base::trait_helpers::HasTrait<DisableAggressiveThrottling,
ArgTypes...>()),
disable_back_forward_cache(
@@ -44,6 +48,7 @@ struct PLATFORM_EXPORT SchedulingPolicy {
SchedulingPolicy() {}
+ bool disable_all_throttling = false;
bool disable_aggressive_throttling = false;
bool disable_back_forward_cache = false;
};
diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
index 9322ff7e3dc..b8e0b122816 100644
--- a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy_unittest.cc
@@ -112,11 +112,8 @@ class WorkerSchedulerProxyTest : public testing::Test {
task_environment_.GetMainThreadTaskRunner(),
task_environment_.GetMockTickClock()),
base::nullopt)),
- page_scheduler_(
- std::make_unique<PageSchedulerImpl>(nullptr,
- main_thread_scheduler_.get())),
- frame_scheduler_(FrameSchedulerImpl::Create(
- page_scheduler_.get(),
+ page_scheduler_(main_thread_scheduler_->CreatePageScheduler(nullptr)),
+ frame_scheduler_(page_scheduler_->CreateFrameScheduler(
nullptr,
nullptr,
FrameScheduler::FrameType::kMainFrame)) {}
@@ -130,8 +127,8 @@ class WorkerSchedulerProxyTest : public testing::Test {
protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<MainThreadSchedulerImpl> main_thread_scheduler_;
- std::unique_ptr<PageSchedulerImpl> page_scheduler_;
- std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
+ std::unique_ptr<PageScheduler> page_scheduler_;
+ std::unique_ptr<FrameScheduler> frame_scheduler_;
};
TEST_F(WorkerSchedulerProxyTest, VisibilitySignalReceived) {
diff --git a/chromium/third_party/blink/renderer/platform/supplementable.h b/chromium/third_party/blink/renderer/platform/supplementable.h
index dbbd63044f5..7c309d32fd7 100644
--- a/chromium/third_party/blink/renderer/platform/supplementable.h
+++ b/chromium/third_party/blink/renderer/platform/supplementable.h
@@ -147,7 +147,9 @@ class Supplement : public GarbageCollectedMixin {
: nullptr;
}
- void Trace(Visitor* visitor) override { visitor->Trace(supplementable_); }
+ void Trace(Visitor* visitor) const override {
+ visitor->Trace(supplementable_);
+ }
private:
Member<T> supplementable_;
@@ -199,7 +201,7 @@ class Supplementable : public GarbageCollectedMixin {
#endif
}
- void Trace(Visitor* visitor) override { visitor->Trace(supplements_); }
+ void Trace(Visitor* visitor) const override { visitor->Trace(supplements_); }
protected:
using SupplementMap =
diff --git a/chromium/third_party/blink/renderer/platform/testing/data/red-full-ranged-8bpc.avif b/chromium/third_party/blink/renderer/platform/testing/data/red-full-ranged-8bpc.avif
new file mode 100644
index 00000000000..9ca796d0d9f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/data/red-full-ranged-8bpc.avif
Binary files differ
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 e6692af38be..2e7f32aa668 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
@@ -29,6 +29,7 @@ class EmptyWebMediaPlayer : public WebMediaPlayer {
void SetRate(double) override {}
void SetVolume(double) override {}
void SetLatencyHint(double) override {}
+ void SetPreservesPitch(bool) override {}
void OnRequestPictureInPicture() override {}
void OnPictureInPictureAvailabilityChanged(bool available) override {}
SurfaceLayerMode GetVideoSurfaceLayerMode() const override {
diff --git a/chromium/third_party/blink/renderer/platform/testing/image_decode_to_nia.cc b/chromium/third_party/blink/renderer/platform/testing/image_decode_to_nia.cc
new file mode 100644
index 00000000000..54bcf234ed5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/testing/image_decode_to_nia.cc
@@ -0,0 +1,217 @@
+// 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.
+
+// This program converts an image from stdin (e.g. a JPEG, PNG, etc.) to stdout
+// (in the NIA/NIE format, a trivial image file format).
+//
+// The NIA/NIE file format specification is at:
+// https://github.com/google/wuffs/blob/master/doc/spec/nie-spec.md
+//
+// Pass "-1" or "-first-frame-only" as a command line flag to output NIE (a
+// still image) instead of NIA (an animated image). The output format (NIA or
+// NIE) depends only on this flag's absence or presence, not on the stdin
+// image's format.
+//
+// There are multiple codec implementations of any given image format. For
+// example, as of May 2020, Chromium, Skia and Wuffs each have their own BMP
+// decoder implementation. There is no standard "libbmp" that they all share.
+// Comparing this program's output (or hashed output) to similar programs in
+// other repositories can identify image inputs for which these decoders (or
+// different versions of the same decoder) produce different output (pixels).
+//
+// An equivalent program (using the Skia image codecs) is at:
+// https://skia-review.googlesource.com/c/skia/+/290618
+//
+// An equivalent program (using the Wuffs image codecs) is at:
+// https://github.com/google/wuffs/blob/master/example/convert-to-nia/convert-to-nia.c
+
+#include <iostream>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/task/single_thread_task_executor.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
+#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+static inline void set_u32le(uint8_t* ptr, uint32_t val) {
+ ptr[0] = val >> 0;
+ ptr[1] = val >> 8;
+ ptr[2] = val >> 16;
+ ptr[3] = val >> 24;
+}
+
+static inline void set_u64le(uint8_t* ptr, uint64_t val) {
+ ptr[0] = val >> 0;
+ ptr[1] = val >> 8;
+ ptr[2] = val >> 16;
+ ptr[3] = val >> 24;
+ ptr[4] = val >> 32;
+ ptr[5] = val >> 40;
+ ptr[6] = val >> 48;
+ ptr[7] = val >> 56;
+}
+
+void write_nix_header(uint32_t magic_u32le, uint32_t width, uint32_t height) {
+ uint8_t data[16];
+ set_u32le(data + 0, magic_u32le);
+ set_u32le(data + 4, 0x346E62FF); // 4 bytes per pixel non-premul BGRA.
+ set_u32le(data + 8, width);
+ set_u32le(data + 12, height);
+ fwrite(data, 1, 16, stdout);
+}
+
+bool write_nia_duration(uint64_t total_duration_micros) {
+ // Flicks are NIA's unit of time. One flick (frame-tick) is 1 / 705_600_000
+ // of a second. See https://github.com/OculusVR/Flicks
+ static constexpr uint64_t flicks_per_ten_micros = 7056;
+ uint64_t d = total_duration_micros / 10;
+ if (d > (INT64_MAX / flicks_per_ten_micros)) {
+ // Converting from micros to flicks would overflow.
+ return false;
+ }
+ d *= flicks_per_ten_micros;
+
+ uint8_t data[8];
+ set_u64le(data + 0, d);
+ fwrite(data, 1, 8, stdout);
+ return true;
+}
+
+void write_nie_pixels(uint32_t width,
+ uint32_t height,
+ blink::ImageFrame* frame) {
+ static constexpr size_t kBufferSize = 4096;
+ uint8_t buf[kBufferSize];
+ size_t n = 0;
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ uint32_t pix = *(frame->GetAddr(x, y));
+ buf[n++] = pix >> SK_B32_SHIFT;
+ buf[n++] = pix >> SK_G32_SHIFT;
+ buf[n++] = pix >> SK_R32_SHIFT;
+ buf[n++] = pix >> SK_A32_SHIFT;
+ if (n == kBufferSize) {
+ fwrite(buf, 1, n, stdout);
+ n = 0;
+ }
+ }
+ }
+ if (n > 0) {
+ fwrite(buf, 1, n, stdout);
+ }
+}
+
+void write_nia_padding(uint32_t width, uint32_t height) {
+ // 4 bytes of padding when the width and height are both odd.
+ if (width & height & 1) {
+ uint8_t data[4];
+ set_u32le(data + 0, 0);
+ fwrite(data, 1, 4, stdout);
+ }
+}
+
+void write_nia_footer(int repetition_count) {
+ uint8_t data[8];
+ // kAnimationNone means a still image.
+ if ((repetition_count == blink::kAnimationNone) ||
+ (repetition_count == blink::kAnimationLoopInfinite)) {
+ set_u32le(data + 0, 0);
+ } else {
+ // NIA's loop count and Chromium/Skia's repetition count differ by one. See
+ // https://github.com/google/wuffs/blob/master/doc/spec/nie-spec.md#nii-footer
+ set_u32le(data + 0, 1 + repetition_count);
+ }
+ set_u32le(data + 4, 0x80000000);
+ fwrite(data, 1, 8, stdout);
+}
+
+int main(int argc, char* argv[]) {
+ base::SingleThreadTaskExecutor main_task_executor;
+ base::CommandLine::Init(argc, argv);
+ std::unique_ptr<blink::Platform> platform =
+ std::make_unique<blink::Platform>();
+ blink::Platform::CreateMainThreadAndInitialize(platform.get());
+
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ bool first_frame_only = command_line->HasSwitch("1") ||
+ command_line->HasSwitch("first-frame-only");
+
+ std::string src;
+ if (!base::ReadStreamToString(stdin, &src)) {
+ std::cerr << "could not read stdin\n";
+ return 1;
+ }
+ static constexpr bool data_complete = true;
+ std::unique_ptr<blink::ImageDecoder> decoder = blink::ImageDecoder::Create(
+ WTF::SharedBuffer::Create(src.data(), src.size()), data_complete,
+ blink::ImageDecoder::kAlphaNotPremultiplied,
+ blink::ImageDecoder::kDefaultBitDepth, blink::ColorBehavior::Ignore());
+
+ const size_t frame_count = decoder->FrameCount();
+ if (frame_count == 0) {
+ std::cerr << "no frames\n";
+ return 1;
+ }
+
+ int image_width;
+ int image_height;
+ uint64_t total_duration_micros = 0;
+ for (size_t i = 0; i < frame_count; i++) {
+ blink::ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(i);
+ if (!frame) {
+ std::cerr << "could not decode frame #" << i << "\n";
+ return 1;
+ }
+ if (frame->GetPixelFormat() != blink::ImageFrame::kN32) {
+ std::cerr << "unsupported pixel format\n";
+ return 1;
+ }
+ const int frame_width = decoder->Size().Width();
+ const int frame_height = decoder->Size().Height();
+ if ((frame_width < 0) || (frame_height < 0)) {
+ std::cerr << "negative dimension\n";
+ return 1;
+ }
+ int64_t duration_micros = decoder->FrameDurationAtIndex(i).InMicroseconds();
+ if (duration_micros < 0) {
+ std::cerr << "negative animation duration\n";
+ return 1;
+ }
+ total_duration_micros += static_cast<uint64_t>(duration_micros);
+ if (total_duration_micros > INT64_MAX) {
+ std::cerr << "unsupported animation duration\n";
+ return 1;
+ }
+
+ if (!first_frame_only) {
+ if (i == 0) {
+ image_width = frame_width;
+ image_height = frame_height;
+ write_nix_header(0x41AFC36E, // "nïA" magic string as a u32le.
+ frame_width, frame_height);
+ } else if ((image_width != frame_width) ||
+ (image_height != frame_height)) {
+ std::cerr << "non-constant animation dimensions\n";
+ return 1;
+ }
+
+ if (!write_nia_duration(total_duration_micros)) {
+ std::cerr << "unsupported animation duration\n";
+ return 1;
+ }
+ }
+
+ write_nix_header(0x45AFC36E, // "nïE" magic string as a u32le.
+ frame_width, frame_height);
+ write_nie_pixels(frame_width, frame_height, frame);
+ if (first_frame_only) {
+ return 0;
+ }
+ write_nia_padding(frame_width, frame_height);
+ }
+ write_nia_footer(decoder->RepetitionCount());
+ return 0;
+}
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 6da9d8037a9..ce0e72401d1 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
@@ -72,13 +72,11 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateFilterEffect(
const TransformPaintPropertyNode& local_transform_space,
const ClipPaintPropertyNode* output_clip,
CompositorFilterOperations filter,
- const FloatPoint& filters_origin = FloatPoint(),
CompositingReasons compositing_reasons = CompositingReason::kNone) {
EffectPaintPropertyNode::State state;
state.local_transform_space = &local_transform_space;
state.output_clip = output_clip;
state.filter = std::move(filter);
- state.filters_origin = filters_origin;
state.direct_compositing_reasons = compositing_reasons;
state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
NewUniqueObjectId(), CompositorElementIdNamespace::kEffectFilter);
@@ -88,10 +86,9 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateFilterEffect(
inline scoped_refptr<EffectPaintPropertyNode> CreateFilterEffect(
const EffectPaintPropertyNode& parent,
CompositorFilterOperations filter,
- const FloatPoint& paint_offset = FloatPoint(),
CompositingReasons compositing_reasons = CompositingReason::kNone) {
return CreateFilterEffect(parent, parent.Unalias().LocalTransformSpace(),
- parent.Unalias().OutputClip(), filter, paint_offset,
+ parent.Unalias().OutputClip(), filter,
compositing_reasons);
}
@@ -114,13 +111,11 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect(
const EffectPaintPropertyNode& parent,
const TransformPaintPropertyNode& local_transform_space,
const ClipPaintPropertyNode* output_clip,
- CompositorFilterOperations backdrop_filter,
- const FloatPoint& filters_origin = FloatPoint()) {
+ CompositorFilterOperations backdrop_filter) {
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 = CompositingReason::kBackdropFilter;
state.compositor_element_id = CompositorElementIdFromUniqueObjectId(
NewUniqueObjectId(), CompositorElementIdNamespace::kPrimary);
@@ -129,11 +124,10 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect(
inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect(
const EffectPaintPropertyNode& parent,
- CompositorFilterOperations backdrop_filter,
- const FloatPoint& paint_offset = FloatPoint()) {
+ CompositorFilterOperations backdrop_filter) {
return CreateBackdropFilterEffect(
parent, parent.Unalias().LocalTransformSpace(),
- parent.Unalias().OutputClip(), backdrop_filter, paint_offset);
+ parent.Unalias().OutputClip(), backdrop_filter);
}
inline scoped_refptr<EffectPaintPropertyNode>
diff --git a/chromium/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc b/chromium/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc
index 8aa156cd53c..a96ee82a26d 100644
--- a/chromium/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc
+++ b/chromium/third_party/blink/renderer/platform/testing/scoped_scheduler_overrider.cc
@@ -18,6 +18,10 @@ class ThreadWithCustomScheduler : public Thread {
ThreadScheduler* Scheduler() override { return scheduler_; }
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override {
+ return scheduler_->DeprecatedDefaultTaskRunner();
+ }
+
private:
ThreadScheduler* scheduler_;
};
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 742dd1a742c..bf30ad05fe6 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
@@ -82,8 +82,7 @@ TestPaintArtifact& TestPaintArtifact::ForeignLayer(
const FloatPoint& offset) {
DEFINE_STATIC_LOCAL(LiteralDebugNameClient, client, ("ForeignLayer"));
display_item_list_.AllocateAndConstruct<ForeignLayerDisplayItem>(
- client, DisplayItem::kForeignLayerFirst, std::move(layer), offset,
- nullptr);
+ client, DisplayItem::kForeignLayerFirst, std::move(layer), offset);
DidAddDisplayItem();
return *this;
}
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 7464c9cd52f..b0b6c3e8d71 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
@@ -107,10 +107,6 @@ WebString TestingPlatformSupport::DefaultLocale() {
return WebString::FromUTF8("en-US");
}
-WebURLLoaderMockFactory* TestingPlatformSupport::GetURLLoaderMockFactory() {
- return old_platform_ ? old_platform_->GetURLLoaderMockFactory() : nullptr;
-}
-
std::unique_ptr<WebURLLoaderFactory>
TestingPlatformSupport::CreateDefaultURLLoaderFactory() {
return old_platform_ ? old_platform_->CreateDefaultURLLoaderFactory()
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 17ea8b4e183..fa73be89b5a 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
@@ -61,7 +61,6 @@ class TestingPlatformSupport : public Platform {
// Platform:
WebString DefaultLocale() override;
- WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
std::unique_ptr<blink::WebURLLoaderFactory> CreateDefaultURLLoaderFactory()
override;
std::unique_ptr<CodeCacheLoader> CreateCodeCacheLoader() override {
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 c9ad5a443ff..15da0476d4b 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
@@ -63,7 +63,8 @@ WebURL RegisterMockedURLLoadFromBase(const WebString& base_url,
void RegisterMockedURLLoad(const WebURL& full_url,
const WebString& file_path,
- const WebString& mime_type) {
+ const WebString& mime_type,
+ WebURLLoaderMockFactory* mock_factory) {
network::mojom::LoadTimingInfoPtr timing =
network::mojom::LoadTimingInfo::New();
@@ -73,10 +74,11 @@ void RegisterMockedURLLoad(const WebURL& full_url,
response.SetHttpStatusCode(200);
response.SetLoadTiming(*timing);
- RegisterMockedURLLoadWithCustomResponse(full_url, file_path, response);
+ mock_factory->RegisterURL(full_url, response, file_path);
}
-void RegisterMockedErrorURLLoad(const WebURL& full_url) {
+void RegisterMockedErrorURLLoad(const WebURL& full_url,
+ WebURLLoaderMockFactory* mock_factory) {
network::mojom::LoadTimingInfoPtr timing =
network::mojom::LoadTimingInfo::New();
@@ -87,33 +89,31 @@ void RegisterMockedErrorURLLoad(const WebURL& full_url) {
response.SetLoadTiming(*timing);
ResourceError error = ResourceError::Failure(full_url);
- Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL(
- full_url, response, WebURLError(error));
+ mock_factory->RegisterErrorURL(full_url, response, WebURLError(error));
}
void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
const WebString& file_path,
WebURLResponse response) {
- Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+ WebURLLoaderMockFactory::GetSingletonInstance()->RegisterURL(
full_url, response, file_path);
}
void RegisterMockedURLUnregister(const WebURL& url) {
- Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url);
+ WebURLLoaderMockFactory::GetSingletonInstance()->UnregisterURL(url);
}
void UnregisterAllURLsAndClearMemoryCache() {
- Platform::Current()
- ->GetURLLoaderMockFactory()
+ WebURLLoaderMockFactory::GetSingletonInstance()
->UnregisterAllURLsAndClearMemoryCache();
}
void SetLoaderDelegate(WebURLLoaderTestDelegate* delegate) {
- Platform::Current()->GetURLLoaderMockFactory()->SetLoaderDelegate(delegate);
+ WebURLLoaderMockFactory::GetSingletonInstance()->SetLoaderDelegate(delegate);
}
void ServeAsynchronousRequests() {
- Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ WebURLLoaderMockFactory::GetSingletonInstance()->ServeAsynchronousRequests();
}
} // namespace url_test_helpers
diff --git a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h
index 2edd24fe5aa..d46c7537955 100644
--- a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h
+++ b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.h
@@ -72,7 +72,9 @@ WebURL RegisterMockedURLLoadFromBase(
void RegisterMockedURLLoad(
const WebURL& full_url,
const WebString& file_path,
- const WebString& mime_type = WebString::FromUTF8("text/html"));
+ const WebString& mime_type = WebString::FromUTF8("text/html"),
+ WebURLLoaderMockFactory* mock_factory =
+ WebURLLoaderMockFactory::GetSingletonInstance());
// Unregisters a URL that has been registered, so that the same URL can be
// registered again from the another test.
@@ -84,7 +86,10 @@ void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
WebURLResponse);
// Registers a mock URL that returns a 404 error.
-void RegisterMockedErrorURLLoad(const WebURL& full_url);
+void RegisterMockedErrorURLLoad(
+ const WebURL& full_url,
+ WebURLLoaderMockFactory* mock_factory =
+ WebURLLoaderMockFactory::GetSingletonInstance());
void UnregisterAllURLsAndClearMemoryCache();
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 c8d802cd025..7d2cd4bb50c 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
@@ -26,11 +26,14 @@
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
-std::unique_ptr<WebURLLoaderMockFactory> WebURLLoaderMockFactory::Create() {
- return base::WrapUnique(new WebURLLoaderMockFactoryImpl(nullptr));
+// static
+WebURLLoaderMockFactory* WebURLLoaderMockFactory::GetSingletonInstance() {
+ DEFINE_STATIC_LOCAL(WebURLLoaderMockFactoryImpl, s_singleton, (nullptr));
+ return &s_singleton;
}
WebURLLoaderMockFactoryImpl::WebURLLoaderMockFactoryImpl(
diff --git a/chromium/third_party/blink/renderer/platform/text/date_components.cc b/chromium/third_party/blink/renderer/platform/text/date_components.cc
index f44343e2443..2ab5c289450 100644
--- a/chromium/third_party/blink/renderer/platform/text/date_components.cc
+++ b/chromium/third_party/blink/renderer/platform/text/date_components.cc
@@ -31,6 +31,7 @@
#include "third_party/blink/renderer/platform/text/date_components.h"
#include <limits.h>
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/date_math.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
diff --git a/chromium/third_party/blink/renderer/platform/text/layout_locale.cc b/chromium/third_party/blink/renderer/platform/text/layout_locale.cc
index 3bb46ed6abf..193803edaae 100644
--- a/chromium/third_party/blink/renderer/platform/text/layout_locale.cc
+++ b/chromium/third_party/blink/renderer/platform/text/layout_locale.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/platform/text/layout_locale.h"
#include "base/compiler_specific.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/language.h"
#include "third_party/blink/renderer/platform/text/hyphenation.h"
#include "third_party/blink/renderer/platform/text/icu_error.h"
diff --git a/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h
index 8ba77102119..74abbef499b 100644
--- a/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h
+++ b/chromium/third_party/blink/renderer/platform/text/locale_to_script_mapping.h
@@ -31,6 +31,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_TO_SCRIPT_MAPPING_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_LOCALE_TO_SCRIPT_MAPPING_H_
+#include "base/check.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
diff --git a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
index a257cd75ccf..5bea0079dd3 100644
--- a/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
+++ b/chromium/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
@@ -30,6 +30,7 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/text/icu_error.h"
#include "third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
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 e92eed0ad8a..6b459ad98e5 100644
--- a/chromium/third_party/blink/renderer/platform/text/text_direction.h
+++ b/chromium/third_party/blink/renderer/platform/text/text_direction.h
@@ -29,7 +29,7 @@
#include <cstdint>
#include <iosfwd>
#include "base/i18n/rtl.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/platform_export.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.cc b/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.cc
new file mode 100644
index 00000000000..7a2f730ea4e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.cc
@@ -0,0 +1,15 @@
+// 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/text/writing_direction_mode.h"
+
+namespace blink {
+
+std::ostream& operator<<(std::ostream& ostream,
+ const WritingDirectionMode& writing_direction) {
+ return ostream << writing_direction.GetWritingMode() << " "
+ << writing_direction.Direction();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.h b/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.h
new file mode 100644
index 00000000000..710beb7f056
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_direction_mode.h
@@ -0,0 +1,77 @@
+// 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_TEXT_WRITING_DIRECTION_MODE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_DIRECTION_MODE_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/text/writing_mode.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+// This class packs |WritingMode| and |TextDirection|, two enums that are often
+// used and passed around together, into the size of the minimum memory align.
+class PLATFORM_EXPORT WritingDirectionMode {
+ DISALLOW_NEW();
+
+ public:
+ WritingDirectionMode(WritingMode writing_mode, TextDirection direction)
+ : writing_mode_(writing_mode), direction_(direction) {}
+
+ //
+ // Inline direction functions.
+ //
+ TextDirection Direction() const { return direction_; }
+ void SetDirection(TextDirection direction) { direction_ = direction; }
+
+ bool IsLtr() const { return blink::IsLtr(direction_); }
+ bool IsRtl() const { return blink::IsRtl(direction_); }
+
+ //
+ // Block direction functions.
+ //
+ WritingMode GetWritingMode() const { return writing_mode_; }
+ void SetWritingMode(WritingMode writing_mode) {
+ writing_mode_ = writing_mode;
+ }
+
+ bool IsHorizontal() const { return IsHorizontalWritingMode(writing_mode_); }
+
+ // Block progression increases in the opposite direction to normal; modes
+ // vertical-rl.
+ bool IsFlippedBlocks() const {
+ return IsFlippedBlocksWritingMode(writing_mode_);
+ }
+
+ // Bottom of the line occurs earlier in the block; modes vertical-lr.
+ bool IsFlippedLines() const {
+ return IsFlippedLinesWritingMode(writing_mode_);
+ }
+
+ //
+ // Functions for both inline and block directions.
+ //
+ bool IsHorizontalLtr() const { return IsHorizontal() && IsLtr(); }
+
+ bool operator==(const WritingDirectionMode& other) const {
+ return writing_mode_ == other.writing_mode_ &&
+ direction_ == other.direction_;
+ }
+ bool operator!=(const WritingDirectionMode& other) const {
+ return !operator==(other);
+ }
+
+ private:
+ WritingMode writing_mode_;
+ TextDirection direction_;
+};
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
+ const WritingDirectionMode&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_DIRECTION_MODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_mode.cc b/chromium/third_party/blink/renderer/platform/text/writing_mode.cc
new file mode 100644
index 00000000000..9b553c70976
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/text/writing_mode.cc
@@ -0,0 +1,27 @@
+// 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/text/writing_mode.h"
+
+#include <ostream>
+
+namespace blink {
+
+std::ostream& operator<<(std::ostream& ostream, WritingMode writing_mode) {
+ switch (writing_mode) {
+ case WritingMode::kHorizontalTb:
+ return ostream << "horizontal-tb";
+ case WritingMode::kVerticalRl:
+ return ostream << "vertical-rl";
+ case WritingMode::kVerticalLr:
+ return ostream << "vertical-lr";
+ case WritingMode::kSidewaysRl:
+ return ostream << "sideways-rl";
+ case WritingMode::kSidewaysLr:
+ return ostream << "sideways-lr";
+ }
+ return ostream << static_cast<unsigned>(writing_mode);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/text/writing_mode.h b/chromium/third_party/blink/renderer/platform/text/writing_mode.h
index bbe414bc241..eead44672ac 100644
--- a/chromium/third_party/blink/renderer/platform/text/writing_mode.h
+++ b/chromium/third_party/blink/renderer/platform/text/writing_mode.h
@@ -31,13 +31,17 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
+#include <cstdint>
+#include <iosfwd>
+#include "third_party/blink/renderer/platform/platform_export.h"
+
namespace blink {
// These values are named to match the CSS keywords they correspond to: namely
// horizontal-tb, vertical-rl and vertical-lr.
// Since these names aren't very self-explanatory, where possible use the
// inline utility functions below.
-enum class WritingMode : unsigned {
+enum class WritingMode : uint8_t {
kHorizontalTb = 0,
kVerticalRl = 1,
kVerticalLr = 2,
@@ -78,6 +82,8 @@ inline bool IsParallelWritingMode(WritingMode a, WritingMode b) {
return (a == WritingMode::kHorizontalTb) == (b == WritingMode::kHorizontalTb);
}
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, WritingMode);
+
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_WRITING_MODE_H_
diff --git a/chromium/third_party/blink/renderer/platform/timer_test.cc b/chromium/third_party/blink/renderer/platform/timer_test.cc
index f4758a300e2..925ea2ce201 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(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
private:
void Fired(TimerBase*) {
diff --git a/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
index 2e7f4a03833..637cdfdbcac 100644
--- a/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
+++ b/chromium/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
@@ -338,7 +338,7 @@ static bool Inverse(const TransformationMatrix::Matrix4& matrix,
: [mat] "+r"(mat), [pr] "+r"(pr)
: [rdet] "r"(rdet)
: "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16", "v17",
- "v18", "v19", "v20", "v21", "v22", "v23", "24", "25", "v26", "v27",
+ "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
"v28", "v29", "v30");
#elif defined(HAVE_MIPS_MSA_INTRINSICS)
const double rDet = 1 / det;
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 4be9b4f4f6a..8540933a29b 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
@@ -161,10 +161,8 @@ struct VideoCaptureImpl::BufferContext
mailbox_holder_array,
base::BindOnce(&BufferContext::MailboxHolderReleased, buffer_context),
info->timestamp);
- frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY,
- true);
- frame->metadata()->SetBoolean(
- media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
+ frame->metadata()->allow_overlay = true;
+ frame->metadata()->read_lock_fences_enabled = true;
std::move(on_texture_bound)
.Run(std::move(info), std::move(frame), std::move(buffer_context));
@@ -510,12 +508,7 @@ void VideoCaptureImpl::OnBufferReady(
return;
}
- base::TimeTicks reference_time;
- media::VideoFrameMetadata frame_metadata;
- frame_metadata.MergeInternalValuesFrom(info->metadata);
- const bool success = frame_metadata.GetTimeTicks(
- media::VideoFrameMetadata::REFERENCE_TIME, &reference_time);
- DCHECK(success);
+ base::TimeTicks reference_time = *info->metadata.reference_time;
if (first_frame_ref_time_.is_null()) {
first_frame_ref_time_ = reference_time;
@@ -621,7 +614,8 @@ void VideoCaptureImpl::OnBufferReady(
gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
buffer_context->TakeGpuMemoryBufferHandle(),
gfx::Size(info->coded_size), gfx_format,
- gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, base::DoNothing());
+ gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
+ base::DoNothing());
buffer_context->SetGpuMemoryBuffer(std::move(gmb));
}
CHECK(buffer_context->GetGpuMemoryBuffer());
@@ -632,7 +626,8 @@ void VideoCaptureImpl::OnBufferReady(
buffer_context->GetGpuMemoryBuffer()->CloneHandle(),
buffer_context->GetGpuMemoryBuffer()->GetSize(),
buffer_context->GetGpuMemoryBuffer()->GetFormat(),
- gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, base::DoNothing());
+ gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE,
+ base::DoNothing());
media_task_runner_->PostTask(
FROM_HERE,
@@ -673,7 +668,8 @@ void VideoCaptureImpl::OnVideoFrameReady(
if (info->color_space.has_value() && info->color_space->IsValid())
frame->set_color_space(info->color_space.value());
- frame->metadata()->MergeInternalValuesFrom(info->metadata);
+ media::VideoFrameMetadata metadata = info->metadata;
+ frame->metadata()->MergeMetadataFrom(&metadata);
// TODO(qiangchen): Dive into the full code path to let frame metadata hold
// reference time rather than using an extra parameter.
@@ -803,12 +799,8 @@ void VideoCaptureImpl::DidFinishConsumingFrame(
BufferFinishedCallback callback_to_io_thread) {
// Note: This function may be called on any thread by the VideoFrame
// destructor. |metadata| is still valid for read-access at this point.
- double consumer_resource_utilization = -1.0;
- if (!metadata->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION,
- &consumer_resource_utilization)) {
- consumer_resource_utilization = -1.0;
- }
- std::move(callback_to_io_thread).Run(consumer_resource_utilization);
+ std::move(callback_to_io_thread)
+ .Run(metadata->resource_utilization.value_or(-1.0));
}
} // namespace blink
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 650f0da130d..d261dcba734 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
@@ -193,15 +193,14 @@ class VideoCaptureImplTest : public ::testing::Test {
media::mojom::blink::VideoFrameInfo::New();
const base::TimeTicks now = base::TimeTicks::Now();
- media::VideoFrameMetadata frame_metadata;
- frame_metadata.SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME, now);
- info->metadata = frame_metadata.GetInternalValues().Clone();
-
+ media::VideoFrameMetadata metadata;
+ metadata.reference_time = now;
info->timestamp = now - base::TimeTicks();
info->pixel_format = pixel_format;
info->coded_size = size;
info->visible_rect = gfx::Rect(size);
info->color_space = gfx::ColorSpace();
+ info->metadata = metadata;
video_capture_impl_->OnBufferReady(buffer_id, std::move(info));
}
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc
index ce96d1bef63..4065b75d003 100644
--- a/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -30,6 +30,7 @@
#include <algorithm>
#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.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/blink/renderer/platform/wtf/text/string_hash.h"
@@ -126,19 +127,21 @@ bool IsValidProtocol(const String& protocol) {
return true;
}
-String KURL::StrippedForUseAsReferrer() const {
- if (!ProtocolIsInHTTPFamily())
- return String();
+KURL KURL::UrlStrippedForUseAsReferrer() const {
+ if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(Protocol()))
+ return KURL();
- if (parsed_.username.is_nonempty() || parsed_.password.is_nonempty() ||
- parsed_.ref.is_valid()) {
- KURL referrer(*this);
- referrer.SetUser(String());
- referrer.SetPass(String());
- referrer.RemoveFragmentIdentifier();
- return referrer.GetString();
- }
- return GetString();
+ KURL referrer(*this);
+
+ referrer.SetUser(String());
+ referrer.SetPass(String());
+ referrer.RemoveFragmentIdentifier();
+
+ return referrer;
+}
+
+String KURL::StrippedForUseAsReferrer() const {
+ return UrlStrippedForUseAsReferrer().GetString();
}
String KURL::StrippedForUseAsHref() const {
diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.h b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h
index 8ddb31a682b..4e944bc2c4b 100644
--- a/chromium/third_party/blink/renderer/platform/weborigin/kurl.h
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h
@@ -114,6 +114,7 @@ class PLATFORM_EXPORT KURL {
~KURL();
+ KURL UrlStrippedForUseAsReferrer() const;
String StrippedForUseAsReferrer() const;
String StrippedForUseAsHref() const;
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 94a471da823..f1d7bcf850b 100644
--- a/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc
+++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc
@@ -38,6 +38,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/weborigin/scheme_registry.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"
@@ -850,27 +851,63 @@ TEST(KURLTest, ProtocolIs) {
EXPECT_EQ(capital.Protocol(), "http");
}
+TEST(KURLTest, urlStrippedForUseAsReferrer) {
+ struct ReferrerCase {
+ const String input;
+ const String output;
+ } referrer_cases[] = {
+ {"data:text/html;charset=utf-8,<html></html>", String()},
+ {"javascript:void(0);", String()},
+ {"about:config", String()},
+ {"https://www.google.com/", "https://www.google.com/"},
+ {"http://me@news.google.com:8888/", "http://news.google.com:8888/"},
+ {"http://:pass@news.google.com:8888/foo",
+ "http://news.google.com:8888/foo"},
+ {"http://me:pass@news.google.com:8888/", "http://news.google.com:8888/"},
+ {"https://www.google.com/a?f#b", "https://www.google.com/a?f"},
+ {"file:///tmp/test.html", String()},
+ {"https://www.google.com/#", "https://www.google.com/"},
+ };
+
+ for (const ReferrerCase& referrer_case : referrer_cases) {
+ const KURL kurl(referrer_case.input);
+ EXPECT_EQ(KURL(referrer_case.output), kurl.UrlStrippedForUseAsReferrer());
+ }
+}
+
+TEST(KURLTest, urlStrippedForUseAsReferrerRespectsReferrerScheme) {
+ const KURL example_http_url = KURL("http://example.com/");
+ const KURL foobar_url = KURL("foobar://somepage/");
+ const String foobar_scheme = String::FromUTF8("foobar");
+
+ EXPECT_EQ("", foobar_url.StrippedForUseAsReferrer().Utf8());
+
+ SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(foobar_scheme);
+ EXPECT_EQ("foobar://somepage/", foobar_url.StrippedForUseAsReferrer());
+ SchemeRegistry::RemoveURLSchemeAsAllowedForReferrer(foobar_scheme);
+}
+
TEST(KURLTest, strippedForUseAsReferrer) {
struct ReferrerCase {
const char* input;
- const char* output;
+ const String output;
} referrer_cases[] = {
- {"data:text/html;charset=utf-8,<html></html>", ""},
- {"javascript:void(0);", ""},
- {"about:config", ""},
+ {"data:text/html;charset=utf-8,<html></html>", String()},
+ {"javascript:void(0);", String()},
+ {"about:config", String()},
{"https://www.google.com/", "https://www.google.com/"},
{"http://me@news.google.com:8888/", "http://news.google.com:8888/"},
{"http://:pass@news.google.com:8888/foo",
"http://news.google.com:8888/foo"},
{"http://me:pass@news.google.com:8888/", "http://news.google.com:8888/"},
{"https://www.google.com/a?f#b", "https://www.google.com/a?f"},
- {"file:///tmp/test.html", ""},
+ {"file:///tmp/test.html", String()},
{"https://www.google.com/#", "https://www.google.com/"},
};
- for (size_t i = 0; i < base::size(referrer_cases); i++) {
- const KURL kurl(referrer_cases[i].input);
- EXPECT_EQ(referrer_cases[i].output, kurl.StrippedForUseAsReferrer().Utf8());
+ for (const ReferrerCase& referrer_case : referrer_cases) {
+ const KURL kurl(referrer_case.input);
+ EXPECT_EQ(referrer_case.output, kurl.StrippedForUseAsReferrer());
}
}
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 a1d26ce91c1..3ce6e47dc30 100644
--- a/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -112,63 +112,56 @@ Referrer SecurityPolicy::GenerateReferrer(
return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
DCHECK(!referrer.IsEmpty());
- KURL referrer_url = KURL(NullURL(), referrer);
- String scheme = referrer_url.Protocol();
- if (!SchemeRegistry::ShouldTreatURLSchemeAsAllowedForReferrer(scheme))
+ KURL referrer_url = KURL(NullURL(), referrer).UrlStrippedForUseAsReferrer();
+
+ if (!referrer_url.IsValid())
return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
if (SecurityOrigin::ShouldUseInnerURL(url))
return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
+ // 5. Let referrerOrigin be the result of stripping referrerSource for use as
+ // a referrer, with the origin-only flag set to true.
+ KURL referrer_origin = referrer_url;
+ referrer_origin.SetPath(String());
+ referrer_origin.SetQuery(String());
+
+ // 6. If the result of serializing referrerURL is a string whose length is
+ // greater than 4096, set referrerURL to referrerOrigin.
+ if (referrer_url.GetString().length() > 4096)
+ referrer_url = referrer_origin;
+
switch (referrer_policy_no_default) {
case network::mojom::ReferrerPolicy::kNever:
return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
case network::mojom::ReferrerPolicy::kAlways:
- return Referrer(referrer, referrer_policy_no_default);
+ return Referrer(referrer_url, referrer_policy_no_default);
case network::mojom::ReferrerPolicy::kOrigin: {
- String origin = SecurityOrigin::Create(referrer_url)->ToString();
- // A security origin is not a canonical URL as it lacks a path. Add /
- // to turn it into a canonical URL we can use as referrer.
- return Referrer(origin + "/", referrer_policy_no_default);
+ return Referrer(referrer_origin, referrer_policy_no_default);
}
case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: {
- scoped_refptr<const SecurityOrigin> referrer_origin =
- SecurityOrigin::Create(referrer_url);
- scoped_refptr<const SecurityOrigin> url_origin =
- SecurityOrigin::Create(url);
- if (!url_origin->IsSameOriginWith(referrer_origin.get())) {
- String origin = referrer_origin->ToString();
- return Referrer(origin + "/", referrer_policy_no_default);
+ if (!SecurityOrigin::AreSameOrigin(referrer_url, url)) {
+ return Referrer(referrer_origin, referrer_policy_no_default);
}
break;
}
case network::mojom::ReferrerPolicy::kSameOrigin: {
- scoped_refptr<const SecurityOrigin> referrer_origin =
- SecurityOrigin::Create(referrer_url);
- scoped_refptr<const SecurityOrigin> url_origin =
- SecurityOrigin::Create(url);
- if (!url_origin->IsSameOriginWith(referrer_origin.get())) {
+ if (!SecurityOrigin::AreSameOrigin(referrer_url, url)) {
return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
}
- return Referrer(referrer, referrer_policy_no_default);
+ return Referrer(referrer_url, referrer_policy_no_default);
}
case network::mojom::ReferrerPolicy::kStrictOrigin: {
- String origin = SecurityOrigin::Create(referrer_url)->ToString();
return Referrer(ShouldHideReferrer(url, referrer_url)
? Referrer::NoReferrer()
- : origin + "/",
+ : referrer_origin,
referrer_policy_no_default);
}
case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: {
- scoped_refptr<const SecurityOrigin> referrer_origin =
- SecurityOrigin::Create(referrer_url);
- scoped_refptr<const SecurityOrigin> url_origin =
- SecurityOrigin::Create(url);
- if (!url_origin->IsSameOriginWith(referrer_origin.get())) {
- String origin = referrer_origin->ToString();
+ if (!SecurityOrigin::AreSameOrigin(referrer_url, url)) {
return Referrer(ShouldHideReferrer(url, referrer_url)
? Referrer::NoReferrer()
- : origin + "/",
+ : referrer_origin,
referrer_policy_no_default);
}
break;
@@ -180,9 +173,9 @@ Referrer SecurityPolicy::GenerateReferrer(
break;
}
- return Referrer(
- ShouldHideReferrer(url, referrer_url) ? Referrer::NoReferrer() : referrer,
- referrer_policy_no_default);
+ return Referrer(ShouldHideReferrer(url, referrer_url) ? Referrer::NoReferrer()
+ : referrer_url,
+ referrer_policy_no_default);
}
void SecurityPolicy::AddOriginToTrustworthySafelist(
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 ecc706ea8ea..10e733487c9 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
@@ -101,6 +101,7 @@ TEST(SecurityPolicyTest, GenerateReferrer) {
const char kBlobURL[] =
"blob:http://a.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde";
const char kFilesystemURL[] = "filesystem:http://a.test/path/t/file.html";
+ const char kInvalidURL[] = "not-a-valid-url";
bool reduced_granularity =
RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled();
@@ -226,7 +227,7 @@ TEST(SecurityPolicyTest, GenerateReferrer) {
{network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin,
kSecureURLA, kInsecureURLB, nullptr},
- // blob and filesystem URL handling
+ // blob, filesystem, and invalid URL handling
{network::mojom::ReferrerPolicy::kAlways, kInsecureURLA, kBlobURL,
nullptr},
{network::mojom::ReferrerPolicy::kAlways, kBlobURL, kInsecureURLA,
@@ -235,6 +236,10 @@ TEST(SecurityPolicyTest, GenerateReferrer) {
nullptr},
{network::mojom::ReferrerPolicy::kAlways, kFilesystemURL, kInsecureURLA,
nullptr},
+ {network::mojom::ReferrerPolicy::kAlways, kInsecureURLA, kInvalidURL,
+ kInsecureURLA},
+ {network::mojom::ReferrerPolicy::kAlways, kInvalidURL, kInsecureURLA,
+ nullptr},
};
for (TestCase test : inputs) {
@@ -244,7 +249,8 @@ TEST(SecurityPolicyTest, GenerateReferrer) {
if (test.expected) {
EXPECT_EQ(String::FromUTF8(test.expected), result.referrer)
<< "'" << test.referrer << "' to '" << test.destination
- << "' should have been '" << test.expected << "': was '"
+ << "' with policy=" << static_cast<int>(test.policy)
+ << " should have been '" << test.expected << "': was '"
<< result.referrer.Utf8() << "'.";
} else {
EXPECT_TRUE(result.referrer.IsEmpty())
@@ -267,6 +273,42 @@ TEST(SecurityPolicyTest, GenerateReferrer) {
}
}
+TEST(SecurityPolicyTest, GenerateReferrerTruncatesLongUrl) {
+ char buffer[4097];
+ std::fill_n(std::begin(buffer), 4097, 'a');
+
+ String base = "https://a.com/";
+ String string_with_4096 = base + String(buffer, 4096 - base.length());
+ ASSERT_EQ(string_with_4096.length(), 4096u);
+
+ network::mojom::ReferrerPolicy kAlways =
+ network::mojom::ReferrerPolicy::kAlways;
+ EXPECT_EQ(SecurityPolicy::GenerateReferrer(
+ kAlways, KURL("https://destination.example"), string_with_4096)
+ .referrer,
+ string_with_4096);
+
+ String string_with_4097 = base + String(buffer, 4097 - base.length());
+ ASSERT_EQ(string_with_4097.length(), 4097u);
+ EXPECT_EQ(SecurityPolicy::GenerateReferrer(
+ kAlways, KURL("https://destination.example"), string_with_4097)
+ .referrer,
+ "https://a.com/");
+
+ // Since refs get stripped from outgoing referrers prior to the "if the length
+ // is greater than 4096, strip the referrer to its origin" check, a
+ // referrer with length > 4096 due to its path should not get stripped to its
+ // outgoing origin.
+ String string_with_4097_because_of_long_ref =
+ base + "path#" + String(buffer, 4097 - 5 - base.length());
+ ASSERT_EQ(string_with_4097_because_of_long_ref.length(), 4097u);
+ EXPECT_EQ(SecurityPolicy::GenerateReferrer(
+ kAlways, KURL("https://destination.example"),
+ string_with_4097_because_of_long_ref)
+ .referrer,
+ "https://a.com/path");
+}
+
TEST(SecurityPolicyTest, ReferrerPolicyFromHeaderValue) {
struct TestCase {
const char* header;
diff --git a/chromium/third_party/blink/renderer/platform/widget/DEPS b/chromium/third_party/blink/renderer/platform/widget/DEPS
index d12a302b113..5371c127b23 100644
--- a/chromium/third_party/blink/renderer/platform/widget/DEPS
+++ b/chromium/third_party/blink/renderer/platform/widget/DEPS
@@ -1,6 +1,11 @@
include_rules = [
"+cc/paint/element_id.h",
+ "+cc/trees/latency_info_swap_promise_monitor.h",
"+cc/trees/layer_tree_host.h",
"+cc/trees/layer_tree_settings.h",
"+cc/trees/ukm_manager.h",
+ "+ui/base/ime/text_input_mode.h",
+ "+ui/base/ime/text_input_type.h",
+ "+ui/base/ime/mojom/text_input_state.mojom-blink.h",
+ "+ui/base/ime/mojom/virtual_keyboard_types.mojom-blink.h"
] \ No newline at end of file
diff --git a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
index cb929c85cc3..39b3e388951 100644
--- a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
@@ -336,6 +336,26 @@ void LayerTreeView::NotifyThroughputTrackerResults(
NOTREACHED();
}
+void LayerTreeView::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ if (!delegate_)
+ return;
+ delegate_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+}
+
+void LayerTreeView::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ if (!delegate_) {
+ return;
+ }
+ delegate_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
void LayerTreeView::DidScheduleBeginMainFrame() {
if (!delegate_ || !web_main_thread_scheduler_)
return;
diff --git a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
index 3524f66688e..25eedd050b6 100644
--- a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
+++ b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h
@@ -104,6 +104,13 @@ class PLATFORM_EXPORT LayerTreeView
override;
void NotifyThroughputTrackerResults(
cc::CustomTrackerResults results) override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override;
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override;
// cc::LayerTreeHostSingleThreadClient implementation.
void DidSubmitCompositorFrame() override;
diff --git a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h
index 4f8c4b3b890..39e1b9fd855 100644
--- a/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h
+++ b/chromium/third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h
@@ -65,6 +65,10 @@ class LayerTreeViewDelegate {
// Notifies that the draw commands for a committed frame have been issued.
virtual void DidCommitAndDrawCompositorFrame() = 0;
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) = 0;
+
// Notifies that a compositor frame commit operation is about to start.
virtual void WillCommitCompositorFrame() = 0;
@@ -111,6 +115,14 @@ class LayerTreeViewDelegate {
// perform actual painting work.
virtual void WillBeginMainFrame() = 0;
+ // Submit throughput data to the browser process to store it in case the
+ // renderer process is destroyed via fast shutdown or crashes, at which point
+ // the data can still be submitted to UKM.
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) = 0;
+
protected:
virtual ~LayerTreeViewDelegate() {}
};
diff --git a/chromium/third_party/blink/renderer/platform/widget/frame_widget.h b/chromium/third_party/blink/renderer/platform/widget/frame_widget.h
index 01e5f8b1582..d476c86bcf6 100644
--- a/chromium/third_party/blink/renderer/platform/widget/frame_widget.h
+++ b/chromium/third_party/blink/renderer/platform/widget/frame_widget.h
@@ -5,10 +5,15 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_
+#include "mojo/public/mojom/base/text_direction.mojom-blink.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom-blink.h"
+#include "third_party/blink/public/platform/web_text_input_info.h"
+#include "third_party/blink/public/platform/web_text_input_type.h"
+#include "third_party/blink/public/platform/web_vector.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"
+#include "ui/base/ime/mojom/virtual_keyboard_types.mojom-blink.h"
namespace cc {
class AnimationHost;
@@ -78,6 +83,62 @@ class PLATFORM_EXPORT FrameWidget {
// Returns the DisplayMode in use for the widget.
virtual mojom::blink::DisplayMode DisplayMode() const = 0;
+
+ // Returns the window segments for the widget.
+ virtual const WebVector<WebRect>& WindowSegments() const = 0;
+
+ // Sets the ink metadata on the layer tree host
+ virtual void SetDelegatedInkMetadata(
+ std::unique_ptr<viz::DelegatedInkMetadata> metadata) = 0;
+
+ // Called when the main thread overscrolled.
+ virtual void DidOverscroll(const gfx::Vector2dF& overscroll_delta,
+ const gfx::Vector2dF& accumulated_overscroll,
+ const gfx::PointF& position,
+ const gfx::Vector2dF& velocity) = 0;
+
+ // Requests that a gesture of |injected_type| be reissued at a later point in
+ // time. |injected_type| is required to be one of
+ // GestureScroll{Begin,Update,End}. The dispatched gesture will scroll the
+ // ScrollableArea identified by |scrollable_area_element_id| by the given
+ // delta + granularity.
+ virtual void InjectGestureScrollEvent(
+ WebGestureDevice device,
+ const gfx::Vector2dF& delta,
+ ui::ScrollGranularity granularity,
+ cc::ElementId scrollable_area_element_id,
+ WebInputEvent::Type injected_type) = 0;
+
+ // Called when the cursor for the widget changes.
+ virtual void DidChangeCursor(const ui::Cursor&) = 0;
+
+ // Return the composition character in window coordinates.
+ virtual void GetCompositionCharacterBoundsInWindow(
+ Vector<gfx::Rect>* bounds) = 0;
+
+ virtual gfx::Range CompositionRange() = 0;
+ virtual WebTextInputInfo TextInputInfo() = 0;
+ virtual ui::mojom::blink::VirtualKeyboardVisibilityRequest
+ GetLastVirtualKeyboardVisibilityRequest() = 0;
+ virtual bool ShouldSuppressKeyboardForFocusedElement() = 0;
+
+ // Return the edit context bounds in window coordinates.
+ virtual void GetEditContextBoundsInWindow(
+ base::Optional<gfx::Rect>* control_bounds,
+ base::Optional<gfx::Rect>* selection_bounds) = 0;
+ virtual int32_t ComputeWebTextInputNextPreviousFlags() = 0;
+ virtual void ResetVirtualKeyboardVisibilityRequest() = 0;
+
+ // Return the selection bounds in window coordinates. Returns true if the
+ // bounds returned were different than the passed in focus and anchor bounds.
+ virtual bool GetSelectionBoundsInWindow(gfx::Rect* focus,
+ gfx::Rect* anchor,
+ base::i18n::TextDirection* focus_dir,
+ base::i18n::TextDirection* anchor_dir,
+ bool* is_anchor_first) = 0;
+
+ // Clear any cached text input state.
+ virtual void ClearTextInputState() = 0;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/DEPS b/chromium/third_party/blink/renderer/platform/widget/input/DEPS
index 97b795dad74..6ad1673e51e 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/DEPS
+++ b/chromium/third_party/blink/renderer/platform/widget/input/DEPS
@@ -3,6 +3,7 @@ include_rules = [
"+base/numerics/math_constants.h",
"+base/profiler/sample_metadata.h",
"+base/strings/string_number_conversions.h",
+ "+cc/base/features.h",
"+cc/input/input_handler.h",
"+cc/input/scroll_behavior.h",
"+cc/input/scroll_elasticity_helper.h",
@@ -17,4 +18,4 @@ include_rules = [
"+ui/base/ui_base_features.h",
"+ui/events/types/scroll_types.h",
"+ui/latency/latency_info.h",
-] \ No newline at end of file
+]
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc b/chromium/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
index fba0dda603d..43850976709 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.cc
@@ -173,14 +173,16 @@ void CompositorThreadEventQueue::Queue(
std::unique_ptr<EventWithCallback> scroll_event =
std::make_unique<EventWithCallback>(
- coalesced_events.first.Clone(), scroll_latency,
+ std::make_unique<WebCoalescedInputEvent>(
+ coalesced_events.first.Clone(), scroll_latency),
oldest_creation_timestamp, timestamp_now,
std::move(scroll_original_events));
scroll_event->set_coalesced_scroll_and_pinch();
std::unique_ptr<EventWithCallback> pinch_event =
std::make_unique<EventWithCallback>(
- coalesced_events.second.Clone(), pinch_latency,
+ std::make_unique<WebCoalescedInputEvent>(
+ coalesced_events.second.Clone(), pinch_latency),
oldest_creation_timestamp, timestamp_now,
std::move(pinch_original_events));
pinch_event->set_coalesced_scroll_and_pinch();
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.cc b/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.cc
index d79d40d3d1e..b9842eff91e 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.cc
@@ -11,25 +11,21 @@
namespace blink {
EventWithCallback::EventWithCallback(
- WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ std::unique_ptr<WebCoalescedInputEvent> event,
base::TimeTicks timestamp_now,
InputHandlerProxy::EventDispositionCallback callback)
- : event_(event->Clone()),
- latency_(latency),
+ : event_(std::make_unique<WebCoalescedInputEvent>(*event)),
creation_timestamp_(timestamp_now),
last_coalesced_timestamp_(timestamp_now) {
- original_events_.emplace_back(std::move(event), latency, std::move(callback));
+ original_events_.emplace_back(std::move(event), std::move(callback));
}
EventWithCallback::EventWithCallback(
- WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ std::unique_ptr<WebCoalescedInputEvent> event,
base::TimeTicks creation_timestamp,
base::TimeTicks last_coalesced_timestamp,
std::unique_ptr<OriginalEventList> original_events)
: event_(std::move(event)),
- latency_(latency),
creation_timestamp_(creation_timestamp),
last_coalesced_timestamp_(last_coalesced_timestamp) {
if (original_events)
@@ -43,33 +39,15 @@ bool EventWithCallback::CanCoalesceWith(const EventWithCallback& other) const {
}
void EventWithCallback::SetScrollbarManipulationHandledOnCompositorThread() {
- for (auto& original_event : original_events_)
- original_event.event_->SetScrollbarManipulationHandledOnCompositorThread();
+ for (auto& original_event : original_events_) {
+ original_event.event_->EventPointer()
+ ->SetScrollbarManipulationHandledOnCompositorThread();
+ }
}
void EventWithCallback::CoalesceWith(EventWithCallback* other,
base::TimeTicks timestamp_now) {
- TRACE_EVENT2("input", "EventWithCallback::CoalesceWith", "traceId",
- latency_.trace_id(), "coalescedTraceId",
- other->latency_.trace_id());
- // |other| should be a newer event than |this|.
- if (other->latency_.trace_id() >= 0 && latency_.trace_id() >= 0)
- DCHECK_GT(other->latency_.trace_id(), latency_.trace_id());
-
- // New events get coalesced into older events, and the newer timestamp
- // should always be preserved.
- const base::TimeTicks time_stamp = other->event().TimeStamp();
- event_->Coalesce(other->event());
- event_->SetTimeStamp(time_stamp);
-
- // When coalescing two input events, we keep the oldest LatencyInfo
- // since it will represent the longest latency. If it's a GestureScrollUpdate
- // event, also update the old event's last timestamp and scroll delta using
- // the newer event's latency info.
- if (event_->GetType() == WebInputEvent::Type::kGestureScrollUpdate)
- latency_.CoalesceScrollUpdateWith(other->latency_);
- other->latency_ = latency_;
- other->latency_.set_coalesced();
+ event_->CoalesceWith(*other->event_);
// Move original events.
original_events_.splice(original_events_.end(), other->original_events_);
@@ -96,8 +74,9 @@ void EventWithCallback::RunCallbacks(
return;
// Ack the oldest event with original latency.
+ original_events_.front().event_->latency_info() = latency;
std::move(original_events_.front().callback_)
- .Run(disposition, std::move(original_events_.front().event_), latency,
+ .Run(disposition, std::move(original_events_.front().event_),
did_overscroll_params
? std::make_unique<InputHandlerProxy::DidOverscrollParams>(
*did_overscroll_params)
@@ -114,14 +93,14 @@ void EventWithCallback::RunCallbacks(
bool handled = HandledOnCompositorThread(disposition);
for (auto& coalesced_event : original_events_) {
if (handled) {
- int64_t original_trace_id = coalesced_event.latency_.trace_id();
- coalesced_event.latency_ = latency;
- coalesced_event.latency_.set_trace_id(original_trace_id);
- coalesced_event.latency_.set_coalesced();
+ int64_t original_trace_id =
+ coalesced_event.event_->latency_info().trace_id();
+ coalesced_event.event_->latency_info() = latency;
+ coalesced_event.event_->latency_info().set_trace_id(original_trace_id);
+ coalesced_event.event_->latency_info().set_coalesced();
}
std::move(coalesced_event.callback_)
.Run(disposition, std::move(coalesced_event.event_),
- coalesced_event.latency_,
did_overscroll_params
? std::make_unique<InputHandlerProxy::DidOverscrollParams>(
*did_overscroll_params)
@@ -131,12 +110,9 @@ void EventWithCallback::RunCallbacks(
}
EventWithCallback::OriginalEventWithCallback::OriginalEventWithCallback(
- WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ std::unique_ptr<WebCoalescedInputEvent> event,
InputHandlerProxy::EventDispositionCallback callback)
- : event_(std::move(event)),
- latency_(latency),
- callback_(std::move(callback)) {}
+ : event_(std::move(event)), callback_(std::move(callback)) {}
EventWithCallback::OriginalEventWithCallback::~OriginalEventWithCallback() {}
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.h b/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.h
index cca19851de8..ad0e56dfcec 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.h
+++ b/chromium/third_party/blink/renderer/platform/widget/input/event_with_callback.h
@@ -7,6 +7,7 @@
#include <list>
+#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
#include "third_party/blink/public/platform/input/input_handler_proxy.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "ui/latency/latency_info.h"
@@ -19,26 +20,20 @@ class InputHandlerProxyEventQueueTest;
class PLATFORM_EXPORT EventWithCallback {
public:
- using WebScopedInputEvent = std::unique_ptr<WebInputEvent>;
-
struct PLATFORM_EXPORT OriginalEventWithCallback {
OriginalEventWithCallback(
- WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ std::unique_ptr<WebCoalescedInputEvent> event,
InputHandlerProxy::EventDispositionCallback callback);
~OriginalEventWithCallback();
- WebScopedInputEvent event_;
- ui::LatencyInfo latency_;
+ std::unique_ptr<WebCoalescedInputEvent> event_;
InputHandlerProxy::EventDispositionCallback callback_;
};
using OriginalEventList = std::list<OriginalEventWithCallback>;
- EventWithCallback(WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ EventWithCallback(std::unique_ptr<WebCoalescedInputEvent> event,
base::TimeTicks timestamp_now,
InputHandlerProxy::EventDispositionCallback callback);
- EventWithCallback(WebScopedInputEvent event,
- const ui::LatencyInfo& latency,
+ EventWithCallback(std::unique_ptr<WebCoalescedInputEvent> event,
base::TimeTicks creation_timestamp,
base::TimeTicks last_coalesced_timestamp,
std::unique_ptr<OriginalEventList> original_events);
@@ -52,10 +47,10 @@ class PLATFORM_EXPORT EventWithCallback {
std::unique_ptr<InputHandlerProxy::DidOverscrollParams>,
const WebInputEventAttribution&);
- const WebInputEvent& event() const { return *event_; }
- WebInputEvent* event_pointer() { return event_.get(); }
- const ui::LatencyInfo& latency_info() const { return latency_; }
- ui::LatencyInfo* mutable_latency_info() { return &latency_; }
+ const WebInputEvent& event() const { return event_->Event(); }
+ WebInputEvent* event_pointer() { return event_->EventPointer(); }
+ const ui::LatencyInfo& latency_info() const { return event_->latency_info(); }
+ ui::LatencyInfo& latency_info() { return event_->latency_info(); }
base::TimeTicks creation_timestamp() const { return creation_timestamp_; }
base::TimeTicks last_coalesced_timestamp() const {
return last_coalesced_timestamp_;
@@ -68,8 +63,9 @@ class PLATFORM_EXPORT EventWithCallback {
OriginalEventList& original_events() { return original_events_; }
// |first_original_event()| is used as ID for tracing.
WebInputEvent* first_original_event() {
- return original_events_.empty() ? nullptr
- : original_events_.front().event_.get();
+ return original_events_.empty()
+ ? nullptr
+ : original_events_.front().event_->EventPointer();
}
void SetScrollbarManipulationHandledOnCompositorThread();
@@ -78,8 +74,7 @@ class PLATFORM_EXPORT EventWithCallback {
void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
- WebScopedInputEvent event_;
- ui::LatencyInfo latency_;
+ std::unique_ptr<WebCoalescedInputEvent> event_;
OriginalEventList original_events_;
bool coalesced_scroll_and_pinch_ = false;
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc b/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
index 9da77d69fb0..e366ca1ec13 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
@@ -22,6 +22,7 @@
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
+#include "cc/base/features.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/metrics/event_metrics.h"
#include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
@@ -63,6 +64,25 @@ cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) {
WebGestureEvent::InertialPhaseState::kMomentum);
scroll_state_data.delta_granularity =
event.data.scroll_begin.delta_hint_units;
+
+ if (cc::ElementId::IsValid(
+ event.data.scroll_begin.scrollable_area_element_id)) {
+ cc::ElementId target_scroller(
+ event.data.scroll_begin.scrollable_area_element_id);
+ scroll_state_data.set_current_native_scrolling_element(target_scroller);
+
+ // If the target scroller comes from a main thread hit test, we're in
+ // scroll unification.
+ scroll_state_data.is_main_thread_hit_tested =
+ event.data.scroll_begin.main_thread_hit_tested;
+ DCHECK(!event.data.scroll_begin.main_thread_hit_tested ||
+ base::FeatureList::IsEnabled(::features::kScrollUnification));
+ } else {
+ // If a main thread hit test didn't yield a target we should have
+ // discarded this event before this point.
+ DCHECK(!event.data.scroll_begin.main_thread_hit_tested);
+ }
+
break;
case WebInputEvent::Type::kGestureScrollUpdate:
scroll_state_data.delta_x = -event.data.scroll_update.delta_x;
@@ -235,27 +255,26 @@ void InputHandlerProxy::WillShutdown() {
}
void InputHandlerProxy::HandleInputEventWithLatencyInfo(
- WebScopedInputEvent event,
- const ui::LatencyInfo& latency_info,
+ std::unique_ptr<blink::WebCoalescedInputEvent> event,
EventDispositionCallback callback) {
DCHECK(input_handler_);
input_handler_->NotifyInputEvent();
+ int64_t trace_id = event->latency_info().trace_id();
TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
- [&latency_info](perfetto::EventContext ctx) {
+ [trace_id](perfetto::EventContext ctx) {
ChromeLatencyInfo* info =
ctx.event()->set_chrome_latency_info();
- info->set_trace_id(latency_info.trace_id());
+ info->set_trace_id(trace_id);
info->set_step(ChromeLatencyInfo::STEP_HANDLE_INPUT_EVENT_IMPL);
tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_INOUT,
- latency_info.trace_id());
+ trace_id);
});
std::unique_ptr<EventWithCallback> event_with_callback =
- std::make_unique<EventWithCallback>(std::move(event), latency_info,
- tick_clock_->NowTicks(),
- std::move(callback));
+ std::make_unique<EventWithCallback>(
+ std::move(event), tick_clock_->NowTicks(), std::move(callback));
enum {
NO_SCROLL_PINCH = 0,
@@ -348,6 +367,62 @@ void InputHandlerProxy::HandleInputEventWithLatencyInfo(
tick_clock_->NowTicks());
}
+void InputHandlerProxy::ContinueScrollBeginAfterMainThreadHitTest(
+ std::unique_ptr<blink::WebCoalescedInputEvent> event,
+ EventDispositionCallback callback,
+ cc::ElementIdType hit_test_result) {
+ DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
+ DCHECK_EQ(event->Event().GetType(),
+ WebGestureEvent::Type::kGestureScrollBegin);
+ DCHECK(hit_testing_scroll_begin_on_main_thread_);
+ DCHECK(currently_active_gesture_device_);
+ DCHECK(input_handler_);
+
+ hit_testing_scroll_begin_on_main_thread_ = false;
+
+ // HandleGestureScrollBegin has logic to end an existing scroll when an
+ // unexpected scroll begin arrives. We currently think we're in a scroll
+ // because of the first ScrollBegin so clear this so we don't spurriously
+ // call ScrollEnd. It will be set again in HandleGestureScrollBegin.
+ currently_active_gesture_device_ = base::nullopt;
+
+ auto* gesture_event =
+ static_cast<blink::WebGestureEvent*>(event->EventPointer());
+ if (cc::ElementId::IsValid(hit_test_result)) {
+ gesture_event->data.scroll_begin.scrollable_area_element_id =
+ hit_test_result;
+ gesture_event->data.scroll_begin.main_thread_hit_tested = true;
+
+ std::unique_ptr<EventWithCallback> event_with_callback =
+ std::make_unique<EventWithCallback>(
+ std::move(event), tick_clock_->NowTicks(), std::move(callback));
+
+ DispatchSingleInputEvent(std::move(event_with_callback),
+ tick_clock_->NowTicks());
+ } else {
+ // TODO(bokan): This looks odd but is actually what happens in the
+ // non-unified path. If a scroll is DROP_EVENT'ed, we still call
+ // RecordMainThreadScrollingReasons and then LTHI::RecordScrollEnd when we
+ // DROP the ScrollEnd. We call this to ensure symmetry between
+ // RecordScrollBegin and RecordScrollEnd but we should probably be avoiding
+ // this if the scroll never starts. https://crbug.com/1082601.
+ RecordMainThreadScrollingReasons(gesture_event->SourceDevice(), 0);
+
+ // If the main thread failed to return a scroller for whatever reason,
+ // consider the ScrollBegin to be dropped.
+ scroll_sequence_ignored_ = true;
+ WebInputEventAttribution attribution =
+ PerformEventAttribution(event->Event());
+ std::move(callback).Run(DROP_EVENT, std::move(event),
+ /*overscroll_params=*/nullptr, attribution);
+ }
+
+ // We blocked the compositor gesture event queue while the hit test was
+ // pending so scroll updates may be waiting in the queue. Now that we've
+ // finished the hit test and performed the scroll begin, flush the queue.
+ DispatchQueuedInputEvents();
+}
+
void InputHandlerProxy::DispatchSingleInputEvent(
std::unique_ptr<EventWithCallback> event_with_callback,
const base::TimeTicks now) {
@@ -376,7 +451,16 @@ void InputHandlerProxy::DispatchSingleInputEvent(
case WebGestureEvent::Type::kGestureScrollBegin:
case WebGestureEvent::Type::kGesturePinchBegin:
if (disposition == DID_HANDLE ||
- disposition == DID_HANDLE_SHOULD_BUBBLE) {
+ disposition == DID_HANDLE_SHOULD_BUBBLE ||
+ disposition == REQUIRES_MAIN_THREAD_HIT_TEST) {
+ // REQUIRES_MAIN_THREAD_HIT_TEST means the scroll will be handled by
+ // the compositor but needs to block until a hit test is performed by
+ // Blink. We need to set this to indicate we're in a scroll so that
+ // gestures are queued rather than dispatched immediately.
+ // TODO(bokan): It's a bit of an open question if we need to also set
+ // |handling_gesture_on_impl_thread_|. Ideally these two bits would be
+ // merged. The queueing behavior is currently just determined by having
+ // an active gesture device.
currently_active_gesture_device_ =
static_cast<const WebGestureEvent&>(event).SourceDevice();
}
@@ -420,6 +504,14 @@ void InputHandlerProxy::DispatchSingleInputEvent(
}
void InputHandlerProxy::DispatchQueuedInputEvents() {
+ // Block flushing the compositor gesture event queue while there's an async
+ // scroll begin hit test outstanding. We'll flush the queue when the hit test
+ // responds.
+ if (hit_testing_scroll_begin_on_main_thread_) {
+ DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
+ return;
+ }
+
// Calling |NowTicks()| is expensive so we only want to do it once.
base::TimeTicks now = tick_clock_->NowTicks();
while (!compositor_event_queue_->empty())
@@ -486,7 +578,8 @@ void InputHandlerProxy::InjectScrollbarGestureScroll(
std::unique_ptr<EventWithCallback> gesture_event_with_callback_update =
std::make_unique<EventWithCallback>(
- std::move(web_scoped_gesture_event), scrollbar_latency_info,
+ std::make_unique<WebCoalescedInputEvent>(
+ std::move(web_scoped_gesture_event), scrollbar_latency_info),
original_timestamp, original_timestamp, nullptr);
bool needs_animate_input = compositor_event_queue_->empty();
@@ -497,7 +590,7 @@ void InputHandlerProxy::InjectScrollbarGestureScroll(
input_handler_->SetNeedsAnimateInput();
}
-bool HasModifier(const WebInputEvent& event) {
+bool HasScrollbarJumpKeyModifier(const WebInputEvent& event) {
#if defined(OS_MACOSX)
// Mac uses the "Option" key (which is mapped to the enum "kAltKey").
return event.GetModifiers() & WebInputEvent::kAltKey;
@@ -588,7 +681,7 @@ InputHandlerProxy::RouteToTypeSpecificHandler(
cc::InputHandlerPointerResult pointer_result =
input_handler_->MouseDown(
gfx::PointF(mouse_event.PositionInWidget()),
- HasModifier(event));
+ HasScrollbarJumpKeyModifier(event));
if (pointer_result.type == cc::PointerResultType::kScrollbarScroll) {
// Since a kScrollbarScroll is about to commence, ensure that any
// existing ongoing scroll is ended.
@@ -903,24 +996,23 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
cc::InputHandler::ScrollStatus scroll_status;
- cc::ElementIdType element_id_type =
- gesture_event.data.scroll_begin.scrollable_area_element_id;
- if (element_id_type) {
- scroll_state.data()->set_current_native_scrolling_element(
- cc::ElementId(element_id_type));
- }
- if (gesture_event.data.scroll_begin.delta_hint_units ==
- ui::ScrollGranularity::kScrollByPage) {
- scroll_status.thread = cc::InputHandler::SCROLL_ON_MAIN_THREAD;
- scroll_status.main_thread_scrolling_reasons =
- cc::MainThreadScrollingReason::kContinuingMainThreadScroll;
- } else if (gesture_event.data.scroll_begin.target_viewport) {
+ if (gesture_event.data.scroll_begin.target_viewport) {
scroll_status = input_handler_->RootScrollBegin(
&scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
} else {
scroll_status = input_handler_->ScrollBegin(
&scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
}
+
+ // If we need a hit test from the main thread, we'll reinject this scroll
+ // begin event once the hit test is complete so avoid everything below for
+ // now, it'll be run on the second iteration.
+ if (scroll_status.needs_main_thread_hit_test) {
+ DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
+ hit_testing_scroll_begin_on_main_thread_ = true;
+ return REQUIRES_MAIN_THREAD_HIT_TEST;
+ }
+
RecordMainThreadScrollingReasons(gesture_event.SourceDevice(),
scroll_status.main_thread_scrolling_reasons);
@@ -950,6 +1042,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
result = DROP_EVENT;
break;
}
+
+ // TODO(bokan): Should we really be calling this in cases like DROP_EVENT and
+ // DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING? I think probably not.
if (elastic_overscroll_controller_ && result != DID_NOT_HANDLE) {
HandleScrollElasticityOverscroll(gesture_event,
cc::InputHandlerScrollResult());
@@ -988,7 +1083,8 @@ InputHandlerProxy::HandleGestureScrollUpdate(
return DROP_EVENT;
}
- if (input_handler_->ScrollingShouldSwitchtoMainThread()) {
+ if (!base::FeatureList::IsEnabled(::features::kScrollUnification) &&
+ input_handler_->ScrollingShouldSwitchtoMainThread()) {
TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
TRACE_EVENT_SCOPE_THREAD);
handling_gesture_on_impl_thread_ = false;
@@ -1020,9 +1116,20 @@ InputHandlerProxy::HandleGestureScrollUpdate(
return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
}
+// TODO(arakeri): Ensure that redudant GSE(s) in the CompositorThreadEventQueue
+// are handled gracefully. (i.e currently, when an ongoing scroll needs to end,
+// we call RecordScrollEnd and InputHandlerScrollEnd synchronously. Ideally, we
+// should end the scroll when the GSB is being handled).
InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
const WebGestureEvent& gesture_event) {
TRACE_EVENT0("input", "InputHandlerProxy::HandleGestureScrollEnd");
+
+ // TODO(bokan): It seems odd that we'd record a ScrollEnd for a scroll
+ // secuence that was ignored (i.e. the ScrollBegin was dropped). However,
+ // RecordScrollBegin does get called in that case so this needs to be this
+ // way for now. This makes life rather awkward for the unified scrolling path
+ // so perhaps we should only record a scrolling thread if a scroll actually
+ // started? https://crbug.com/1082601.
input_handler_->RecordScrollEnd(
GestureScrollInputType(gesture_event.SourceDevice()));
@@ -1053,22 +1160,25 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
void InputHandlerProxy::InputHandlerScrollEnd() {
input_handler_->ScrollEnd(/*should_snap=*/true);
handling_gesture_on_impl_thread_ = false;
+
+ DCHECK(!gesture_pinch_in_progress_);
+ currently_active_gesture_device_ = base::nullopt;
}
InputHandlerProxy::EventDisposition InputHandlerProxy::HitTestTouchEvent(
const WebTouchEvent& touch_event,
bool* is_touching_scrolling_layer,
- cc::TouchAction* white_listed_touch_action) {
+ cc::TouchAction* allowed_touch_action) {
TRACE_EVENT1("input", "InputHandlerProxy::HitTestTouchEvent",
- "Needs whitelisted TouchAction",
- static_cast<bool>(white_listed_touch_action));
+ "Needs allowed TouchAction",
+ static_cast<bool>(allowed_touch_action));
*is_touching_scrolling_layer = false;
EventDisposition result = DROP_EVENT;
for (size_t i = 0; i < touch_event.touches_length; ++i) {
if (touch_event.touch_start_or_first_touch_move)
- DCHECK(white_listed_touch_action);
+ DCHECK(allowed_touch_action);
else
- DCHECK(!white_listed_touch_action);
+ DCHECK(!allowed_touch_action);
if (touch_event.GetType() == WebInputEvent::Type::kTouchStart &&
touch_event.touches[i].state != WebTouchPoint::State::kStatePressed) {
@@ -1081,11 +1191,11 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HitTestTouchEvent(
gfx::Point(touch_event.touches[i].PositionInWidget().x(),
touch_event.touches[i].PositionInWidget().y()),
&touch_action);
- if (white_listed_touch_action && touch_action != cc::TouchAction::kAuto) {
+ if (allowed_touch_action && touch_action != cc::TouchAction::kAuto) {
TRACE_EVENT_INSTANT1("input", "Adding TouchAction",
TRACE_EVENT_SCOPE_THREAD, "TouchAction",
cc::TouchActionToString(touch_action));
- *white_listed_touch_action &= touch_action;
+ *allowed_touch_action &= touch_action;
}
if (event_listener_type !=
@@ -1098,13 +1208,12 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HitTestTouchEvent(
cc::InputHandler::TouchStartOrMoveEventListenerType::
HANDLER_ON_SCROLLING_LAYER;
- // A non-passive touch start / move will always set the whitelisted touch
+ // A non-passive touch start / move will always set the allowed touch
// action to TouchAction::kNone, and in that case we do not ack the event
// from the compositor.
- if (white_listed_touch_action &&
- *white_listed_touch_action != cc::TouchAction::kNone) {
- TRACE_EVENT_INSTANT0("input",
- "NonBlocking due to whitelisted touchaction",
+ if (allowed_touch_action &&
+ *allowed_touch_action != cc::TouchAction::kNone) {
+ TRACE_EVENT_INSTANT0("input", "NonBlocking due to allowed touchaction",
TRACE_EVENT_SCOPE_THREAD);
result = DID_HANDLE_NON_BLOCKING;
} else {
@@ -1176,9 +1285,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
TRACE_EVENT0("input", "InputHandlerProxy::HandleTouchStart");
bool is_touching_scrolling_layer;
- cc::TouchAction white_listed_touch_action = cc::TouchAction::kAuto;
+ cc::TouchAction allowed_touch_action = cc::TouchAction::kAuto;
EventDisposition result = HitTestTouchEvent(
- touch_event, &is_touching_scrolling_layer, &white_listed_touch_action);
+ touch_event, &is_touching_scrolling_layer, &allowed_touch_action);
TRACE_EVENT_INSTANT1("input", "HitTest", TRACE_EVENT_SCOPE_THREAD,
"disposition", result);
@@ -1203,20 +1312,19 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
// Due to tap suppression on the browser side, this will reset the
// browser-side touch action (see comment in
// TouchActionFilter::FilterGestureEvent for GestureScrollBegin). Ensure we
- // send back a white_listed_touch_action that matches this non-blocking
- // behavior rather than treating it as if it'll block.
+ // send back an allowed_touch_action that matches this non-blocking behavior
+ // rather than treating it as if it'll block.
TRACE_EVENT_INSTANT0("input", "NonBlocking due to fling",
TRACE_EVENT_SCOPE_THREAD);
- white_listed_touch_action = cc::TouchAction::kAuto;
+ allowed_touch_action = cc::TouchAction::kAuto;
result = DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING;
}
- TRACE_EVENT_INSTANT2("input", "Whitelisted TouchAction",
- TRACE_EVENT_SCOPE_THREAD, "TouchAction",
- cc::TouchActionToString(white_listed_touch_action),
- "disposition", result);
- client_->SetWhiteListedTouchAction(white_listed_touch_action,
- touch_event.unique_touch_event_id, result);
+ TRACE_EVENT_INSTANT2(
+ "input", "Allowed TouchAction", TRACE_EVENT_SCOPE_THREAD, "TouchAction",
+ cc::TouchActionToString(allowed_touch_action), "disposition", result);
+ client_->SetAllowedTouchAction(allowed_touch_action,
+ touch_event.unique_touch_event_id, result);
return result;
}
@@ -1232,15 +1340,14 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove(
if (!touch_result_.has_value() ||
touch_event.touch_start_or_first_touch_move) {
bool is_touching_scrolling_layer;
- cc::TouchAction white_listed_touch_action = cc::TouchAction::kAuto;
+ cc::TouchAction allowed_touch_action = cc::TouchAction::kAuto;
EventDisposition result = HitTestTouchEvent(
- touch_event, &is_touching_scrolling_layer, &white_listed_touch_action);
- TRACE_EVENT_INSTANT2("input", "Whitelisted TouchAction",
- TRACE_EVENT_SCOPE_THREAD, "TouchAction",
- cc::TouchActionToString(white_listed_touch_action),
- "disposition", result);
- client_->SetWhiteListedTouchAction(
- white_listed_touch_action, touch_event.unique_touch_event_id, result);
+ touch_event, &is_touching_scrolling_layer, &allowed_touch_action);
+ TRACE_EVENT_INSTANT2(
+ "input", "Allowed TouchAction", TRACE_EVENT_SCOPE_THREAD, "TouchAction",
+ cc::TouchActionToString(allowed_touch_action), "disposition", result);
+ client_->SetAllowedTouchAction(allowed_touch_action,
+ touch_event.unique_touch_event_id, result);
return result;
}
return touch_result_.value();
@@ -1283,6 +1390,14 @@ void InputHandlerProxy::UpdateRootLayerStateForSynchronousInputHandler(
void InputHandlerProxy::DeliverInputForBeginFrame(
const viz::BeginFrameArgs& args) {
+ // Block flushing the compositor gesture event queue while there's an async
+ // scroll begin hit test outstanding. We'll flush the queue when the hit test
+ // responds.
+ if (hit_testing_scroll_begin_on_main_thread_) {
+ DCHECK(base::FeatureList::IsEnabled(::features::kScrollUnification));
+ return;
+ }
+
if (!scroll_predictor_)
DispatchQueuedInputEvents();
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
index d6a6cdaa05c..b1a59a5035e 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -17,6 +17,7 @@
#include "base/test/task_environment.h"
#include "base/test/trace_event_analyzer.h"
#include "build/build_config.h"
+#include "cc/base/features.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/trees/swap_promise_monitor.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -42,30 +43,22 @@ using cc::InputHandler;
using cc::ScrollBeginThreadState;
using cc::TouchAction;
using testing::_;
+using testing::AllOf;
using testing::DoAll;
+using testing::Eq;
using testing::Field;
using testing::Mock;
+using testing::NiceMock;
+using testing::Property;
using testing::Return;
using testing::SetArgPointee;
+using testing::StrictMock;
namespace blink {
namespace test {
namespace {
-enum InputHandlerProxyTestType {
- ROOT_SCROLL_NORMAL_HANDLER,
- ROOT_SCROLL_SYNCHRONOUS_HANDLER,
- CHILD_SCROLL_NORMAL_HANDLER,
- CHILD_SCROLL_SYNCHRONOUS_HANDLER,
-};
-static const InputHandlerProxyTestType test_types[] = {
- ROOT_SCROLL_NORMAL_HANDLER,
- ROOT_SCROLL_SYNCHRONOUS_HANDLER,
- CHILD_SCROLL_NORMAL_HANDLER,
- CHILD_SCROLL_SYNCHRONOUS_HANDLER,
-};
-
MATCHER_P(WheelEventsMatch, expected, "") {
return WheelEventsMatch(arg, expected);
}
@@ -221,11 +214,10 @@ class MockInputHandlerProxyClient : public InputHandlerProxyClient {
const WebInputEventAttribution&));
void DispatchNonBlockingEventToMainThread(
- std::unique_ptr<WebInputEvent> event,
- const ui::LatencyInfo& latency_info,
+ std::unique_ptr<WebCoalescedInputEvent> event,
const WebInputEventAttribution&) override {
CHECK(event.get());
- DispatchNonBlockingEventToMainThread_(*event.get());
+ DispatchNonBlockingEventToMainThread_(event->Event());
}
MOCK_METHOD5(DidOverscroll,
@@ -236,7 +228,7 @@ class MockInputHandlerProxyClient : public InputHandlerProxyClient {
const cc::OverscrollBehavior& overscroll_behavior));
void DidAnimateForInput() override {}
void DidStartScrollingViewport() override {}
- MOCK_METHOD3(SetWhiteListedTouchAction,
+ MOCK_METHOD3(SetAllowedTouchAction,
void(cc::TouchAction touch_action,
uint32_t unique_touch_event_id,
InputHandlerProxy::EventDisposition event_disposition));
@@ -271,6 +263,11 @@ const cc::InputHandler::ScrollStatus kImplThreadScrollState(
cc::InputHandler::SCROLL_ON_IMPL_THREAD,
cc::MainThreadScrollingReason::kNotScrollingOnMain);
+const cc::InputHandler::ScrollStatus kRequiresMainThreadHitTestState(
+ cc::InputHandler::SCROLL_ON_IMPL_THREAD,
+ cc::MainThreadScrollingReason::kNotScrollingOnMain,
+ /*needs_main_thread_hit_test=*/true);
+
const cc::InputHandler::ScrollStatus kMainThreadScrollState(
cc::InputHandler::SCROLL_ON_MAIN_THREAD,
cc::MainThreadScrollingReason::kHandlingScrollFromMainThread);
@@ -297,9 +294,9 @@ class TestInputHandlerProxy : public InputHandlerProxy {
EventDisposition HitTestTouchEventForTest(
const WebTouchEvent& touch_event,
bool* is_touching_scrolling_layer,
- cc::TouchAction* white_listed_touch_action) {
+ cc::TouchAction* allowed_touch_action) {
return HitTestTouchEvent(touch_event, is_touching_scrolling_layer,
- white_listed_touch_action);
+ allowed_touch_action);
}
EventDisposition HandleMouseWheelForTest(
@@ -312,23 +309,41 @@ class TestInputHandlerProxy : public InputHandlerProxy {
void DispatchQueuedInputEventsHelper() { DispatchQueuedInputEvents(); }
};
+// Whether or not the input handler says that the viewport is scrolling the
+// root scroller or a child.
+enum class ScrollerType { kRoot, kChild };
+
+// Whether or not to setup a synchronous input handler. This simulates the mode
+// that WebView runs in.
+enum class HandlerType { kNormal, kSynchronous };
+
+// Run tests with unification both on and off.
+enum class ScrollUnification { kEnabled, kDisabled };
+
class InputHandlerProxyTest
: public testing::Test,
- public testing::WithParamInterface<InputHandlerProxyTestType> {
+ public testing::WithParamInterface<
+ std::tuple<ScrollerType, HandlerType, ScrollUnification>> {
+ ScrollerType GetScrollerType() { return std::get<0>(GetParam()); }
+ HandlerType GetHandlerType() { return std::get<1>(GetParam()); }
+ ScrollUnification GetScrollUnificationState() {
+ return std::get<2>(GetParam());
+ }
+
public:
- InputHandlerProxyTest()
- : synchronous_root_scroll_(GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER),
- install_synchronous_handler_(
- GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER ||
- GetParam() == CHILD_SCROLL_SYNCHRONOUS_HANDLER),
- expected_disposition_(InputHandlerProxy::DID_HANDLE) {
+ InputHandlerProxyTest() {
+ if (GetScrollUnificationState() == ScrollUnification::kEnabled)
+ scoped_feature_list_.InitAndEnableFeature(features::kScrollUnification);
+ else
+ scoped_feature_list_.InitAndDisableFeature(features::kScrollUnification);
+
input_handler_ = std::make_unique<TestInputHandlerProxy>(
&mock_input_handler_, &mock_client_,
/*force_input_to_main_thread=*/false);
scroll_result_did_scroll_.did_scroll = true;
scroll_result_did_not_scroll_.did_scroll = false;
- if (install_synchronous_handler_) {
+ if (GetHandlerType() == HandlerType::kSynchronous) {
EXPECT_CALL(mock_input_handler_,
RequestUpdateForSynchronousInputHandler())
.Times(1);
@@ -336,7 +351,9 @@ class InputHandlerProxyTest
&mock_synchronous_input_handler_);
}
- mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_);
+ mock_input_handler_.set_is_scrolling_root(
+ GetHandlerType() == HandlerType::kSynchronous &&
+ GetScrollerType() == ScrollerType::kRoot);
// Set a default device so tests don't always have to set this.
gesture_.SetSourceDevice(WebGestureDevice::kTouchpad);
@@ -373,18 +390,18 @@ class InputHandlerProxyTest
void GestureScrollIgnored();
void FlingAndSnap();
- const bool synchronous_root_scroll_;
- const bool install_synchronous_handler_;
testing::StrictMock<MockInputHandler> mock_input_handler_;
testing::StrictMock<MockSynchronousInputHandler>
mock_synchronous_input_handler_;
std::unique_ptr<TestInputHandlerProxy> input_handler_;
testing::StrictMock<MockInputHandlerProxyClient> mock_client_;
WebGestureEvent gesture_;
- InputHandlerProxy::EventDisposition expected_disposition_;
+ InputHandlerProxy::EventDisposition expected_disposition_ =
+ InputHandlerProxy::DID_HANDLE;
base::HistogramTester histogram_tester_;
cc::InputHandlerScrollResult scroll_result_did_scroll_;
cc::InputHandlerScrollResult scroll_result_did_not_scroll_;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
// The helper basically returns the EventDisposition that is returned by
@@ -395,16 +412,17 @@ class InputHandlerProxyTest
InputHandlerProxy::EventDisposition HandleInputEventWithLatencyInfo(
TestInputHandlerProxy* input_handler,
const WebInputEvent& event) {
- std::unique_ptr<WebInputEvent> scoped_input_event(event.Clone());
+ std::unique_ptr<WebCoalescedInputEvent> scoped_input_event =
+ std::make_unique<WebCoalescedInputEvent>(event.Clone(),
+ ui::LatencyInfo());
InputHandlerProxy::EventDisposition event_disposition =
InputHandlerProxy::DID_NOT_HANDLE;
input_handler->HandleInputEventWithLatencyInfo(
- std::move(scoped_input_event), ui::LatencyInfo(),
+ std::move(scoped_input_event),
base::BindLambdaForTesting(
[&event_disposition](
InputHandlerProxy::EventDisposition disposition,
- std::unique_ptr<WebInputEvent> event,
- const ui::LatencyInfo& latency_info,
+ std::unique_ptr<blink::WebCoalescedInputEvent> event,
std::unique_ptr<InputHandlerProxy::DidOverscrollParams> callback,
const WebInputEventAttribution& attribution) {
event_disposition = disposition;
@@ -419,16 +437,17 @@ InputHandlerProxy::EventDisposition HandleInputEventAndFlushEventQueue(
const WebInputEvent& event) {
EXPECT_CALL(mock_input_handler, SetNeedsAnimateInput())
.Times(testing::AnyNumber());
- std::unique_ptr<WebInputEvent> scoped_input_event(event.Clone());
+ std::unique_ptr<WebCoalescedInputEvent> scoped_input_event =
+ std::make_unique<WebCoalescedInputEvent>(event.Clone(),
+ ui::LatencyInfo());
InputHandlerProxy::EventDisposition event_disposition =
InputHandlerProxy::DID_NOT_HANDLE;
input_handler->HandleInputEventWithLatencyInfo(
- std::move(scoped_input_event), ui::LatencyInfo(),
+ std::move(scoped_input_event),
base::BindLambdaForTesting(
[&event_disposition](
InputHandlerProxy::EventDisposition disposition,
- std::unique_ptr<WebInputEvent> event,
- const ui::LatencyInfo& latency_info,
+ std::unique_ptr<blink::WebCoalescedInputEvent> event,
std::unique_ptr<InputHandlerProxy::DidOverscrollParams> callback,
const WebInputEventAttribution& attribution) {
event_disposition = disposition;
@@ -444,10 +463,6 @@ class InputHandlerProxyEventQueueTest : public testing::Test {
: input_handler_proxy_(&mock_input_handler_,
&mock_client_,
/*force_input_to_main_thread=*/false) {
- if (input_handler_proxy_.compositor_event_queue_) {
- input_handler_proxy_.compositor_event_queue_ =
- std::make_unique<CompositorThreadEventQueue>();
- }
SetScrollPredictionEnabled(true);
}
@@ -472,9 +487,9 @@ class InputHandlerProxyEventQueueTest : public testing::Test {
}
void InjectInputEvent(std::unique_ptr<WebInputEvent> event) {
- ui::LatencyInfo latency;
input_handler_proxy_.HandleInputEventWithLatencyInfo(
- std::move(event), latency,
+ std::make_unique<WebCoalescedInputEvent>(std::move(event),
+ ui::LatencyInfo()),
base::BindOnce(
&InputHandlerProxyEventQueueTest::DidHandleInputEventAndOverscroll,
weak_ptr_factory_.GetWeakPtr()));
@@ -491,12 +506,11 @@ class InputHandlerProxyEventQueueTest : public testing::Test {
void DidHandleInputEventAndOverscroll(
InputHandlerProxy::EventDisposition event_disposition,
- std::unique_ptr<WebInputEvent> input_event,
- const ui::LatencyInfo& latency_info,
+ std::unique_ptr<WebCoalescedInputEvent> input_event,
std::unique_ptr<InputHandlerProxy::DidOverscrollParams> overscroll_params,
const WebInputEventAttribution& attribution) {
event_disposition_recorder_.push_back(event_disposition);
- latency_info_recorder_.push_back(latency_info);
+ latency_info_recorder_.push_back(input_event->latency_info());
}
base::circular_deque<std::unique_ptr<EventWithCallback>>& event_queue() {
@@ -554,7 +568,7 @@ class InputHandlerProxyEventQueueTest : public testing::Test {
// Tests that changing source devices mid gesture scroll is handled gracefully.
// For example, when a touch scroll is in progress and the user initiates a
// scrollbar scroll before the touch scroll has had a chance to dispatch a GSE.
-TEST_P(InputHandlerProxyTest, NestedGestureBasedScrolls) {
+TEST_P(InputHandlerProxyTest, NestedGestureBasedScrollsDifferentSourceDevice) {
// Touchpad initiates a scroll.
EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
.WillOnce(testing::Return(kImplThreadScrollState));
@@ -587,8 +601,10 @@ TEST_P(InputHandlerProxyTest, NestedGestureBasedScrolls) {
cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(0);
EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
@@ -747,8 +763,10 @@ TEST_P(InputHandlerProxyTest, ScrollbarScrollEndOnDeviceChange) {
cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(0);
EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
@@ -841,8 +859,10 @@ void InputHandlerProxyTest::GestureScrollStarted() {
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
_))
.WillOnce(testing::Return(scroll_result_did_not_scroll_));
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_EQ(expected_disposition_,
HandleInputEventAndFlushEventQueue(mock_input_handler_,
input_handler_.get(), gesture_));
@@ -851,8 +871,10 @@ void InputHandlerProxyTest::GestureScrollStarted() {
expected_disposition_ = InputHandlerProxy::DID_HANDLE;
VERIFY_AND_RESET_MOCKS();
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
gesture_.SetType(WebInputEvent::Type::kGestureScrollUpdate);
gesture_.data.scroll_update.delta_y =
-40; // -Y means scroll down - i.e. in the +Y direction.
@@ -949,22 +971,31 @@ TEST_P(InputHandlerProxyTest, GestureScrollIgnored) {
}
TEST_P(InputHandlerProxyTest, GestureScrollByPage) {
- // We should send all events to the widget for this gesture.
- expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
VERIFY_AND_RESET_MOCKS();
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(testing::Return(kImplThreadScrollState));
+
gesture_.SetType(WebInputEvent::Type::kGestureScrollBegin);
gesture_.data.scroll_begin.delta_hint_units =
ui::ScrollGranularity::kScrollByPage;
EXPECT_CALL(
mock_input_handler_,
- RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
+ RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
EXPECT_EQ(expected_disposition_,
HandleInputEventWithLatencyInfo(input_handler_.get(), gesture_));
VERIFY_AND_RESET_MOCKS();
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
+ .WillOnce(testing::Return(scroll_result_did_scroll_));
+
gesture_.SetType(WebInputEvent::Type::kGestureScrollUpdate);
gesture_.data.scroll_update.delta_y = 1;
gesture_.data.scroll_update.delta_units =
@@ -974,6 +1005,7 @@ TEST_P(InputHandlerProxyTest, GestureScrollByPage) {
VERIFY_AND_RESET_MOCKS();
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(1);
gesture_.SetType(WebInputEvent::Type::kGestureScrollEnd);
gesture_.data.scroll_update.delta_y = 0;
EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
@@ -1110,6 +1142,12 @@ TEST_P(InputHandlerProxyTest, GesturePinch) {
}
TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) {
+ // This situation is no longer possible under scroll unification as all
+ // scrolling now happens on the compositor thread. This test can be removed
+ // when the feature ships.
+ if (base::FeatureList::IsEnabled(features::kScrollUnification))
+ return;
+
// Scrolls will start by being sent to the main thread.
expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
VERIFY_AND_RESET_MOCKS();
@@ -1157,8 +1195,10 @@ TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) {
VERIFY_AND_RESET_MOCKS();
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
gesture_.SetType(WebInputEvent::Type::kGestureScrollUpdate);
gesture_.data.scroll_update.delta_y =
-40; // -Y means scroll down - i.e. in the +Y direction.
@@ -1262,10 +1302,22 @@ void InputHandlerProxyTest::ScrollHandlingSwitchedToMainThread() {
VERIFY_AND_RESET_MOCKS();
}
TEST_P(InputHandlerProxyTest, WheelScrollHandlingSwitchedToMainThread) {
+ // This situation is no longer possible under scroll unification as all
+ // scrolling now happens on the compositor thread. This test can be removed
+ // when the feature ships.
+ if (base::FeatureList::IsEnabled(features::kScrollUnification))
+ return;
+
gesture_.SetSourceDevice(WebGestureDevice::kTouchpad);
ScrollHandlingSwitchedToMainThread();
}
TEST_P(InputHandlerProxyTest, TouchScrollHandlingSwitchedToMainThread) {
+ // This situation is no longer possible under scroll unification as all
+ // scrolling now happens on the compositor thread. This test can be removed
+ // when the feature ships.
+ if (base::FeatureList::IsEnabled(features::kScrollUnification))
+ return;
+
gesture_.SetSourceDevice(WebGestureDevice::kTouchscreen);
ScrollHandlingSwitchedToMainThread();
}
@@ -1372,17 +1424,92 @@ TEST_P(InputHandlerProxyTest, HitTestTouchEventNonNullTouchAction) {
CreateWebTouchPoint(WebTouchPoint::State::kStatePressed, -10, 10);
bool is_touching_scrolling_layer;
- cc::TouchAction white_listed_touch_action = cc::TouchAction::kAuto;
- EXPECT_EQ(expected_disposition_, input_handler_->HitTestTouchEventForTest(
- touch, &is_touching_scrolling_layer,
- &white_listed_touch_action));
+ cc::TouchAction allowed_touch_action = cc::TouchAction::kAuto;
+ EXPECT_EQ(expected_disposition_,
+ input_handler_->HitTestTouchEventForTest(
+ touch, &is_touching_scrolling_layer, &allowed_touch_action));
EXPECT_TRUE(is_touching_scrolling_layer);
- EXPECT_EQ(white_listed_touch_action, cc::TouchAction::kPanUp);
+ EXPECT_EQ(allowed_touch_action, cc::TouchAction::kPanUp);
VERIFY_AND_RESET_MOCKS();
}
-// Tests that the whitelisted touch action is correctly set when a touch is
-// made non blocking due to an ongoing fling. https://crbug.com/1048098.
+// Tests that multiple mousedown(s) on scrollbar are handled gracefully and
+// don't fail any DCHECK(s).
+TEST_F(InputHandlerProxyEventQueueTest,
+ NestedGestureBasedScrollsSameSourceDevice) {
+ // Start with mousedown. Expect CompositorThreadEventQueue to contain [GSB,
+ // GSU].
+ EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput());
+ HandleMouseEvent(WebInputEvent::Type::kMouseDown);
+ EXPECT_EQ(2ul, event_queue().size());
+ EXPECT_EQ(event_queue()[0]->event().GetType(),
+ WebInputEvent::Type::kGestureScrollBegin);
+ EXPECT_EQ(event_queue()[1]->event().GetType(),
+ WebInputEvent::Type::kGestureScrollUpdate);
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kImplThreadScrollState));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(Return(false));
+ }
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
+
+ DeliverInputForBeginFrame();
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // A mouseup adds a GSE to the CompositorThreadEventQueue.
+ EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput());
+ HandleMouseEvent(WebInputEvent::Type::kMouseUp);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ EXPECT_EQ(1ul, event_queue().size());
+ EXPECT_EQ(event_queue()[0]->event().GetType(),
+ WebInputEvent::Type::kGestureScrollEnd);
+
+ // Called when a mousedown is being handled as it tries to end the ongoing
+ // scroll.
+ EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(true)).Times(1);
+
+ // A mousedown occurs on the scrollbar *before* the GSE is dispatched.
+ HandleMouseEvent(WebInputEvent::Type::kMouseDown);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ EXPECT_EQ(3ul, event_queue().size());
+ EXPECT_EQ(event_queue()[1]->event().GetType(),
+ WebInputEvent::Type::kGestureScrollBegin);
+ EXPECT_EQ(event_queue()[2]->event().GetType(),
+ WebInputEvent::Type::kGestureScrollUpdate);
+
+ // Called when the GSE is being handled. (Note that ScrollEnd isn't called
+ // when the GSE is being handled as the GSE gets dropped in
+ // HandleGestureScrollEnd because handling_gesture_on_impl_thread_ is false)
+ EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kImplThreadScrollState));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(Return(false));
+ }
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
+
+ DeliverInputForBeginFrame();
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Finally, a mouseup ends the scroll.
+ EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput());
+ HandleMouseEvent(WebInputEvent::Type::kMouseUp);
+
+ EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(true)).Times(1);
+
+ DeliverInputForBeginFrame();
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+}
+
+// Tests that the allowed touch action is correctly set when a touch is made
+// non-blocking due to an ongoing fling. https://crbug.com/1048098.
TEST_F(InputHandlerProxyEventQueueTest, AckTouchActionNonBlockingForFling) {
// Simulate starting a compositor scroll and then flinging. This is setup for
// the real checks below.
@@ -1405,8 +1532,10 @@ TEST_F(InputHandlerProxyEventQueueTest, AckTouchActionNonBlockingForFling) {
// ScrollUpdate
{
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(Return(false));
+ }
EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
HandleGestureEvent(WebInputEvent::Type::kGestureScrollUpdate, delta);
@@ -1421,8 +1550,10 @@ TEST_F(InputHandlerProxyEventQueueTest, AckTouchActionNonBlockingForFling) {
EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
.WillOnce(Return(scroll_result_did_scroll));
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(Return(false));
+ }
EXPECT_CALL(mock_input_handler_,
GetSnapFlingInfoAndSetAnimatingSnapTarget(_, _, _))
.WillOnce(Return(false));
@@ -1443,9 +1574,9 @@ TEST_F(InputHandlerProxyEventQueueTest, AckTouchActionNonBlockingForFling) {
// the screen. If this touch hits a blocking region (e.g. touch-action or a
// non-passive touchstart listener), we won't actually treat it as blocking;
// because of the ongoing fling it will be treated as non blocking. However,
- // we also have to ensure that the whitelisted_touch_action reported is also
- // kAuto so that the browser knows that it shouldn't wait for an ACK with an
- // allowed touch-action before dispatching more scrolls.
+ // we also have to ensure that the allowed_touch_action reported is also kAuto
+ // so that the browser knows that it shouldn't wait for an ACK with an allowed
+ // touch-action before dispatching more scrolls.
{
// Simulate hitting a blocking region on the scrolling layer, as if there
// was a non-passive touchstart handler.
@@ -1465,12 +1596,11 @@ TEST_F(InputHandlerProxyEventQueueTest, AckTouchActionNonBlockingForFling) {
CreateWebTouchPoint(WebTouchPoint::State::kStatePressed, 10, 10);
// This is the call this test is checking: we expect that the client will
- // report the touch as non-blocking and also that the whitelisted touch
- // action matches the non blocking expectatithe whitelisted touch action
+ // report the touch as non-blocking and also that the allowed touch action
// matches the non blocking expectation (i.e. all touches are allowed).
EXPECT_CALL(
mock_client_,
- SetWhiteListedTouchAction(
+ SetAllowedTouchAction(
TouchAction::kAuto, touch_start->unique_touch_event_id,
InputHandlerProxy::DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING))
.WillOnce(Return());
@@ -1513,12 +1643,12 @@ TEST_P(InputHandlerProxyTest, HitTestTouchEventNullTouchAction) {
CreateWebTouchPoint(WebTouchPoint::State::kStatePressed, -10, 10);
bool is_touching_scrolling_layer;
- cc::TouchAction* white_listed_touch_action = nullptr;
- EXPECT_EQ(expected_disposition_, input_handler_->HitTestTouchEventForTest(
- touch, &is_touching_scrolling_layer,
- white_listed_touch_action));
+ cc::TouchAction* allowed_touch_action = nullptr;
+ EXPECT_EQ(expected_disposition_,
+ input_handler_->HitTestTouchEventForTest(
+ touch, &is_touching_scrolling_layer, allowed_touch_action));
EXPECT_TRUE(is_touching_scrolling_layer);
- EXPECT_TRUE(!white_listed_touch_action);
+ EXPECT_TRUE(!allowed_touch_action);
VERIFY_AND_RESET_MOCKS();
}
@@ -1544,8 +1674,8 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestNegative) {
return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER;
}));
EXPECT_CALL(mock_client_,
- SetWhiteListedTouchAction(cc::TouchAction::kPanUp, 1,
- InputHandlerProxy::DROP_EVENT))
+ SetAllowedTouchAction(cc::TouchAction::kPanUp, 1,
+ InputHandlerProxy::DROP_EVENT))
.WillOnce(testing::Return());
WebTouchEvent touch(WebInputEvent::Type::kTouchStart,
@@ -1590,8 +1720,8 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) {
return cc::InputHandler::TouchStartOrMoveEventListenerType::
HANDLER_ON_SCROLLING_LAYER;
}));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(cc::TouchAction::kPanY, 1,
- expected_disposition_))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(cc::TouchAction::kPanY, 1,
+ expected_disposition_))
.WillOnce(testing::Return());
// Since the second touch point hits a touch-region, there should be no
// hit-testing for the third touch point.
@@ -1637,9 +1767,9 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) {
*touch_action = cc::TouchAction::kPanX;
return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER;
}));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(
- cc::TouchAction::kPanRight, 1,
- InputHandlerProxy::DID_HANDLE_NON_BLOCKING))
+ EXPECT_CALL(mock_client_,
+ SetAllowedTouchAction(cc::TouchAction::kPanRight, 1,
+ InputHandlerProxy::DID_HANDLE_NON_BLOCKING))
.WillOnce(testing::Return());
WebTouchEvent touch(WebInputEvent::Type::kTouchStart,
@@ -1682,9 +1812,9 @@ TEST_P(InputHandlerProxyTest, TouchStartPassiveAndTouchEndBlocking) {
*touch_action = cc::TouchAction::kNone;
return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER;
}));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(
- cc::TouchAction::kNone, 1,
- InputHandlerProxy::DID_HANDLE_NON_BLOCKING))
+ EXPECT_CALL(mock_client_,
+ SetAllowedTouchAction(cc::TouchAction::kNone, 1,
+ InputHandlerProxy::DID_HANDLE_NON_BLOCKING))
.WillOnce(testing::Return());
WebTouchEvent touch(WebInputEvent::Type::kTouchStart,
@@ -1721,7 +1851,7 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) {
EXPECT_CALL(mock_input_handler_, EventListenerTypeForTouchStartOrMoveAt(_, _))
.WillOnce(testing::Return(
cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
WebTouchEvent touch(WebInputEvent::Type::kTouchStart,
@@ -1737,7 +1867,7 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) {
EXPECT_CALL(mock_input_handler_, EventListenerTypeForTouchStartOrMoveAt(_, _))
.WillOnce(testing::Return(
cc::InputHandler::TouchStartOrMoveEventListenerType::HANDLER));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
touch.SetType(WebInputEvent::Type::kTouchMove);
@@ -1750,6 +1880,487 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) {
VERIFY_AND_RESET_MOCKS();
}
+class UnifiedScrollingInputHandlerProxyTest : public testing::Test {
+ public:
+ using ElementId = cc::ElementId;
+ using ElementIdType = cc::ElementIdType;
+ using EventDisposition = InputHandlerProxy::EventDisposition;
+ using EventDispositionCallback = InputHandlerProxy::EventDispositionCallback;
+ using LatencyInfo = ui::LatencyInfo;
+ using ScrollGranularity = ui::ScrollGranularity;
+ using ScrollState = cc::ScrollState;
+ using ReturnedDisposition = base::Optional<EventDisposition>;
+
+ UnifiedScrollingInputHandlerProxyTest()
+ : input_handler_proxy_(&mock_input_handler_,
+ &mock_client_,
+ /*force_input_to_main_thread=*/false) {}
+
+ void SetUp() override {
+ scoped_feature_list_.InitAndEnableFeature(features::kScrollUnification);
+ }
+
+ std::unique_ptr<WebCoalescedInputEvent> ScrollBegin() {
+ auto gsb = std::make_unique<WebGestureEvent>(
+ WebInputEvent::Type::kGestureScrollBegin, WebInputEvent::kNoModifiers,
+ TimeForInputEvents(), WebGestureDevice::kTouchpad);
+ gsb->data.scroll_begin.scrollable_area_element_id = 0;
+ gsb->data.scroll_begin.main_thread_hit_tested = false;
+ ;
+ gsb->data.scroll_begin.delta_x_hint = 0;
+ gsb->data.scroll_begin.delta_y_hint = 10;
+ gsb->data.scroll_begin.pointer_count = 0;
+
+ LatencyInfo unused;
+ return std::make_unique<WebCoalescedInputEvent>(std::move(gsb), unused);
+ }
+
+ std::unique_ptr<WebCoalescedInputEvent> ScrollUpdate() {
+ auto gsu = std::make_unique<WebGestureEvent>(
+ WebInputEvent::Type::kGestureScrollUpdate, WebInputEvent::kNoModifiers,
+ TimeForInputEvents(), WebGestureDevice::kTouchpad);
+ gsu->data.scroll_update.delta_x = 0;
+ gsu->data.scroll_update.delta_y = 10;
+
+ LatencyInfo unused;
+ return std::make_unique<WebCoalescedInputEvent>(std::move(gsu), unused);
+ }
+
+ std::unique_ptr<WebCoalescedInputEvent> ScrollEnd() {
+ auto gse = std::make_unique<WebGestureEvent>(
+ WebInputEvent::Type::kGestureScrollEnd, WebInputEvent::kNoModifiers,
+ TimeForInputEvents(), WebGestureDevice::kTouchpad);
+
+ LatencyInfo unused;
+ return std::make_unique<WebCoalescedInputEvent>(std::move(gse), unused);
+ }
+
+ void DispatchEvent(std::unique_ptr<blink::WebCoalescedInputEvent> event,
+ ReturnedDisposition* out_disposition = nullptr) {
+ input_handler_proxy_.HandleInputEventWithLatencyInfo(
+ std::move(event), BindEventHandledCallback(out_disposition));
+ }
+
+ void ContinueScrollBeginAfterMainThreadHitTest(
+ std::unique_ptr<WebCoalescedInputEvent> event,
+ cc::ElementIdType hit_test_result,
+ ReturnedDisposition* out_disposition = nullptr) {
+ input_handler_proxy_.ContinueScrollBeginAfterMainThreadHitTest(
+ std::move(event), BindEventHandledCallback(out_disposition),
+ hit_test_result);
+ }
+
+ bool MainThreadHitTestInProgress() const {
+ return input_handler_proxy_.hit_testing_scroll_begin_on_main_thread_;
+ }
+
+ void BeginFrame() {
+ constexpr base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16);
+ base::TimeTicks frame_time =
+ TimeForInputEvents() +
+ (next_begin_frame_number_ - viz::BeginFrameArgs::kStartingFrameNumber) *
+ interval;
+ input_handler_proxy_.DeliverInputForBeginFrame(viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL));
+ }
+
+ cc::InputHandlerScrollResult DidScrollResult() const {
+ cc::InputHandlerScrollResult result;
+ result.did_scroll = true;
+ return result;
+ }
+
+ protected:
+ NiceMock<MockInputHandler> mock_input_handler_;
+ NiceMock<MockInputHandlerProxyClient> mock_client_;
+
+ private:
+ void EventHandledCallback(
+ ReturnedDisposition* out_disposition,
+ EventDisposition event_disposition,
+ std::unique_ptr<WebCoalescedInputEvent> input_event,
+ std::unique_ptr<InputHandlerProxy::DidOverscrollParams> overscroll_params,
+ const WebInputEventAttribution& attribution) {
+ if (out_disposition)
+ *out_disposition = event_disposition;
+ }
+
+ EventDispositionCallback BindEventHandledCallback(
+ ReturnedDisposition* out_disposition = nullptr) {
+ return base::BindOnce(
+ &UnifiedScrollingInputHandlerProxyTest::EventHandledCallback,
+ weak_ptr_factory_.GetWeakPtr(), out_disposition);
+ }
+
+ base::TimeTicks TimeForInputEvents() const {
+ return WebInputEvent::GetStaticTimeStampForTests();
+ }
+
+ InputHandlerProxy input_handler_proxy_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ base::SimpleTestTickClock tick_clock_;
+ uint64_t next_begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
+ base::WeakPtrFactory<UnifiedScrollingInputHandlerProxyTest> weak_ptr_factory_{
+ this};
+};
+
+// Test that when a main thread hit test is requested, the InputHandlerProxy
+// starts queueing incoming gesture event and the compositor queue is blocked
+// until the hit test is satisfied.
+TEST_F(UnifiedScrollingInputHandlerProxyTest, MainThreadHitTestRequired) {
+ // The hit testing state shouldn't be entered until one is actually requested.
+ EXPECT_FALSE(MainThreadHitTestInProgress());
+
+ // Inject a GSB that returns RequiresMainThreadHitTest.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState));
+
+ ReturnedDisposition disposition;
+ DispatchEvent(ScrollBegin(), &disposition);
+
+ EXPECT_TRUE(MainThreadHitTestInProgress());
+ EXPECT_EQ(InputHandlerProxy::REQUIRES_MAIN_THREAD_HIT_TEST, *disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ ReturnedDisposition gsu1_disposition;
+ ReturnedDisposition gsu2_disposition;
+
+ // Now inject a GSU. This should be queued.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
+
+ DispatchEvent(ScrollUpdate(), &gsu1_disposition);
+ EXPECT_FALSE(gsu1_disposition);
+
+ // Ensure the queue is blocked; a BeginFrame doesn't cause event dispatch.
+ BeginFrame();
+ EXPECT_FALSE(gsu1_disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Inject a second GSU; it should be coalesced and also queued.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
+
+ DispatchEvent(ScrollUpdate(), &gsu2_disposition);
+ EXPECT_FALSE(gsu2_disposition);
+
+ // Ensure the queue is blocked.
+ BeginFrame();
+ EXPECT_FALSE(gsu2_disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ EXPECT_TRUE(MainThreadHitTestInProgress());
+
+ // The hit test reply arrives. Ensure we call ScrollBegin and unblock the
+ // queue.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kImplThreadScrollState));
+
+ // Additionally, the queue should be flushed by
+ // ContinueScrollBeginAfterMainThreadHitTest so that the GSUs dispatched
+ // earlier will now handled.
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
+ .WillOnce(Return(DidScrollResult()));
+
+ // Ensure we don't spurriously call ScrollEnd (because we think we're
+ // already in a scroll from the first GSB).
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(0);
+
+ ReturnedDisposition disposition;
+ const ElementIdType kHitTestResult = 12345;
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(), kHitTestResult,
+ &disposition);
+
+ // The ScrollBegin should have been immediately re-injected and queue
+ // flushed.
+ EXPECT_FALSE(MainThreadHitTestInProgress());
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gsu1_disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gsu2_disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Injecting a new GSU should cause queueing and dispatching as usual.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
+ .WillOnce(Return(DidScrollResult()));
+
+ ReturnedDisposition disposition;
+ DispatchEvent(ScrollUpdate(), &disposition);
+ EXPECT_FALSE(disposition);
+
+ BeginFrame();
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Finish the scroll.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(1);
+ ReturnedDisposition disposition;
+ DispatchEvent(ScrollEnd(), &disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ EXPECT_FALSE(MainThreadHitTestInProgress());
+}
+
+// Test to ensure that a main thread hit test sets the correct flags on the
+// re-injected GestureScrollBegin.
+TEST_F(UnifiedScrollingInputHandlerProxyTest, MainThreadHitTestEvent) {
+ // Inject a GSB that returns RequiresMainThreadHitTest.
+ {
+ // Ensure that by default we don't set a target. The
+ // |is_main_thread_hit_tested| property should default to false.
+ EXPECT_CALL(
+ mock_input_handler_,
+ ScrollBegin(
+ AllOf(Property(&ScrollState::target_element_id, Eq(ElementId())),
+ Property(&ScrollState::is_main_thread_hit_tested, Eq(false))),
+ _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState));
+ DispatchEvent(ScrollBegin());
+ ASSERT_TRUE(MainThreadHitTestInProgress());
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // The hit test reply arrives. Ensure we call ScrollBegin with the ElementId
+ // from the hit test and the main_thread
+ {
+ const ElementId kHitTestResult(12345);
+
+ EXPECT_CALL(
+ mock_input_handler_,
+ ScrollBegin(
+ AllOf(Property(&ScrollState::target_element_id, Eq(kHitTestResult)),
+ Property(&ScrollState::is_main_thread_hit_tested, Eq(true))),
+ _))
+ .Times(1);
+
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(),
+ kHitTestResult.GetStableId());
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+}
+
+// Test to ensure that a main thread hit test counts the correct number of
+// scrolls for metrics.
+TEST_F(UnifiedScrollingInputHandlerProxyTest, MainThreadHitTestMetrics) {
+ // Inject a GSB that returns RequiresMainThreadHitTest followed by a GSU and
+ // a GSE.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState))
+ .WillOnce(Return(kImplThreadScrollState));
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(1);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(1);
+
+ // The record begin/end should be called exactly once.
+ EXPECT_CALL(mock_input_handler_, RecordScrollBegin(_, _)).Times(1);
+ EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
+
+ DispatchEvent(ScrollBegin());
+ EXPECT_TRUE(MainThreadHitTestInProgress());
+ DispatchEvent(ScrollUpdate());
+ DispatchEvent(ScrollEnd());
+
+ // Hit test reply.
+ const ElementIdType kHitTestResult = 12345;
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(), kHitTestResult);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Ensure we don't record either a begin or an end if the hit test fails.
+ // TODO(bokan): Though it looks odd, it appears that today we do record the
+ // scrolling thread if the scroll is dropped. We should fix that but in the
+ // mean-time we add a test for the unified path in this case.
+ // https://crbug.com/1082601.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState));
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(0);
+
+ EXPECT_CALL(mock_input_handler_, RecordScrollBegin(_, _)).Times(1);
+ EXPECT_CALL(mock_input_handler_, RecordScrollEnd(_)).Times(1);
+
+ DispatchEvent(ScrollBegin());
+ EXPECT_TRUE(MainThreadHitTestInProgress());
+ DispatchEvent(ScrollUpdate());
+ DispatchEvent(ScrollEnd());
+
+ // Hit test reply failed.
+ const ElementIdType kHitTestResult = 0;
+ ASSERT_FALSE(ElementId::IsValid(kHitTestResult));
+
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(), kHitTestResult);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+}
+
+// Test the case where a main thread hit test is in progress on the main thread
+// and a GSE and new GSB arrive.
+TEST_F(UnifiedScrollingInputHandlerProxyTest,
+ ScrollEndAndBeginsDuringMainThreadHitTest) {
+ ReturnedDisposition gsb1_disposition;
+ ReturnedDisposition gsu1_disposition;
+ ReturnedDisposition gse1_disposition;
+ ReturnedDisposition gsb2_disposition;
+ ReturnedDisposition gsu2_disposition;
+ ReturnedDisposition gse2_disposition;
+
+ // Inject a GSB that returns RequiresMainThreadHitTest followed by a GSU and
+ // GSE that get queued.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState));
+ DispatchEvent(ScrollBegin(), &gsb1_disposition);
+ ASSERT_TRUE(MainThreadHitTestInProgress());
+ ASSERT_EQ(InputHandlerProxy::REQUIRES_MAIN_THREAD_HIT_TEST,
+ *gsb1_disposition);
+
+ DispatchEvent(ScrollUpdate(), &gsu1_disposition);
+ DispatchEvent(ScrollEnd(), &gse1_disposition);
+
+ // The queue is blocked so none of the events should be processed.
+ BeginFrame();
+
+ ASSERT_FALSE(gsu1_disposition);
+ ASSERT_FALSE(gse1_disposition);
+ }
+
+ // Inject another group of GSB, GSU, GSE. They should all be queued.
+ {
+ DispatchEvent(ScrollBegin(), &gsb2_disposition);
+ DispatchEvent(ScrollUpdate(), &gsu2_disposition);
+ DispatchEvent(ScrollEnd(), &gse2_disposition);
+
+ // The queue is blocked so none of the events should be processed.
+ BeginFrame();
+
+ EXPECT_FALSE(gsb2_disposition);
+ EXPECT_FALSE(gsu2_disposition);
+ EXPECT_FALSE(gse2_disposition);
+ }
+
+ ASSERT_TRUE(MainThreadHitTestInProgress());
+
+ // The hit test reply arrives. Ensure we call ScrollBegin and unblock the
+ // queue.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(kImplThreadScrollState));
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(DidScrollResult()));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(2);
+
+ ReturnedDisposition disposition;
+ const ElementIdType kHitTestResult = 12345;
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(), kHitTestResult,
+ &disposition);
+
+ // The ScrollBegin should have been immediately re-injected and queue
+ // flushed.
+ EXPECT_FALSE(MainThreadHitTestInProgress());
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gsu1_disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gse1_disposition);
+
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gsb2_disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gsu2_disposition);
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *gse2_disposition);
+
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+}
+
+// Test the case where a main thread hit test returns a null element_id. In
+// this case we should reset the state and unblock the queue.
+TEST_F(UnifiedScrollingInputHandlerProxyTest, MainThreadHitTestFailed) {
+ ReturnedDisposition gsu1_disposition;
+
+ // Inject a GSB that returns RequiresMainThreadHitTest.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kRequiresMainThreadHitTestState));
+ DispatchEvent(ScrollBegin());
+ DispatchEvent(ScrollUpdate(), &gsu1_disposition);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // The hit test reply arrives with an invalid ElementId. We shouldn't call
+ // ScrollBegin nor ScrollUpdate. Both should be dropped without reaching the
+ // input handler.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(_)).Times(0);
+
+ const ElementIdType kHitTestResult = 0;
+ ASSERT_FALSE(ElementId::IsValid(kHitTestResult));
+
+ ReturnedDisposition gsb_disposition;
+ ContinueScrollBeginAfterMainThreadHitTest(ScrollBegin(), kHitTestResult,
+ &gsb_disposition);
+
+ EXPECT_EQ(InputHandlerProxy::DROP_EVENT, *gsb_disposition);
+ EXPECT_EQ(InputHandlerProxy::DROP_EVENT, *gsu1_disposition);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Send a new GSU, ensure it's dropped without queueing since there's no
+ // scroll in progress.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _)).Times(0);
+
+ ReturnedDisposition disposition;
+ DispatchEvent(ScrollUpdate(), &disposition);
+ EXPECT_EQ(InputHandlerProxy::DROP_EVENT, *disposition);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+
+ // Ensure there's no left-over bad state by sending a new GSB+GSU which
+ // should be handled by the input handler immediately. A following GSU should
+ // be queued and dispatched at BeginFrame.
+ {
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
+ .WillOnce(Return(kImplThreadScrollState));
+ EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _))
+ .WillOnce(Return(DidScrollResult()))
+ .WillOnce(Return(DidScrollResult()));
+
+ // Note: The first GSU after a GSB is dispatched immediately without
+ // queueing.
+ ReturnedDisposition disposition;
+ DispatchEvent(ScrollBegin(), &disposition);
+ DispatchEvent(ScrollUpdate());
+
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+ disposition = base::nullopt;
+
+ DispatchEvent(ScrollUpdate(), &disposition);
+ EXPECT_FALSE(disposition);
+
+ BeginFrame();
+ EXPECT_EQ(InputHandlerProxy::DID_HANDLE, *disposition);
+ Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ }
+}
+
TEST(SynchronousInputHandlerProxyTest, StartupShutdown) {
testing::StrictMock<MockInputHandler> mock_input_handler;
testing::StrictMock<MockInputHandlerProxyClient> mock_client;
@@ -1886,8 +2497,10 @@ TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScroll) {
EXPECT_EQ(1ul, event_disposition_recorder_.size());
testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -1930,8 +2543,10 @@ TEST_F(InputHandlerProxyEventQueueTest,
mock_input_handler_,
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -1958,8 +2573,10 @@ TEST_F(InputHandlerProxyEventQueueTest,
mock_input_handler_,
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillRepeatedly(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillRepeatedly(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2011,8 +2628,10 @@ TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedQueueingTime) {
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2151,8 +2770,10 @@ TEST_F(InputHandlerProxyEventQueueTest, OriginalEventsTracing) {
.Times(2);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput())
.Times(::testing::AtLeast(1));
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillRepeatedly(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillRepeatedly(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2236,8 +2857,10 @@ TEST_F(InputHandlerProxyEventQueueTest, TouchpadGestureScrollEndFlushQueue) {
mock_input_handler_,
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(2);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillRepeatedly(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillRepeatedly(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2304,8 +2927,10 @@ TEST_F(InputHandlerProxyEventQueueTest, CoalescedLatencyInfo) {
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2346,8 +2971,10 @@ TEST_F(InputHandlerProxyEventQueueTest, CoalescedEventSwitchToMainThread) {
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnMain))
.Times(1);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2416,8 +3043,10 @@ TEST_F(InputHandlerProxyEventQueueTest, ScrollPredictorTest) {
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillOnce(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillOnce(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2475,8 +3104,10 @@ TEST_F(InputHandlerProxyEventQueueTest, DeliverInputWithHighLatencyMode) {
RecordScrollBegin(_, cc::ScrollBeginThreadState::kScrollingOnCompositor))
.Times(1);
EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
- EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
- .WillRepeatedly(testing::Return(false));
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+ .WillRepeatedly(testing::Return(false));
+ }
EXPECT_CALL(
mock_input_handler_,
ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
@@ -2574,7 +3205,7 @@ TEST_P(InputHandlerProxyMainThreadScrollingReasonTest,
mock_input_handler_,
GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove))
.WillOnce(testing::Return(cc::EventListenerProperties::kPassive));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
@@ -2621,7 +3252,7 @@ TEST_P(InputHandlerProxyMainThreadScrollingReasonTest,
.WillOnce(
testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType::
HANDLER_ON_SCROLLING_LAYER));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
@@ -2673,7 +3304,7 @@ TEST_P(InputHandlerProxyMainThreadScrollingReasonTest,
.WillOnce(
testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType::
HANDLER_ON_SCROLLING_LAYER));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
@@ -2721,7 +3352,7 @@ TEST_P(InputHandlerProxyMainThreadScrollingReasonTest,
testing::Property(&gfx::Point::x, testing::Gt(0)), _))
.WillOnce(testing::Return(
cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER));
- EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(_, _, _))
+ EXPECT_CALL(mock_client_, SetAllowedTouchAction(_, _, _))
.WillOnce(testing::Return());
EXPECT_CALL(mock_input_handler_, GetEventListenerProperties(_))
.WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive));
@@ -3126,9 +3757,10 @@ class InputHandlerProxyMomentumScrollJankTest : public testing::Test {
protected:
void HandleGesture(std::unique_ptr<WebInputEvent> event) {
- ui::LatencyInfo latency;
input_handler_proxy_.HandleInputEventWithLatencyInfo(
- std::move(event), latency, base::DoNothing());
+ std::make_unique<WebCoalescedInputEvent>(std::move(event),
+ ui::LatencyInfo()),
+ base::DoNothing());
}
uint64_t next_begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
@@ -3372,12 +4004,33 @@ TEST_F(InputHandlerProxyMomentumScrollJankTest, TestNonMomentumNoJank) {
0);
}
-INSTANTIATE_TEST_SUITE_P(AnimateInput,
+const auto kTestCombinations = testing::Combine(
+ testing::Values(ScrollerType::kRoot, ScrollerType::kChild),
+ testing::Values(HandlerType::kNormal, HandlerType::kSynchronous),
+ testing::Values(ScrollUnification::kEnabled, ScrollUnification::kDisabled));
+
+const auto kSuffixGenerator =
+ [](const testing::TestParamInfo<
+ std::tuple<ScrollerType, HandlerType, ScrollUnification>>& info) {
+ std::string name = std::get<1>(info.param) == HandlerType::kSynchronous
+ ? "Synchronous"
+ : "";
+ name += std::get<0>(info.param) == ScrollerType::kRoot ? "Root" : "Child";
+ name += std::get<2>(info.param) == ScrollUnification::kEnabled
+ ? "UnifiedScroll"
+ : "LegacyScroll";
+ return name;
+ };
+
+INSTANTIATE_TEST_SUITE_P(All,
InputHandlerProxyTest,
- testing::ValuesIn(test_types));
+ kTestCombinations,
+ kSuffixGenerator);
-INSTANTIATE_TEST_SUITE_P(AnimateInput,
+INSTANTIATE_TEST_SUITE_P(All,
InputHandlerProxyMainThreadScrollingReasonTest,
- testing::ValuesIn(test_types));
+ kTestCombinations,
+ kSuffixGenerator);
+
} // namespace test
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/input_scroll_elasticity_controller_unittest.cc b/chromium/third_party/blink/renderer/platform/widget/input/input_scroll_elasticity_controller_unittest.cc
index a357bd2852e..ff1abf50eaf 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/input_scroll_elasticity_controller_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/input_scroll_elasticity_controller_unittest.cc
@@ -46,6 +46,8 @@ class MockScrollElasticityHelper : public cc::ScrollElasticityHelper {
set_stretch_amount_count_ += 1;
stretch_amount_ = stretch_amount;
}
+
+ gfx::Size ScrollBounds() const override { return gfx::Size(800, 600); }
gfx::ScrollOffset ScrollOffset() const override { return scroll_offset_; }
gfx::ScrollOffset MaxScrollOffset() const override {
return max_scroll_offset_;
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.cc b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.cc
index d63cbf3b054..027077f21fc 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.cc
@@ -10,9 +10,11 @@
// TODO(arakeri): This is where all the overscroll specific code will go.
namespace blink {
+constexpr float kOverscrollBoundaryMultiplier = 0.1f;
+
OverscrollBounceController::OverscrollBounceController(
cc::ScrollElasticityHelper* helper)
- : weak_factory_(this) {}
+ : state_(kStateInactive), helper_(helper), weak_factory_(this) {}
OverscrollBounceController::~OverscrollBounceController() = default;
@@ -21,12 +23,149 @@ OverscrollBounceController::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
-void OverscrollBounceController::ObserveGestureEventAndResult(
- const blink::WebGestureEvent& gesture_event,
- const cc::InputHandlerScrollResult& scroll_result) {}
-
void OverscrollBounceController::Animate(base::TimeTicks time) {}
-void OverscrollBounceController::ReconcileStretchAndScroll() {}
+// TODO(arakeri): ReconcileStretchAndScroll implementations in both the classes
+// InputScrollElasticityController and OverscrollBounceController have common
+// code that needs to be evaluated and moved up into the base class.
+void OverscrollBounceController::ReconcileStretchAndScroll() {
+ const gfx::Vector2dF stretch = helper_->StretchAmount();
+ if (stretch.IsZero())
+ return;
+
+ const gfx::ScrollOffset scroll_offset = helper_->ScrollOffset();
+ const gfx::ScrollOffset max_scroll_offset = helper_->MaxScrollOffset();
+
+ float scroll_adjustment_x = 0;
+ if (stretch.x() < 0.f)
+ scroll_adjustment_x = scroll_offset.x();
+ else if (stretch.x() > 0.f)
+ scroll_adjustment_x = max_scroll_offset.x() - scroll_offset.x();
+
+ float scroll_adjustment_y = 0;
+ if (stretch.y() < 0.f)
+ scroll_adjustment_y = scroll_offset.y();
+ else if (stretch.y() > 0.f)
+ scroll_adjustment_y = max_scroll_offset.y() - scroll_offset.y();
+
+ if (state_ == kStateActiveScroll) {
+ // During an active scroll, we want to reduce |accumulated_scroll_delta_| by
+ // the amount that was scrolled (but we don't want to over-consume, so limit
+ // it by the amount of |accumulated_scroll_delta_|).
+ scroll_adjustment_x = std::copysign(
+ std::min(std::abs(accumulated_scroll_delta_.x()), scroll_adjustment_x),
+ stretch.x());
+ scroll_adjustment_y = std::copysign(
+ std::min(std::abs(accumulated_scroll_delta_.y()), scroll_adjustment_y),
+ stretch.y());
+
+ accumulated_scroll_delta_ -=
+ gfx::Vector2dF(scroll_adjustment_x, scroll_adjustment_y);
+ helper_->SetStretchAmount(OverscrollBounceDistance(
+ accumulated_scroll_delta_, helper_->ScrollBounds()));
+ }
+
+ helper_->ScrollBy(gfx::Vector2dF(scroll_adjustment_x, scroll_adjustment_y));
+}
+
+// Returns the maximum amount to be overscrolled.
+gfx::Vector2dF OverscrollBounceController::OverscrollBoundary(
+ const gfx::Size& scroller_bounds) const {
+ return gfx::Vector2dF(
+ scroller_bounds.width() * kOverscrollBoundaryMultiplier,
+ scroller_bounds.height() * kOverscrollBoundaryMultiplier);
+}
+
+// The goal of this calculation is to map the distance the user has scrolled
+// past the boundary into the distance to actually scroll the elastic scroller.
+gfx::Vector2d OverscrollBounceController::OverscrollBounceDistance(
+ const gfx::Vector2dF& distance_overscrolled,
+ const gfx::Size& scroller_bounds) const {
+ // TODO(arakeri): This should change as you pinch zoom in.
+ gfx::Vector2dF overscroll_boundary = OverscrollBoundary(scroller_bounds);
+
+ // We use the tanh function in addition to the mapping, which gives it more of
+ // a spring effect. However, we want to use tanh's range from [0, 2], so we
+ // multiply the value we provide to tanh by 2.
+
+ // Also, it may happen that the scroller_bounds are 0 if the viewport scroll
+ // nodes are null (see: ScrollElasticityHelper::ScrollBounds). We therefore
+ // have to check in order to avoid a divide by 0.
+ gfx::Vector2d overbounce_distance;
+ if (scroller_bounds.width() > 0.f) {
+ overbounce_distance.set_x(
+ tanh(2 * distance_overscrolled.x() / scroller_bounds.width()) *
+ overscroll_boundary.x());
+ }
+
+ if (scroller_bounds.height() > 0.f) {
+ overbounce_distance.set_y(
+ tanh(2 * distance_overscrolled.y() / scroller_bounds.height()) *
+ overscroll_boundary.y());
+ }
+ return overbounce_distance;
+}
+
+void OverscrollBounceController::EnterStateActiveScroll() {
+ state_ = kStateActiveScroll;
+}
+
+void OverscrollBounceController::ObserveRealScrollBegin(
+ const blink::WebGestureEvent& gesture_event) {
+ if (gesture_event.data.scroll_begin.inertial_phase ==
+ blink::WebGestureEvent::InertialPhaseState::kNonMomentum &&
+ gesture_event.data.scroll_begin.delta_hint_units ==
+ ui::ScrollGranularity::kScrollByPrecisePixel) {
+ EnterStateActiveScroll();
+ }
+}
+
+void OverscrollBounceController::ObserveRealScrollEnd() {
+ state_ = kStateInactive;
+}
+
+void OverscrollBounceController::OverscrollIfNecessary(
+ const gfx::Vector2dF& overscroll_delta) {
+ accumulated_scroll_delta_ += overscroll_delta;
+ gfx::Vector2d overbounce_distance = OverscrollBounceDistance(
+ accumulated_scroll_delta_, helper_->ScrollBounds());
+ helper_->SetStretchAmount(overbounce_distance);
+}
+
+void OverscrollBounceController::ObserveScrollUpdate(
+ const gfx::Vector2dF& unused_scroll_delta) {
+ if (state_ == kStateInactive)
+ return;
+
+ if (state_ == kStateActiveScroll) {
+ // TODO(arakeri): Implement animate back.
+ OverscrollIfNecessary(unused_scroll_delta);
+ }
+}
+
+void OverscrollBounceController::ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result) {
+ switch (gesture_event.GetType()) {
+ case blink::WebInputEvent::Type::kGestureScrollBegin: {
+ if (gesture_event.data.scroll_begin.synthetic)
+ return;
+ ObserveRealScrollBegin(gesture_event);
+ break;
+ }
+ case blink::WebInputEvent::Type::kGestureScrollUpdate: {
+ gfx::Vector2dF event_delta(-gesture_event.data.scroll_update.delta_x,
+ -gesture_event.data.scroll_update.delta_y);
+ ObserveScrollUpdate(scroll_result.unused_scroll_delta);
+ break;
+ }
+ case blink::WebInputEvent::Type::kGestureScrollEnd: {
+ ObserveRealScrollEnd();
+ break;
+ }
+ default:
+ break;
+ }
+}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h
index 1231c483aaa..71b16b07556 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h
+++ b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h
@@ -7,19 +7,17 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "cc/input/input_handler.h"
#include "cc/input/overscroll_behavior.h"
#include "cc/input/scroll_elasticity_helper.h"
#include "third_party/blink/public/common/input/web_gesture_event.h"
#include "third_party/blink/public/platform/input/elastic_overscroll_controller.h"
-namespace cc {
-struct InputHandlerScrollResult;
-} // namespace cc
-
namespace blink {
// The overbounce version of elastic overscrolling mimics Windows style
// overscroll animations.
-class OverscrollBounceController : public ElasticOverscrollController {
+class BLINK_PLATFORM_EXPORT OverscrollBounceController
+ : public ElasticOverscrollController {
public:
explicit OverscrollBounceController(cc::ScrollElasticityHelper* helper);
~OverscrollBounceController() override;
@@ -31,8 +29,37 @@ class OverscrollBounceController : public ElasticOverscrollController {
const cc::InputHandlerScrollResult& scroll_result) override;
void Animate(base::TimeTicks time) override;
void ReconcileStretchAndScroll() override;
+ gfx::Vector2d OverscrollBounceDistance(
+ const gfx::Vector2dF& distance_overscrolled,
+ const gfx::Size& scroller_bounds) const;
private:
+ void ObserveRealScrollBegin(const blink::WebGestureEvent& gesture_event);
+ void ObserveRealScrollEnd();
+ gfx::Vector2dF OverscrollBoundary(const gfx::Size& scroller_bounds) const;
+ void EnterStateActiveScroll();
+ void OverscrollIfNecessary(const gfx::Vector2dF& overscroll_delta);
+
+ void ObserveScrollUpdate(const gfx::Vector2dF& unused_scroll_delta);
+
+ enum State {
+ // The initial state, during which the overscroll amount is zero.
+ kStateInactive,
+ // ActiveScroll indicates that this controller is listening to future GSU
+ // events, and those events may or may not update the overscroll amount.
+ // This occurs when the user is actively panning either via a touchscreen or
+ // touchpad, or is an active fling that has not triggered an overscroll.
+ kStateActiveScroll,
+ };
+
+ State state_;
+ cc::ScrollElasticityHelper* helper_;
+
+ // This is the accumulated raw delta in pixels that's been overscrolled. It
+ // will be fed into a tanh function (ranging [0, 2]) that decides the stretch
+ // bounds.
+ gfx::Vector2dF accumulated_scroll_delta_;
+
base::WeakPtrFactory<OverscrollBounceController> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(OverscrollBounceController);
};
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller_unittest.cc b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller_unittest.cc
new file mode 100644
index 00000000000..bf3d374aee6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright (C) Microsoft Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a fork of the input_scroll_elasticity_controller_unittest.cc.
+
+#include "third_party/blink/renderer/platform/widget/input/overscroll_bounce_controller.h"
+
+#include "cc/input/input_handler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
+
+namespace blink {
+namespace test {
+
+class MockScrollElasticityHelper : public cc::ScrollElasticityHelper {
+ public:
+ MockScrollElasticityHelper() = default;
+ ~MockScrollElasticityHelper() override = default;
+
+ // cc::ScrollElasticityHelper implementation:
+ gfx::Size ScrollBounds() const override { return gfx::Size(1000, 1000); }
+ bool IsUserScrollable() const override { return false; }
+ gfx::Vector2dF StretchAmount() const override { return stretch_amount_; }
+ void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override {
+ stretch_amount_ = stretch_amount;
+ }
+ void ScrollBy(const gfx::Vector2dF& delta) override {
+ scroll_offset_ += gfx::ScrollOffset(delta);
+ }
+ void RequestOneBeginFrame() override {}
+ gfx::ScrollOffset ScrollOffset() const override { return scroll_offset_; }
+ gfx::ScrollOffset MaxScrollOffset() const override {
+ return max_scroll_offset_;
+ }
+
+ void SetScrollOffsetAndMaxScrollOffset(
+ const gfx::ScrollOffset& scroll_offset,
+ const gfx::ScrollOffset& max_scroll_offset) {
+ scroll_offset_ = scroll_offset;
+ max_scroll_offset_ = max_scroll_offset;
+ }
+
+ private:
+ gfx::Vector2dF stretch_amount_;
+ gfx::ScrollOffset scroll_offset_, max_scroll_offset_;
+};
+
+class OverscrollBounceControllerTest : public testing::Test {
+ public:
+ OverscrollBounceControllerTest() : controller_(&helper_) {}
+ ~OverscrollBounceControllerTest() override = default;
+
+ void SetUp() override {}
+
+ void SendGestureScrollBegin(
+ WebGestureEvent::InertialPhaseState inertialPhase) {
+ WebGestureEvent event(WebInputEvent::Type::kGestureScrollBegin,
+ WebInputEvent::kNoModifiers, base::TimeTicks(),
+ WebGestureDevice::kTouchpad);
+ event.data.scroll_begin.inertial_phase = inertialPhase;
+
+ controller_.ObserveGestureEventAndResult(event,
+ cc::InputHandlerScrollResult());
+ }
+
+ void SendGestureScrollUpdate(
+ WebGestureEvent::InertialPhaseState inertialPhase,
+ const gfx::Vector2dF& scroll_delta,
+ const gfx::Vector2dF& unused_scroll_delta) {
+ blink::WebGestureEvent event(WebInputEvent::Type::kGestureScrollUpdate,
+ WebInputEvent::kNoModifiers, base::TimeTicks(),
+ blink::WebGestureDevice::kTouchpad);
+ event.data.scroll_update.inertial_phase = inertialPhase;
+ event.data.scroll_update.delta_x = -scroll_delta.x();
+ event.data.scroll_update.delta_y = -scroll_delta.y();
+
+ cc::InputHandlerScrollResult scroll_result;
+ scroll_result.did_overscroll_root = !unused_scroll_delta.IsZero();
+ scroll_result.unused_scroll_delta = unused_scroll_delta;
+
+ controller_.ObserveGestureEventAndResult(event, scroll_result);
+ }
+ void SendGestureScrollEnd() {
+ WebGestureEvent event(WebInputEvent::Type::kGestureScrollEnd,
+ WebInputEvent::kNoModifiers, base::TimeTicks(),
+ WebGestureDevice::kTouchpad);
+
+ controller_.ObserveGestureEventAndResult(event,
+ cc::InputHandlerScrollResult());
+ }
+
+ MockScrollElasticityHelper helper_;
+ OverscrollBounceController controller_;
+};
+
+// Tests the bounds of the overscroll and that the "StretchAmount" returns back
+// to 0 once the overscroll is done.
+TEST_F(OverscrollBounceControllerTest, VerifyOverscrollStretch) {
+ // Test vertical overscroll.
+ SendGestureScrollBegin(WebGestureEvent::InertialPhaseState::kNonMomentum);
+ gfx::Vector2dF delta(0, -50);
+ EXPECT_EQ(gfx::Vector2dF(0, 0), helper_.StretchAmount());
+ SendGestureScrollUpdate(WebGestureEvent::InertialPhaseState::kNonMomentum,
+ delta, gfx::Vector2dF(0, -100));
+ EXPECT_EQ(gfx::Vector2dF(0, -19), helper_.StretchAmount());
+ SendGestureScrollUpdate(WebGestureEvent::InertialPhaseState::kNonMomentum,
+ delta, gfx::Vector2dF(0, 100));
+ EXPECT_EQ(gfx::Vector2dF(0, 0), helper_.StretchAmount());
+ SendGestureScrollEnd();
+
+ // Test horizontal overscroll.
+ SendGestureScrollBegin(WebGestureEvent::InertialPhaseState::kNonMomentum);
+ delta = gfx::Vector2dF(-50, 0);
+ EXPECT_EQ(gfx::Vector2dF(0, 0), helper_.StretchAmount());
+ SendGestureScrollUpdate(WebGestureEvent::InertialPhaseState::kNonMomentum,
+ delta, gfx::Vector2dF(-100, 0));
+ EXPECT_EQ(gfx::Vector2dF(-19, 0), helper_.StretchAmount());
+ SendGestureScrollUpdate(WebGestureEvent::InertialPhaseState::kNonMomentum,
+ delta, gfx::Vector2dF(100, 0));
+ EXPECT_EQ(gfx::Vector2dF(0, 0), helper_.StretchAmount());
+ SendGestureScrollEnd();
+}
+
+// Verify that ReconcileStretchAndScroll reduces the overscrolled delta.
+TEST_F(OverscrollBounceControllerTest, ReconcileStretchAndScroll) {
+ // Test overscroll in both directions.
+ gfx::Vector2dF delta(0, -50);
+ SendGestureScrollBegin(WebGestureEvent::InertialPhaseState::kNonMomentum);
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 8),
+ gfx::ScrollOffset(100, 100));
+ SendGestureScrollUpdate(WebGestureEvent::InertialPhaseState::kNonMomentum,
+ delta, gfx::Vector2dF(-100, -100));
+ EXPECT_EQ(gfx::Vector2dF(-19, -19), helper_.StretchAmount());
+ controller_.ReconcileStretchAndScroll();
+ EXPECT_EQ(gfx::Vector2dF(-18, -18), helper_.StretchAmount());
+ // Adjustment of gfx::ScrollOffset(-5, -8) should bring back the
+ // scroll_offset_ to 0.
+ EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(0, 0));
+}
+
+// Tests if the overscrolled delta maps correctly to the actual amount that the
+// scroller gets stretched.
+TEST_F(OverscrollBounceControllerTest, VerifyOverscrollBounceDistance) {
+ gfx::Vector2dF overscroll_bounce_distance(
+ controller_.OverscrollBounceDistance(gfx::Vector2dF(0, -100),
+ helper_.ScrollBounds()));
+ EXPECT_EQ(overscroll_bounce_distance.y(), -19);
+
+ overscroll_bounce_distance = controller_.OverscrollBounceDistance(
+ gfx::Vector2dF(-100, 0), helper_.ScrollBounds());
+ EXPECT_EQ(overscroll_bounce_distance.x(), -19);
+}
+
+} // namespace test
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/prediction/input_filter_unittest_helpers.h b/chromium/third_party/blink/renderer/platform/widget/input/prediction/input_filter_unittest_helpers.h
index db2041c42bf..5c654520215 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/prediction/input_filter_unittest_helpers.h
+++ b/chromium/third_party/blink/renderer/platform/widget/input/prediction/input_filter_unittest_helpers.h
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/platform/widget/input/prediction/input_filter.h"
+#include "base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
index 5cd5c7139c5..c62a9bd07bd 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.cc
@@ -74,11 +74,11 @@ std::unique_ptr<EventWithCallback> ScrollPredictor::ResampleScrollEvents(
return event_with_callback;
for (auto& coalesced_event : original_events)
- UpdatePrediction(coalesced_event.event_, frame_time);
+ UpdatePrediction(coalesced_event.event_->Event(), frame_time);
if (should_resample_scroll_events_) {
ResampleEvent(frame_time, event_with_callback->event_pointer(),
- event_with_callback->mutable_latency_info());
+ &event_with_callback->latency_info());
}
metrics_handler_.EvaluatePrediction();
@@ -100,12 +100,11 @@ void ScrollPredictor::Reset() {
metrics_handler_.Reset();
}
-void ScrollPredictor::UpdatePrediction(
- const std::unique_ptr<WebInputEvent>& event,
- base::TimeTicks frame_time) {
- DCHECK(event->GetType() == WebInputEvent::Type::kGestureScrollUpdate);
+void ScrollPredictor::UpdatePrediction(const WebInputEvent& event,
+ base::TimeTicks frame_time) {
+ DCHECK(event.GetType() == WebInputEvent::Type::kGestureScrollUpdate);
const WebGestureEvent& gesture_event =
- static_cast<const WebGestureEvent&>(*event);
+ static_cast<const WebGestureEvent&>(event);
// When fling, GSU is sending per frame, resampling is not needed.
if (gesture_event.data.scroll_update.inertial_phase ==
WebGestureEvent::InertialPhaseState::kMomentum) {
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.h b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.h
index 0678e90c3da..7206159dd7c 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.h
+++ b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor.h
@@ -49,8 +49,7 @@ class PLATFORM_EXPORT ScrollPredictor {
void Reset();
// Update the prediction with GestureScrollUpdate deltaX and deltaY
- void UpdatePrediction(const std::unique_ptr<WebInputEvent>& event,
- base::TimeTicks frame_time);
+ void UpdatePrediction(const WebInputEvent& event, base::TimeTicks frame_time);
// Apply resampled deltaX/deltaY to gesture events
void ResampleEvent(base::TimeTicks frame_time,
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
index ada25ead61f..65b14dc9b39 100644
--- a/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/input/scroll_predictor_unittest.cc
@@ -54,7 +54,8 @@ class ScrollPredictorTest : public testing::Test {
gesture.data.scroll_update.delta_y = delta_y;
gesture.data.scroll_update.inertial_phase = phase;
- original_events_.emplace_back(gesture.Clone(), ui::LatencyInfo(),
+ original_events_.emplace_back(std::make_unique<WebCoalescedInputEvent>(
+ gesture.Clone(), ui::LatencyInfo()),
base::NullCallback());
return gesture.Clone();
@@ -76,9 +77,10 @@ class ScrollPredictorTest : public testing::Test {
void HandleResampleScrollEvents(std::unique_ptr<WebInputEvent>& event,
double time_delta_in_milliseconds = 0) {
std::unique_ptr<EventWithCallback> event_with_callback =
- std::make_unique<EventWithCallback>(std::move(event), ui::LatencyInfo(),
- base::TimeTicks(),
- base::NullCallback());
+ std::make_unique<EventWithCallback>(
+ std::make_unique<WebCoalescedInputEvent>(std::move(event),
+ ui::LatencyInfo()),
+ base::TimeTicks(), base::NullCallback());
event_with_callback->original_events() = std::move(original_events_);
event_with_callback = scroll_predictor_->ResampleScrollEvents(
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc b/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc
new file mode 100644
index 00000000000..47bcd0cdd3a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc
@@ -0,0 +1,702 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utility>
+
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "cc/metrics/event_metrics.h"
+#include "cc/paint/element_id.h"
+#include "cc/trees/latency_info_swap_promise_monitor.h"
+#include "cc/trees/layer_tree_host.h"
+#include "services/tracing/public/cpp/perfetto/flow_event_utils.h"
+#include "services/tracing/public/cpp/perfetto/macros.h"
+#include "third_party/blink/public/common/input/web_gesture_device.h"
+#include "third_party/blink/public/common/input/web_gesture_event.h"
+#include "third_party/blink/public/common/input/web_input_event_attribution.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"
+#include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
+#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_node.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
+#include "ui/latency/latency_info.h"
+
+#if defined(OS_ANDROID)
+#include <android/keycodes.h>
+#endif
+
+using perfetto::protos::pbzero::ChromeLatencyInfo;
+using perfetto::protos::pbzero::TrackEvent;
+
+namespace blink {
+
+namespace {
+
+int64_t GetEventLatencyMicros(base::TimeTicks event_timestamp,
+ base::TimeTicks now) {
+ return (now - event_timestamp).InMicroseconds();
+}
+
+void LogInputEventLatencyUma(const WebInputEvent& event, base::TimeTicks now) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Event.AggregatedLatency.Renderer2",
+ base::saturated_cast<base::HistogramBase::Sample>(
+ GetEventLatencyMicros(event.TimeStamp(), now)),
+ 1, 10000000, 100);
+}
+
+void LogPassiveEventListenersUma(WebInputEventResult result,
+ WebInputEvent::DispatchType dispatch_type) {
+ // This enum is backing a histogram. Do not remove or reorder members.
+ enum ListenerEnum {
+ PASSIVE_LISTENER_UMA_ENUM_PASSIVE,
+ PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE,
+ PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED,
+ PASSIVE_LISTENER_UMA_ENUM_CANCELABLE,
+ PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED,
+ PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING,
+ PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_MAIN_THREAD_RESPONSIVENESS_DEPRECATED,
+ PASSIVE_LISTENER_UMA_ENUM_COUNT
+ };
+
+ ListenerEnum enum_value;
+ switch (dispatch_type) {
+ case WebInputEvent::DispatchType::kListenersForcedNonBlockingDueToFling:
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_FORCED_NON_BLOCKING_DUE_TO_FLING;
+ break;
+ case WebInputEvent::DispatchType::kListenersNonBlockingPassive:
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_PASSIVE;
+ break;
+ case WebInputEvent::DispatchType::kEventNonBlocking:
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE;
+ break;
+ case WebInputEvent::DispatchType::kBlocking:
+ if (result == WebInputEventResult::kHandledApplication)
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_CANCELABLE_AND_CANCELED;
+ else if (result == WebInputEventResult::kHandledSuppressed)
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_SUPPRESSED;
+ else
+ enum_value = PASSIVE_LISTENER_UMA_ENUM_CANCELABLE;
+ break;
+ default:
+ NOTREACHED();
+ return;
+ }
+
+ UMA_HISTOGRAM_ENUMERATION("Event.PassiveListeners", enum_value,
+ PASSIVE_LISTENER_UMA_ENUM_COUNT);
+}
+
+void LogAllPassiveEventListenersUma(const WebInputEvent& input_event,
+ WebInputEventResult result) {
+ // TODO(dtapuska): Use the input_event.timeStampSeconds as the start
+ // ideally this should be when the event was sent by the compositor to the
+ // renderer. https://crbug.com/565348.
+ if (input_event.GetType() == WebInputEvent::Type::kTouchStart ||
+ input_event.GetType() == WebInputEvent::Type::kTouchMove ||
+ input_event.GetType() == WebInputEvent::Type::kTouchEnd) {
+ const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(input_event);
+
+ LogPassiveEventListenersUma(result, touch.dispatch_type);
+ } else if (input_event.GetType() == WebInputEvent::Type::kMouseWheel) {
+ LogPassiveEventListenersUma(
+ result,
+ static_cast<const WebMouseWheelEvent&>(input_event).dispatch_type);
+ }
+}
+
+WebCoalescedInputEvent GetCoalescedWebPointerEventForTouch(
+ const WebPointerEvent& pointer_event,
+ const std::vector<std::unique_ptr<WebInputEvent>>& coalesced_events,
+ const std::vector<std::unique_ptr<WebInputEvent>>& predicted_events,
+ const ui::LatencyInfo& latency) {
+ std::vector<std::unique_ptr<WebInputEvent>> related_pointer_events;
+ for (const std::unique_ptr<WebInputEvent>& event : coalesced_events) {
+ DCHECK(WebInputEvent::IsTouchEventType(event->GetType()));
+ const WebTouchEvent& touch_event =
+ static_cast<const WebTouchEvent&>(*event);
+ for (unsigned i = 0; i < touch_event.touches_length; ++i) {
+ if (touch_event.touches[i].id == pointer_event.id &&
+ touch_event.touches[i].state !=
+ WebTouchPoint::State::kStateStationary) {
+ related_pointer_events.emplace_back(std::make_unique<WebPointerEvent>(
+ touch_event, touch_event.touches[i]));
+ }
+ }
+ }
+ std::vector<std::unique_ptr<WebInputEvent>> predicted_pointer_events;
+ for (const std::unique_ptr<WebInputEvent>& event : predicted_events) {
+ DCHECK(WebInputEvent::IsTouchEventType(event->GetType()));
+ const WebTouchEvent& touch_event =
+ static_cast<const WebTouchEvent&>(*event);
+ for (unsigned i = 0; i < touch_event.touches_length; ++i) {
+ if (touch_event.touches[i].id == pointer_event.id &&
+ touch_event.touches[i].state !=
+ WebTouchPoint::State::kStateStationary) {
+ predicted_pointer_events.emplace_back(std::make_unique<WebPointerEvent>(
+ touch_event, touch_event.touches[i]));
+ }
+ }
+ }
+
+ return WebCoalescedInputEvent(pointer_event.Clone(),
+ std::move(related_pointer_events),
+ std::move(predicted_pointer_events), latency);
+}
+
+mojom::InputEventResultState GetAckResult(WebInputEventResult processed) {
+ return processed == WebInputEventResult::kNotHandled
+ ? mojom::InputEventResultState::kNotConsumed
+ : mojom::InputEventResultState::kConsumed;
+}
+
+bool IsGestureScroll(WebInputEvent::Type type) {
+ switch (type) {
+ case WebGestureEvent::Type::kGestureScrollBegin:
+ case WebGestureEvent::Type::kGestureScrollUpdate:
+ case WebGestureEvent::Type::kGestureScrollEnd:
+ return true;
+ default:
+ return false;
+ }
+}
+
+gfx::PointF PositionInWidgetFromInputEvent(const WebInputEvent& event) {
+ if (WebInputEvent::IsMouseEventType(event.GetType())) {
+ return static_cast<const WebMouseEvent&>(event).PositionInWidget();
+ } else if (WebInputEvent::IsGestureEventType(event.GetType())) {
+ return static_cast<const WebGestureEvent&>(event).PositionInWidget();
+ } else {
+ return gfx::PointF(0, 0);
+ }
+}
+
+bool IsTouchStartOrMove(const WebInputEvent& event) {
+ if (WebInputEvent::IsPointerEventType(event.GetType())) {
+ return static_cast<const WebPointerEvent&>(event)
+ .touch_start_or_first_touch_move;
+ } else if (WebInputEvent::IsTouchEventType(event.GetType())) {
+ return static_cast<const WebTouchEvent&>(event)
+ .touch_start_or_first_touch_move;
+ } else {
+ return false;
+ }
+}
+
+} // namespace
+
+// This class should be placed on the stack when handling an input event. It
+// stores information from callbacks from blink while handling an input event
+// and allows them to be returned in the InputEventAck result.
+class WidgetBaseInputHandler::HandlingState {
+ public:
+ HandlingState(base::WeakPtr<WidgetBaseInputHandler> input_handler_param,
+ bool is_touch_start_or_move)
+ : touch_start_or_move(is_touch_start_or_move),
+ input_handler(std::move(input_handler_param)) {
+ previous_was_handling_input = input_handler->handling_input_event_;
+ previous_state = input_handler->handling_input_state_;
+ input_handler->handling_input_event_ = true;
+ input_handler->handling_input_state_ = this;
+ }
+
+ ~HandlingState() {
+ // Unwinding the HandlingState on the stack might result in an
+ // input_handler_ that got destroyed. i.e. via a nested event loop.
+ if (!input_handler)
+ return;
+ input_handler->handling_input_event_ = previous_was_handling_input;
+ DCHECK_EQ(input_handler->handling_input_state_, this);
+ input_handler->handling_input_state_ = previous_state;
+
+#if defined(OS_ANDROID)
+ if (show_virtual_keyboard)
+ input_handler->ShowVirtualKeyboard();
+ else
+ input_handler->UpdateTextInputState();
+#endif
+ }
+
+ // Used to intercept overscroll notifications while an event is being
+ // handled. If the event causes overscroll, the overscroll metadata can be
+ // bundled in the event ack, saving an IPC. Note that we must continue
+ // supporting overscroll IPC notifications due to fling animation updates.
+ std::unique_ptr<InputHandlerProxy::DidOverscrollParams> event_overscroll;
+
+ base::Optional<WebTouchAction> touch_action;
+
+ // Used to hold a sequence of parameters corresponding to scroll gesture
+ // events that should be injected once the current input event is done
+ // being processed.
+ std::vector<WidgetBaseInputHandler::InjectScrollGestureParams>
+ injected_scroll_params;
+
+ // Whether the event we are handling is a touch start or move.
+ bool touch_start_or_move;
+
+#if defined(OS_ANDROID)
+ // Whether to show the virtual keyboard or not at the end of processing.
+ bool show_virtual_keyboard = false;
+#endif
+
+ private:
+ HandlingState* previous_state;
+ bool previous_was_handling_input;
+ base::WeakPtr<WidgetBaseInputHandler> input_handler;
+};
+
+WidgetBaseInputHandler::WidgetBaseInputHandler(WidgetBase* widget)
+ : widget_(widget),
+ supports_buffered_touch_(
+ widget_->client()->SupportsBufferedTouchEvents()) {}
+
+WebInputEventResult WidgetBaseInputHandler::HandleTouchEvent(
+ const WebCoalescedInputEvent& coalesced_event) {
+ const WebInputEvent& input_event = coalesced_event.Event();
+
+ if (input_event.GetType() == WebInputEvent::Type::kTouchScrollStarted) {
+ WebPointerEvent pointer_event =
+ WebPointerEvent::CreatePointerCausesUaActionEvent(
+ WebPointerProperties::PointerType::kUnknown,
+ input_event.TimeStamp());
+ return widget_->client()->HandleInputEvent(
+ WebCoalescedInputEvent(pointer_event, coalesced_event.latency_info()));
+ }
+
+ const WebTouchEvent touch_event =
+ static_cast<const WebTouchEvent&>(input_event);
+ for (unsigned i = 0; i < touch_event.touches_length; ++i) {
+ const WebTouchPoint& touch_point = touch_event.touches[i];
+ if (touch_point.state != WebTouchPoint::State::kStateStationary) {
+ const WebPointerEvent& pointer_event =
+ WebPointerEvent(touch_event, touch_point);
+ const WebCoalescedInputEvent& coalesced_pointer_event =
+ GetCoalescedWebPointerEventForTouch(
+ pointer_event, coalesced_event.GetCoalescedEventsPointers(),
+ coalesced_event.GetPredictedEventsPointers(),
+ coalesced_event.latency_info());
+ widget_->client()->HandleInputEvent(coalesced_pointer_event);
+ }
+ }
+ return widget_->client()->DispatchBufferedTouchEvents();
+}
+
+void WidgetBaseInputHandler::HandleInputEvent(
+ const WebCoalescedInputEvent& coalesced_event,
+ HandledEventCallback callback) {
+ const WebInputEvent& input_event = coalesced_event.Event();
+
+ // Keep a WeakPtr to this WidgetBaseInputHandler to detect if executing the
+ // input event destroyed the associated RenderWidget (and this handler).
+ base::WeakPtr<WidgetBaseInputHandler> weak_self =
+ weak_ptr_factory_.GetWeakPtr();
+ HandlingState handling_state(weak_self, IsTouchStartOrMove(input_event));
+
+ base::TimeTicks start_time;
+ if (base::TimeTicks::IsHighResolution())
+ start_time = base::TimeTicks::Now();
+
+ TRACE_EVENT1("renderer,benchmark,rail",
+ "WidgetBaseInputHandler::OnHandleInputEvent", "event",
+ WebInputEvent::GetName(input_event.GetType()));
+ int64_t trace_id = coalesced_event.latency_info().trace_id();
+ TRACE_EVENT("input,benchmark", "LatencyInfo.Flow",
+ [trace_id](perfetto::EventContext ctx) {
+ ChromeLatencyInfo* info =
+ ctx.event()->set_chrome_latency_info();
+ info->set_trace_id(trace_id);
+ info->set_step(ChromeLatencyInfo::STEP_HANDLE_INPUT_EVENT_MAIN);
+ tracing::FillFlowEvent(ctx, TrackEvent::LegacyEvent::FLOW_INOUT,
+ trace_id);
+ });
+
+ // If we don't have a high res timer, these metrics won't be accurate enough
+ // to be worth collecting. Note that this does introduce some sampling bias.
+ if (!start_time.is_null())
+ LogInputEventLatencyUma(input_event, start_time);
+
+ ui::LatencyInfo swap_latency_info(coalesced_event.latency_info());
+ swap_latency_info.AddLatencyNumber(
+ ui::LatencyComponentType::INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT);
+ cc::LatencyInfoSwapPromiseMonitor swap_promise_monitor(
+ &swap_latency_info, widget_->LayerTreeHost()->GetSwapPromiseManager(),
+ nullptr);
+ auto scoped_event_metrics_monitor =
+ widget_->LayerTreeHost()->GetScopedEventMetricsMonitor(
+ cc::EventMetrics::Create(input_event.GetTypeAsUiEventType(),
+ input_event.TimeStamp(),
+ input_event.GetScrollInputType()));
+
+ bool prevent_default = false;
+ bool show_virtual_keyboard_for_mouse = false;
+ if (WebInputEvent::IsMouseEventType(input_event.GetType())) {
+ const WebMouseEvent& mouse_event =
+ static_cast<const WebMouseEvent&>(input_event);
+ TRACE_EVENT2("renderer", "HandleMouseMove", "x",
+ mouse_event.PositionInWidget().x(), "y",
+ mouse_event.PositionInWidget().y());
+
+ prevent_default = widget_->client()->WillHandleMouseEvent(mouse_event);
+
+ // Reset the last known cursor if mouse has left this widget. So next
+ // time that the mouse enters we always set the cursor accordingly.
+ if (mouse_event.GetType() == WebInputEvent::Type::kMouseLeave)
+ current_cursor_.reset();
+
+ if (mouse_event.button == WebPointerProperties::Button::kLeft &&
+ mouse_event.GetType() == WebInputEvent::Type::kMouseUp) {
+ show_virtual_keyboard_for_mouse = true;
+ }
+ }
+
+ if (WebInputEvent::IsKeyboardEventType(input_event.GetType())) {
+#if defined(OS_ANDROID)
+ // The DPAD_CENTER key on Android has a dual semantic: (1) in the general
+ // case it should behave like a select key (i.e. causing a click if a button
+ // is focused). However, if a text field is focused (2), its intended
+ // behavior is to just show the IME and don't propagate the key.
+ // A typical use case is a web form: the DPAD_CENTER should bring up the IME
+ // when clicked on an input text field and cause the form submit if clicked
+ // when the submit button is focused, but not vice-versa.
+ // The UI layer takes care of translating DPAD_CENTER into a RETURN key,
+ // but at this point we have to swallow the event for the scenario (2).
+ const WebKeyboardEvent& key_event =
+ static_cast<const WebKeyboardEvent&>(input_event);
+ if (key_event.native_key_code == AKEYCODE_DPAD_CENTER &&
+ widget_->client()->GetTextInputType() !=
+ WebTextInputType::kWebTextInputTypeNone) {
+ // Show the keyboard on keyup (not keydown) to match the behavior of
+ // Android's TextView.
+ if (key_event.GetType() == WebInputEvent::Type::kKeyUp)
+ widget_->ShowVirtualKeyboardOnElementFocus();
+ // Prevent default for both keydown and keyup (letting the keydown go
+ // through to the web app would cause compatibility problems since
+ // DPAD_CENTER is also used as a "confirm" button).
+ prevent_default = true;
+ }
+#endif
+ }
+
+ if (WebInputEvent::IsGestureEventType(input_event.GetType())) {
+ const WebGestureEvent& gesture_event =
+ static_cast<const WebGestureEvent&>(input_event);
+ prevent_default = prevent_default ||
+ widget_->client()->WillHandleGestureEvent(gesture_event);
+ }
+
+ WebInputEventResult processed = prevent_default
+ ? WebInputEventResult::kHandledSuppressed
+ : WebInputEventResult::kNotHandled;
+ if (input_event.GetType() != WebInputEvent::Type::kChar ||
+ !suppress_next_char_events_) {
+ suppress_next_char_events_ = false;
+ if (processed == WebInputEventResult::kNotHandled) {
+ if (supports_buffered_touch_ &&
+ WebInputEvent::IsTouchEventType(input_event.GetType()))
+ processed = HandleTouchEvent(coalesced_event);
+ else
+ processed = widget_->client()->HandleInputEvent(coalesced_event);
+ }
+
+ // The associated WidgetBase (and this WidgetBaseInputHandler) could
+ // have been destroyed. If it was return early before accessing any more of
+ // this class.
+ if (!weak_self) {
+ if (callback) {
+ std::move(callback).Run(GetAckResult(processed), swap_latency_info,
+ std::move(handling_state.event_overscroll),
+ handling_state.touch_action);
+ }
+ return;
+ }
+ }
+
+ // Handling |input_event| is finished and further down, we might start
+ // handling injected scroll events. So, stop monitoring EventMetrics for
+ // |input_event| to avoid nested monitors.
+ scoped_event_metrics_monitor = nullptr;
+
+ LogAllPassiveEventListenersUma(input_event, processed);
+
+ // If this RawKeyDown event corresponds to a browser keyboard shortcut and
+ // it's not processed by webkit, then we need to suppress the upcoming Char
+ // events.
+ bool is_keyboard_shortcut =
+ input_event.GetType() == WebInputEvent::Type::kRawKeyDown &&
+ static_cast<const WebKeyboardEvent&>(input_event).is_browser_shortcut;
+ if (processed == WebInputEventResult::kNotHandled && is_keyboard_shortcut)
+ suppress_next_char_events_ = true;
+
+ // The handling of some input events on the main thread may require injecting
+ // scroll gestures back into blink, e.g., a mousedown on a scrollbar. We
+ // do this here so that we can attribute latency information from the mouse as
+ // a scroll interaction, instead of just classifying as mouse input.
+ if (handling_state.injected_scroll_params.size()) {
+ HandleInjectedScrollGestures(
+ std::move(handling_state.injected_scroll_params), input_event,
+ coalesced_event.latency_info());
+ }
+
+ // Send gesture scroll events and their dispositions to the compositor thread,
+ // so that they can be used to produce the elastic overscroll effect.
+ if (input_event.GetType() == WebInputEvent::Type::kGestureScrollBegin ||
+ input_event.GetType() == WebInputEvent::Type::kGestureScrollEnd ||
+ input_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
+ const WebGestureEvent& gesture_event =
+ static_cast<const WebGestureEvent&>(input_event);
+ if (gesture_event.SourceDevice() == WebGestureDevice::kTouchpad ||
+ gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen) {
+ gfx::Vector2dF latest_overscroll_delta =
+ handling_state.event_overscroll
+ ? handling_state.event_overscroll->latest_overscroll_delta
+ : gfx::Vector2dF();
+ cc::OverscrollBehavior overscroll_behavior =
+ handling_state.event_overscroll
+ ? handling_state.event_overscroll->overscroll_behavior
+ : cc::OverscrollBehavior();
+ widget_->client()->ObserveGestureEventAndResult(
+ gesture_event, latest_overscroll_delta, overscroll_behavior,
+ processed != WebInputEventResult::kNotHandled);
+ }
+ }
+
+ if (callback) {
+ std::move(callback).Run(GetAckResult(processed), swap_latency_info,
+ std::move(handling_state.event_overscroll),
+ handling_state.touch_action);
+ } else {
+ DCHECK(!handling_state.event_overscroll)
+ << "Unexpected overscroll for un-acked event";
+ }
+
+ // Show the virtual keyboard if enabled and a user gesture triggers a focus
+ // change.
+ if ((processed != WebInputEventResult::kNotHandled &&
+ input_event.GetType() == WebInputEvent::Type::kTouchEnd) ||
+ show_virtual_keyboard_for_mouse) {
+ ShowVirtualKeyboard();
+ }
+
+ if (!prevent_default &&
+ WebInputEvent::IsKeyboardEventType(input_event.GetType()))
+ widget_->client()->DidHandleKeyEvent();
+
+// TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
+// virtual keyboard.
+#if !defined(OS_ANDROID)
+ // Virtual keyboard is not supported, so react to focus change immediately.
+ if ((processed != WebInputEventResult::kNotHandled &&
+ input_event.GetType() == WebInputEvent::Type::kMouseDown) ||
+ input_event.GetType() == WebInputEvent::Type::kGestureTap) {
+ widget_->client()->FocusChangeComplete();
+ }
+#endif
+
+ // Ensure all injected scrolls were handled or queue up - any remaining
+ // injected scrolls at this point would not be processed.
+ DCHECK(handling_state.injected_scroll_params.empty());
+}
+
+bool WidgetBaseInputHandler::DidOverscrollFromBlink(
+ const gfx::Vector2dF& overscroll_delta,
+ const gfx::Vector2dF& accumulated_overscroll,
+ const gfx::PointF& position,
+ const gfx::Vector2dF& velocity,
+ const cc::OverscrollBehavior& behavior) {
+ // We aren't currently handling an event. Allow the processing to be
+ // dispatched separately from the ACK.
+ if (!handling_input_state_)
+ return true;
+
+ // If we're currently handling an event, stash the overscroll data such that
+ // it can be bundled in the event ack.
+ std::unique_ptr<InputHandlerProxy::DidOverscrollParams> params =
+ std::make_unique<InputHandlerProxy::DidOverscrollParams>();
+ params->accumulated_overscroll = accumulated_overscroll;
+ params->latest_overscroll_delta = overscroll_delta;
+ params->current_fling_velocity = velocity;
+ params->causal_event_viewport_point = position;
+ params->overscroll_behavior = behavior;
+ handling_input_state_->event_overscroll = std::move(params);
+ return false;
+}
+
+void WidgetBaseInputHandler::InjectGestureScrollEvent(
+ WebGestureDevice device,
+ const gfx::Vector2dF& delta,
+ ui::ScrollGranularity granularity,
+ cc::ElementId scrollable_area_element_id,
+ WebInputEvent::Type injected_type) {
+ DCHECK(IsGestureScroll(injected_type));
+ // If we're currently handling an input event, cache the appropriate
+ // parameters so we can dispatch the events directly once blink finishes
+ // handling the event.
+ // Otherwise, queue the event on the main thread event queue.
+ // The latter may occur when scrollbar scrolls are injected due to
+ // autoscroll timer - i.e. not within the handling of a mouse event.
+ // We don't always just enqueue events, since events queued to the
+ // MainThreadEventQueue in the middle of dispatch (which we are) won't
+ // be dispatched until the next time the queue gets to run. The side effect
+ // of that would be an extra frame of latency if we're injecting a scroll
+ // during the handling of a rAF aligned input event, such as mouse move.
+ if (handling_input_state_) {
+ InjectScrollGestureParams params{device, delta, granularity,
+ scrollable_area_element_id, injected_type};
+ handling_input_state_->injected_scroll_params.push_back(params);
+ } else {
+ base::TimeTicks now = base::TimeTicks::Now();
+ std::unique_ptr<WebGestureEvent> gesture_event =
+ WebGestureEvent::GenerateInjectedScrollGesture(
+ injected_type, now, device, gfx::PointF(0, 0), delta, granularity);
+ if (injected_type == WebInputEvent::Type::kGestureScrollBegin) {
+ gesture_event->data.scroll_begin.scrollable_area_element_id =
+ scrollable_area_element_id.GetStableId();
+ }
+
+ std::unique_ptr<WebCoalescedInputEvent> web_scoped_gesture_event =
+ std::make_unique<WebCoalescedInputEvent>(std::move(gesture_event),
+ ui::LatencyInfo());
+ widget_->client()->QueueSyntheticEvent(std::move(web_scoped_gesture_event));
+ }
+}
+
+void WidgetBaseInputHandler::HandleInjectedScrollGestures(
+ std::vector<InjectScrollGestureParams> injected_scroll_params,
+ const WebInputEvent& input_event,
+ const ui::LatencyInfo& original_latency_info) {
+ DCHECK(injected_scroll_params.size());
+
+ base::TimeTicks original_timestamp;
+ bool found_original_component = original_latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, &original_timestamp);
+ DCHECK(found_original_component);
+
+ gfx::PointF position = PositionInWidgetFromInputEvent(input_event);
+ for (const InjectScrollGestureParams& params : injected_scroll_params) {
+ // Set up a new LatencyInfo for the injected scroll - this is the original
+ // LatencyInfo for the input event that was being handled when the scroll
+ // was injected. This new LatencyInfo will have a modified type, and an
+ // additional scroll update component. Also set up a SwapPromiseMonitor that
+ // will cause the LatencyInfo to be sent up with the compositor frame, if
+ // the GSU causes a commit. This allows end to end latency to be logged for
+ // the injected scroll, annotated with the correct type.
+ ui::LatencyInfo scrollbar_latency_info(original_latency_info);
+
+ // Currently only scrollbar is supported - if this DCHECK hits due to a
+ // new type being injected, please modify the type passed to
+ // |set_source_event_type()|.
+ DCHECK(params.device == WebGestureDevice::kScrollbar);
+ scrollbar_latency_info.set_source_event_type(
+ ui::SourceEventType::SCROLLBAR);
+ scrollbar_latency_info.AddLatencyNumber(
+ ui::LatencyComponentType::INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT);
+
+ if (params.type == WebInputEvent::Type::kGestureScrollUpdate) {
+ if (input_event.GetType() != WebInputEvent::Type::kGestureScrollUpdate) {
+ scrollbar_latency_info.AddLatencyNumberWithTimestamp(
+ last_injected_gesture_was_begin_
+ ? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
+ : ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+ original_timestamp);
+ } else {
+ // If we're injecting a GSU in response to a GSU (touch drags of the
+ // scrollbar thumb in Blink handles GSUs, and reverses them with
+ // injected GSUs), the LatencyInfo will already have the appropriate
+ // SCROLL_UPDATE component set.
+ DCHECK(
+ scrollbar_latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+ nullptr) ||
+ scrollbar_latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+ nullptr));
+ }
+ }
+
+ std::unique_ptr<WebGestureEvent> gesture_event =
+ WebGestureEvent::GenerateInjectedScrollGesture(
+ params.type, input_event.TimeStamp(), params.device, position,
+ params.scroll_delta, params.granularity);
+ if (params.type == WebInputEvent::Type::kGestureScrollBegin) {
+ gesture_event->data.scroll_begin.scrollable_area_element_id =
+ params.scrollable_area_element_id.GetStableId();
+ last_injected_gesture_was_begin_ = true;
+ } else {
+ last_injected_gesture_was_begin_ = false;
+ }
+
+ {
+ cc::LatencyInfoSwapPromiseMonitor swap_promise_monitor(
+ &scrollbar_latency_info,
+ widget_->LayerTreeHost()->GetSwapPromiseManager(), nullptr);
+ auto scoped_event_metrics_monitor =
+ widget_->LayerTreeHost()->GetScopedEventMetricsMonitor(
+ cc::EventMetrics::Create(gesture_event->GetTypeAsUiEventType(),
+ gesture_event->TimeStamp(),
+ gesture_event->GetScrollInputType()));
+ widget_->client()->HandleInputEvent(
+ WebCoalescedInputEvent(*gesture_event, scrollbar_latency_info));
+ }
+ }
+}
+
+bool WidgetBaseInputHandler::DidChangeCursor(const ui::Cursor& cursor) {
+ if (current_cursor_.has_value() && current_cursor_.value() == cursor)
+ return false;
+ current_cursor_ = cursor;
+ return true;
+}
+
+bool WidgetBaseInputHandler::ProcessTouchAction(WebTouchAction touch_action) {
+ if (!handling_input_state_)
+ return false;
+ // Ignore setTouchAction calls that result from synthetic touch events (eg.
+ // when blink is emulating touch with mouse).
+ if (!handling_input_state_->touch_start_or_move)
+ return false;
+ handling_input_state_->touch_action = touch_action;
+ return true;
+}
+
+void WidgetBaseInputHandler::ShowVirtualKeyboard() {
+#if defined(OS_ANDROID)
+ if (handling_input_state_) {
+ handling_input_state_->show_virtual_keyboard = true;
+ return;
+ }
+#endif
+ widget_->ShowVirtualKeyboard();
+}
+
+void WidgetBaseInputHandler::UpdateTextInputState() {
+#if defined(OS_ANDROID)
+ if (handling_input_state_)
+ return;
+#endif
+ widget_->UpdateTextInputState();
+}
+
+bool WidgetBaseInputHandler::ProtectedByIMEGuard(bool show_virtual_keyboard) {
+#if defined(OS_ANDROID)
+ if (show_virtual_keyboard && handling_input_state_) {
+ handling_input_state_->show_virtual_keyboard = true;
+ }
+ return handling_input_state_;
+#else
+ return false;
+#endif
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h b/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h
new file mode 100644
index 00000000000..e54011feaed
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h
@@ -0,0 +1,144 @@
+// 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_WIDGET_INPUT_WIDGET_BASE_INPUT_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_WIDGET_BASE_INPUT_HANDLER_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
+#include "third_party/blink/public/common/input/web_gesture_event.h"
+#include "third_party/blink/public/mojom/input/input_event_result.mojom-blink.h"
+#include "third_party/blink/public/platform/input/input_handler_proxy.h"
+#include "third_party/blink/public/platform/web_input_event_result.h"
+#include "third_party/blink/public/platform/web_touch_action.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/events/types/scroll_types.h"
+
+namespace cc {
+struct ElementId;
+struct OverscrollBehavior;
+} // namespace cc
+
+namespace ui {
+class LatencyInfo;
+}
+
+namespace viz {
+class FrameSinkId;
+}
+
+namespace blink {
+
+class WidgetBase;
+
+class PLATFORM_EXPORT WidgetBaseInputHandler {
+ public:
+ WidgetBaseInputHandler(WidgetBase* widget);
+ WidgetBaseInputHandler(const WidgetBaseInputHandler&) = delete;
+ WidgetBaseInputHandler& operator=(const WidgetBaseInputHandler&) = delete;
+
+ // Hit test the given point to find out the frame underneath and
+ // returns the FrameSinkId for that frame. |local_point| returns the point
+ // in the coordinate space of the FrameSinkId that was hit.
+ viz::FrameSinkId GetFrameSinkIdAtPoint(const gfx::PointF& point,
+ gfx::PointF* local_point);
+
+ using HandledEventCallback = base::OnceCallback<void(
+ mojom::InputEventResultState ack_state,
+ const ui::LatencyInfo& latency_info,
+ std::unique_ptr<InputHandlerProxy::DidOverscrollParams>,
+ base::Optional<WebTouchAction>)>;
+
+ // Handle input events from the input event provider.
+ void HandleInputEvent(const blink::WebCoalescedInputEvent& coalesced_event,
+ HandledEventCallback callback);
+
+ // Handle overscroll from Blink. Returns whether the should be sent to the
+ // browser. This will return false if an event is currently being processed
+ // and will be returned part of the input ack.
+ bool DidOverscrollFromBlink(const gfx::Vector2dF& overscrollDelta,
+ const gfx::Vector2dF& accumulatedOverscroll,
+ const gfx::PointF& position,
+ const gfx::Vector2dF& velocity,
+ const cc::OverscrollBehavior& behavior);
+
+ void InjectGestureScrollEvent(blink::WebGestureDevice device,
+ const gfx::Vector2dF& delta,
+ ui::ScrollGranularity granularity,
+ cc::ElementId scrollable_area_element_id,
+ blink::WebInputEvent::Type injected_type);
+
+ bool handling_input_event() const { return handling_input_event_; }
+ void set_handling_input_event(bool handling_input_event) {
+ handling_input_event_ = handling_input_event;
+ }
+
+ // Whether the event is protected by an IME guard to prevent intermediate
+ // IPC messages from being dispatched.
+ bool ProtectedByIMEGuard(bool show_virtual_keyboard);
+
+ // Process the touch action, returning whether the action should be relayed
+ // to the browser.
+ bool ProcessTouchAction(WebTouchAction touch_action);
+
+ // Process the new cursor and returns true if it has changed from the last
+ // cursor.
+ bool DidChangeCursor(const ui::Cursor& cursor);
+
+ // Request virtual keyboard be shown. The message will be debounced during
+ // handling of input events.
+ void ShowVirtualKeyboard();
+ void UpdateTextInputState();
+
+ private:
+ class HandlingState;
+ struct InjectScrollGestureParams {
+ WebGestureDevice device;
+ gfx::Vector2dF scroll_delta;
+ ui::ScrollGranularity granularity;
+ cc::ElementId scrollable_area_element_id;
+ blink::WebInputEvent::Type type;
+ };
+
+ WebInputEventResult HandleTouchEvent(
+ const WebCoalescedInputEvent& coalesced_event);
+
+ void HandleInjectedScrollGestures(
+ std::vector<InjectScrollGestureParams> injected_scroll_params,
+ const WebInputEvent& input_event,
+ const ui::LatencyInfo& original_latency_info);
+
+ WidgetBase* widget_;
+
+ // Are we currently handling an input event?
+ bool handling_input_event_ = false;
+
+ // Current state from HandleInputEvent. This variable is stack allocated
+ // and is not owned.
+ HandlingState* handling_input_state_ = nullptr;
+
+ // We store the current cursor object so we can avoid spamming SetCursor
+ // messages.
+ base::Optional<ui::Cursor> current_cursor_;
+
+ // Indicates if the next sequence of Char events should be suppressed or not.
+ bool suppress_next_char_events_ = false;
+
+ // Whether the last injected scroll gesture was a GestureScrollBegin. Used to
+ // determine which GestureScrollUpdate is the first in a gesture sequence for
+ // latency classification.
+ bool last_injected_gesture_was_begin_ = false;
+
+ const bool supports_buffered_touch_ = false;
+
+ base::WeakPtrFactory<WidgetBaseInputHandler> weak_ptr_factory_{this};
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_INPUT_WIDGET_BASE_INPUT_HANDLER_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
index 1eddece5ed4..7735f5cb7fa 100644
--- a/chromium/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/chromium/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/ukm_manager.h"
@@ -14,14 +15,20 @@
#include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/platform/web_screen_info.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h"
+#include "third_party/blink/renderer/platform/widget/frame_widget.h"
#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
+#include "ui/base/ime/mojom/text_input_state.mojom-blink.h"
+#include "ui/gfx/presentation_feedback.h"
namespace blink {
namespace {
+static const int kInvalidNextPreviousFlagsValue = -1;
+
scoped_refptr<base::SingleThreadTaskRunner> GetCleanupTaskRunner() {
if (auto* main_thread_scheduler =
scheduler::WebThreadScheduler::MainThreadScheduler()) {
@@ -31,6 +38,34 @@ scoped_refptr<base::SingleThreadTaskRunner> GetCleanupTaskRunner() {
}
}
+void OnDidPresentForceDrawFrame(
+ mojom::blink::Widget::ForceRedrawCallback callback,
+ const gfx::PresentationFeedback& feedback) {
+ std::move(callback).Run();
+}
+
+bool IsDateTimeInput(ui::TextInputType type) {
+ return type == ui::TEXT_INPUT_TYPE_DATE ||
+ type == ui::TEXT_INPUT_TYPE_DATE_TIME ||
+ type == ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL ||
+ type == ui::TEXT_INPUT_TYPE_MONTH ||
+ type == ui::TEXT_INPUT_TYPE_TIME || type == ui::TEXT_INPUT_TYPE_WEEK;
+}
+
+ui::TextInputType ConvertWebTextInputType(blink::WebTextInputType type) {
+ // Check the type is in the range representable by ui::TextInputType.
+ DCHECK_LE(type, static_cast<int>(ui::TEXT_INPUT_TYPE_MAX))
+ << "blink::WebTextInputType and ui::TextInputType not synchronized";
+ return static_cast<ui::TextInputType>(type);
+}
+
+ui::TextInputMode ConvertWebTextInputMode(blink::WebTextInputMode mode) {
+ // Check the mode is in the range representable by ui::TextInputMode.
+ DCHECK_LE(mode, static_cast<int>(ui::TEXT_INPUT_MODE_MAX))
+ << "blink::WebTextInputMode and ui::TextInputMode not synchronized";
+ return static_cast<ui::TextInputMode>(mode);
+}
+
} // namespace
WidgetBase::WidgetBase(
@@ -107,6 +142,24 @@ WidgetBase::RendererWidgetSchedulingState() const {
return render_widget_scheduling_state_.get();
}
+void WidgetBase::ForceRedraw(
+ mojom::blink::Widget::ForceRedrawCallback callback) {
+ LayerTreeHost()->RequestPresentationTimeForNextFrame(
+ base::BindOnce(&OnDidPresentForceDrawFrame, std::move(callback)));
+ LayerTreeHost()->SetNeedsCommitWithForcedRedraw();
+
+ // ScheduleAnimationForWebTests() which is implemented by WebWidgetTestProxy,
+ // providing the additional control over the lifecycle of compositing required
+ // by web tests. This will be a no-op on production.
+ client_->ScheduleAnimationForWebTests();
+}
+
+void WidgetBase::GetWidgetInputHandler(
+ mojo::PendingReceiver<mojom::blink::WidgetInputHandler> request,
+ mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host) {
+ client_->GetWidgetInputHandler(std::move(request), std::move(host));
+}
+
void WidgetBase::ApplyViewportChanges(
const cc::ApplyViewportChangesArgs& args) {
client_->ApplyViewportChanges(args);
@@ -149,6 +202,13 @@ void WidgetBase::DidCommitAndDrawCompositorFrame() {
client_->DidCommitAndDrawCompositorFrame();
}
+void WidgetBase::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ client_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
void WidgetBase::WillCommitCompositorFrame() {
client_->BeginCommitCompositorFrame();
}
@@ -185,8 +245,23 @@ void WidgetBase::EndUpdateLayers() {
}
void WidgetBase::WillBeginMainFrame() {
+ TRACE_EVENT0("gpu", "WidgetBase::WillBeginMainFrame");
client_->SetSuppressFrameRequestsWorkaroundFor704763Only(true);
client_->WillBeginMainFrame();
+ UpdateSelectionBounds();
+
+ // The UpdateTextInputState can result in further layout and possibly
+ // enable GPU acceleration so they need to be called before any painting
+ // is done.
+ UpdateTextInputState();
+}
+
+void WidgetBase::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ client_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
}
void WidgetBase::SetCompositorVisible(bool visible) {
@@ -233,7 +308,305 @@ void WidgetBase::AddPresentationCallback(
}
void WidgetBase::SetCursor(const ui::Cursor& cursor) {
- widget_host_->SetCursor(cursor);
+ if (input_handler_.DidChangeCursor(cursor)) {
+ widget_host_->SetCursor(cursor);
+ }
+}
+
+void WidgetBase::SetToolTipText(const String& tooltip_text, TextDirection dir) {
+ widget_host_->SetToolTipText(tooltip_text.IsEmpty() ? "" : tooltip_text,
+ ToBaseTextDirection(dir));
+}
+
+void WidgetBase::ShowVirtualKeyboard() {
+ UpdateTextInputStateInternal(true, false);
+}
+
+void WidgetBase::UpdateTextInputState() {
+ UpdateTextInputStateInternal(false, false);
+}
+
+bool WidgetBase::CanComposeInline() {
+ FrameWidget* frame_widget = client_->FrameWidget();
+ if (!frame_widget)
+ return true;
+ return frame_widget->Client()->CanComposeInline();
+}
+
+void WidgetBase::UpdateTextInputStateInternal(bool show_virtual_keyboard,
+ bool reply_to_request) {
+ TRACE_EVENT0("renderer", "WidgetBase::UpdateTextInputStateInternal");
+ if (client_->HasCurrentImeGuard(show_virtual_keyboard) ||
+ input_handler_.ProtectedByIMEGuard(show_virtual_keyboard)) {
+ DCHECK(!reply_to_request);
+ return;
+ }
+ ui::TextInputType new_type = GetTextInputType();
+ if (IsDateTimeInput(new_type))
+ return; // Not considered as a text input field in WebKit/Chromium.
+
+ FrameWidget* frame_widget = client_->FrameWidget();
+
+ blink::WebTextInputInfo new_info;
+ ui::mojom::VirtualKeyboardVisibilityRequest last_vk_visibility_request =
+ ui::mojom::VirtualKeyboardVisibilityRequest::NONE;
+ bool always_hide_ime = false;
+ if (frame_widget) {
+ new_info = frame_widget->TextInputInfo();
+ // This will be used to decide whether or not to show VK when VK policy is
+ // manual.
+ last_vk_visibility_request =
+ frame_widget->GetLastVirtualKeyboardVisibilityRequest();
+
+ // Check whether the keyboard should always be hidden for the currently
+ // focused element.
+ always_hide_ime = frame_widget->ShouldSuppressKeyboardForFocusedElement();
+ }
+ const ui::TextInputMode new_mode =
+ ConvertWebTextInputMode(new_info.input_mode);
+ const ui::mojom::VirtualKeyboardPolicy new_vk_policy =
+ new_info.virtual_keyboard_policy;
+ bool new_can_compose_inline = CanComposeInline();
+
+ // Only sends text input params if they are changed or if the ime should be
+ // shown.
+ if (show_virtual_keyboard || reply_to_request ||
+ text_input_type_ != new_type || text_input_mode_ != new_mode ||
+ text_input_info_ != new_info ||
+ can_compose_inline_ != new_can_compose_inline ||
+ always_hide_ime_ != always_hide_ime || vk_policy_ != new_vk_policy ||
+ (new_vk_policy == ui::mojom::VirtualKeyboardPolicy::MANUAL &&
+ (last_vk_visibility_request !=
+ ui::mojom::VirtualKeyboardVisibilityRequest::NONE))) {
+ ui::mojom::blink::TextInputStatePtr params =
+ ui::mojom::blink::TextInputState::New();
+ params->type = new_type;
+ params->mode = new_mode;
+ params->action = new_info.action;
+ params->flags = new_info.flags;
+ params->vk_policy = new_vk_policy;
+ params->last_vk_visibility_request = last_vk_visibility_request;
+ if (frame_widget) {
+ frame_widget->GetEditContextBoundsInWindow(
+ &params->edit_context_control_bounds,
+ &params->edit_context_selection_bounds);
+ }
+#if defined(OS_ANDROID)
+ if (next_previous_flags_ == kInvalidNextPreviousFlagsValue) {
+ // Due to a focus change, values will be reset by the frame.
+ // That case we only need fresh NEXT/PREVIOUS information.
+ // Also we won't send WidgetHostMsg_TextInputStateChanged if next/previous
+ // focusable status is changed.
+ if (frame_widget) {
+ next_previous_flags_ =
+ frame_widget->ComputeWebTextInputNextPreviousFlags();
+ } else {
+ // For safety in case GetInputMethodController() is null, because -1 is
+ // invalid value to send to browser process.
+ next_previous_flags_ = 0;
+ }
+ }
+#else
+ next_previous_flags_ = 0;
+#endif
+ params->flags |= next_previous_flags_;
+ params->value = new_info.value;
+ params->selection =
+ gfx::Range(new_info.selection_start, new_info.selection_end);
+ if (new_info.composition_start != -1) {
+ params->composition =
+ gfx::Range(new_info.composition_start, new_info.composition_end);
+ }
+ params->can_compose_inline = new_can_compose_inline;
+ // TODO(changwan): change instances of show_ime_if_needed to
+ // show_virtual_keyboard.
+ params->show_ime_if_needed = show_virtual_keyboard;
+ params->always_hide_ime = always_hide_ime;
+ params->reply_to_request = reply_to_request;
+ widget_host_->TextInputStateChanged(std::move(params));
+
+ text_input_info_ = new_info;
+ text_input_type_ = new_type;
+ text_input_mode_ = new_mode;
+ vk_policy_ = new_vk_policy;
+ can_compose_inline_ = new_can_compose_inline;
+ always_hide_ime_ = always_hide_ime;
+ text_input_flags_ = new_info.flags;
+ // Reset the show/hide state in the InputMethodController.
+ if (frame_widget) {
+ if (last_vk_visibility_request !=
+ ui::mojom::VirtualKeyboardVisibilityRequest::NONE) {
+ // Reset the visibility state.
+ frame_widget->ResetVirtualKeyboardVisibilityRequest();
+ }
+ }
+
+#if defined(OS_ANDROID)
+ // If we send a new TextInputStateChanged message, we must also deliver a
+ // new RenderFrameMetadata, as the IME will need this info to be updated.
+ // TODO(ericrk): Consider folding the above IPC into RenderFrameMetadata.
+ // https://crbug.com/912309
+ LayerTreeHost()->RequestForceSendMetadata();
+#endif
+ }
+}
+
+void WidgetBase::ClearTextInputState() {
+ text_input_info_ = blink::WebTextInputInfo();
+ text_input_type_ = ui::TextInputType::TEXT_INPUT_TYPE_NONE;
+ text_input_mode_ = ui::TextInputMode::TEXT_INPUT_MODE_DEFAULT;
+ can_compose_inline_ = false;
+ text_input_flags_ = 0;
+ next_previous_flags_ = kInvalidNextPreviousFlagsValue;
+}
+
+void WidgetBase::ShowVirtualKeyboardOnElementFocus() {
+#if defined(OS_CHROMEOS)
+ // On ChromeOS, virtual keyboard is triggered only when users leave the
+ // mouse button or the finger and a text input element is focused at that
+ // time. Focus event itself shouldn't trigger virtual keyboard.
+ UpdateTextInputState();
+#else
+ ShowVirtualKeyboard();
+#endif
+
+// TODO(rouslan): Fix ChromeOS and Windows 8 behavior of autofill popup with
+// virtual keyboard.
+#if !defined(OS_ANDROID)
+ client_->FocusChangeComplete();
+#endif
+}
+
+bool WidgetBase::ProcessTouchAction(cc::TouchAction touch_action) {
+ return input_handler_.ProcessTouchAction(touch_action);
+}
+
+void WidgetBase::SetFocus(bool enable) {
+ has_focus_ = enable;
+ client_->FocusChanged(enable);
+}
+
+void WidgetBase::UpdateCompositionInfo(bool immediate_request) {
+ if (!monitor_composition_info_ && !immediate_request)
+ return; // Do not calculate composition info if not requested.
+
+ TRACE_EVENT0("renderer", "WidgetBase::UpdateCompositionInfo");
+ gfx::Range range;
+ Vector<gfx::Rect> character_bounds;
+
+ if (GetTextInputType() == ui::TextInputType::TEXT_INPUT_TYPE_NONE) {
+ // Composition information is only available on editable node.
+ range = gfx::Range::InvalidRange();
+ } else {
+ GetCompositionRange(&range);
+ GetCompositionCharacterBounds(&character_bounds);
+ }
+
+ if (!immediate_request &&
+ !ShouldUpdateCompositionInfo(range, character_bounds)) {
+ return;
+ }
+ composition_character_bounds_ = character_bounds;
+ composition_range_ = range;
+
+ client_->SendCompositionRangeChanged(
+ composition_range_,
+ std::vector<gfx::Rect>(composition_character_bounds_.begin(),
+ composition_character_bounds_.end()));
+}
+
+void WidgetBase::ForceTextInputStateUpdate() {
+#if defined(OS_ANDROID)
+ UpdateSelectionBounds();
+ UpdateTextInputStateInternal(false, true /* reply_to_request */);
+#endif
+}
+
+void WidgetBase::RequestCompositionUpdates(bool immediate_request,
+ bool monitor_updates) {
+ monitor_composition_info_ = monitor_updates;
+ if (!immediate_request)
+ return;
+ UpdateCompositionInfo(true /* immediate request */);
+}
+
+void WidgetBase::GetCompositionRange(gfx::Range* range) {
+ *range = gfx::Range::InvalidRange();
+ FrameWidget* frame_widget = client_->FrameWidget();
+ if (!frame_widget ||
+ frame_widget->Client()->ShouldDispatchImeEventsToPepper())
+ return;
+ *range = frame_widget->CompositionRange();
+}
+
+void WidgetBase::GetCompositionCharacterBounds(Vector<gfx::Rect>* bounds) {
+ DCHECK(bounds);
+ bounds->clear();
+
+ FrameWidget* frame_widget = client_->FrameWidget();
+ if (!frame_widget ||
+ frame_widget->Client()->ShouldDispatchImeEventsToPepper())
+ return;
+
+ frame_widget->GetCompositionCharacterBoundsInWindow(bounds);
+}
+
+bool WidgetBase::ShouldUpdateCompositionInfo(const gfx::Range& range,
+ const Vector<gfx::Rect>& bounds) {
+ if (!range.IsValid())
+ return false;
+ if (composition_range_ != range)
+ return true;
+ if (bounds.size() != composition_character_bounds_.size())
+ return true;
+ for (size_t i = 0; i < bounds.size(); ++i) {
+ if (bounds[i] != composition_character_bounds_[i])
+ return true;
+ }
+ return false;
+}
+
+ui::TextInputType WidgetBase::GetTextInputType() {
+ return ConvertWebTextInputType(client_->GetTextInputType());
+}
+
+void WidgetBase::UpdateSelectionBounds() {
+ TRACE_EVENT0("renderer", "WidgetBase::UpdateSelectionBounds");
+ if (client_->HasCurrentImeGuard(false) ||
+ input_handler_.ProtectedByIMEGuard(false)) {
+ return;
+ }
+#if defined(USE_AURA)
+ // TODO(mohsen): For now, always send explicit selection IPC notifications for
+ // Aura beucause composited selection updates are not working for webview tags
+ // which regresses IME inside webview. Remove this when composited selection
+ // updates are fixed for webviews. See, http://crbug.com/510568.
+ bool send_ipc = true;
+#else
+ // With composited selection updates, the selection bounds will be reported
+ // directly by the compositor, in which case explicit IPC selection
+ // notifications should be suppressed.
+ bool send_ipc = !RuntimeEnabledFeatures::CompositedSelectionUpdateEnabled();
+#endif
+ if (send_ipc) {
+ bool is_anchor_first = false;
+ base::i18n::TextDirection focus_dir =
+ base::i18n::TextDirection::UNKNOWN_DIRECTION;
+ base::i18n::TextDirection anchor_dir =
+ base::i18n::TextDirection::UNKNOWN_DIRECTION;
+
+ FrameWidget* frame_widget = client_->FrameWidget();
+ if (!frame_widget)
+ return;
+ if (frame_widget->GetSelectionBoundsInWindow(
+ &selection_focus_rect_, &selection_anchor_rect_, &focus_dir,
+ &anchor_dir, &is_anchor_first)) {
+ widget_host_->SelectionBoundsChanged(selection_anchor_rect_, anchor_dir,
+ selection_focus_rect_, focus_dir,
+ is_anchor_first);
+ }
+ }
+ UpdateCompositionInfo(false /* not an immediate request */);
}
} // 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
index c59239bbba1..a7b69f8dde6 100644
--- a/chromium/third_party/blink/renderer/platform/widget/widget_base.h
+++ b/chromium/third_party/blink/renderer/platform/widget/widget_base.h
@@ -14,8 +14,12 @@
#include "third_party/blink/public/platform/cross_variant_mojo_util.h"
#include "third_party/blink/public/web/web_widget.h"
#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view_delegate.h"
+#include "third_party/blink/renderer/platform/widget/input/widget_base_input_handler.h"
+#include "ui/base/ime/text_input_mode.h"
+#include "ui/base/ime/text_input_type.h"
namespace cc {
class AnimationHost;
@@ -71,6 +75,12 @@ class PLATFORM_EXPORT WidgetBase : public mojom::blink::Widget,
uint32_t frame_token,
base::OnceCallback<void(base::TimeTicks)> callback);
+ // mojom::blink::Widget overrides:
+ void ForceRedraw(mojom::blink::Widget::ForceRedrawCallback callback) override;
+ void GetWidgetInputHandler(
+ mojo::PendingReceiver<mojom::blink::WidgetInputHandler> request,
+ mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host) override;
+
// LayerTreeDelegate overrides:
// Applies viewport related properties during a commit from the compositor
// thread.
@@ -88,6 +98,9 @@ class PLATFORM_EXPORT WidgetBase : public mojom::blink::Widget,
void RequestNewLayerTreeFrameSink(
LayerTreeFrameSinkCallback callback) override;
void DidCommitAndDrawCompositorFrame() override;
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override;
void WillCommitCompositorFrame() override;
void DidCommitCompositorFrame(base::TimeTicks commit_start_time) override;
void DidCompletePageScaleAnimation() override;
@@ -101,19 +114,58 @@ class PLATFORM_EXPORT WidgetBase : public mojom::blink::Widget,
void EndUpdateLayers() override;
void UpdateVisualState() override;
void WillBeginMainFrame() override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override;
cc::AnimationHost* AnimationHost() const;
cc::LayerTreeHost* LayerTreeHost() const;
scheduler::WebRenderWidgetSchedulingState* RendererWidgetSchedulingState()
const;
+ mojom::blink::WidgetHost* GetWidgetHostRemote() { return widget_host_.get(); }
+
// Returns if we should gather begin main frame metrics. If there is no
// compositor thread this returns false.
static bool ShouldRecordBeginMainFrameMetrics();
+ // Set the current cursor relay to browser if necessary.
void SetCursor(const ui::Cursor& cursor);
+ // Dispatch the virtual keyboard and update text input state.
+ void ShowVirtualKeyboardOnElementFocus();
+
+ // Process the touch action, return true if the action should be
+ // sent to the browser.
+ bool ProcessTouchAction(cc::TouchAction touch_action);
+
+ WidgetBaseInputHandler& input_handler() { return input_handler_; }
+
+ WidgetBaseClient* client() { return client_; }
+
+ void SetToolTipText(const String& tooltip_text, TextDirection dir);
+
+ void ShowVirtualKeyboard();
+ void UpdateSelectionBounds();
+ void UpdateTextInputState();
+ void ClearTextInputState();
+ void ForceTextInputStateUpdate();
+ void RequestCompositionUpdates(bool immediate_request, bool monitor_updates);
+ void UpdateCompositionInfo(bool immediate_request);
+ void SetFocus(bool enable);
+ bool has_focus() const { return has_focus_; }
+
private:
+ bool CanComposeInline();
+ void UpdateTextInputStateInternal(bool show_virtual_keyboard,
+ bool immediate_request);
+ void GetCompositionRange(gfx::Range* range);
+ void GetCompositionCharacterBounds(Vector<gfx::Rect>* bounds);
+ ui::TextInputType GetTextInputType();
+ bool ShouldUpdateCompositionInfo(const gfx::Range& range,
+ const Vector<gfx::Rect>& bounds);
+
std::unique_ptr<LayerTreeView> layer_tree_view_;
WidgetBaseClient* client_;
mojo::AssociatedRemote<mojom::blink::WidgetHost> widget_host_;
@@ -122,6 +174,46 @@ class PLATFORM_EXPORT WidgetBase : public mojom::blink::Widget,
render_widget_scheduling_state_;
bool first_update_visual_state_after_hidden_ = false;
base::TimeTicks was_shown_time_ = base::TimeTicks::Now();
+ bool has_focus_ = false;
+ WidgetBaseInputHandler input_handler_{this};
+
+ // Stores the current selection bounds.
+ gfx::Rect selection_focus_rect_;
+ gfx::Rect selection_anchor_rect_;
+
+ // Stores the current composition character bounds.
+ Vector<gfx::Rect> composition_character_bounds_;
+
+ // Stores the current composition range.
+ gfx::Range composition_range_ = gfx::Range::InvalidRange();
+
+ // True if the IME requests updated composition info.
+ bool monitor_composition_info_ = false;
+ // Stores information about the current text input.
+ blink::WebTextInputInfo text_input_info_;
+
+ // Stores the current text input type of |webwidget_|.
+ ui::TextInputType text_input_type_ = ui::TEXT_INPUT_TYPE_NONE;
+
+ // Stores the current text input mode of |webwidget_|.
+ ui::TextInputMode text_input_mode_ = ui::TEXT_INPUT_MODE_DEFAULT;
+
+ // Stores the current virtualkeyboardpolicy of |webwidget_|.
+ ui::mojom::VirtualKeyboardPolicy vk_policy_ =
+ ui::mojom::VirtualKeyboardPolicy::AUTO;
+
+ // Stores the current text input flags of |webwidget_|.
+ int text_input_flags_ = 0;
+
+ // Indicates whether currently focused input field has next/previous focusable
+ // form input field.
+ int next_previous_flags_;
+
+ // Stores the current type of composition text rendering of |webwidget_|.
+ bool can_compose_inline_ = true;
+
+ // Stores whether the IME should always be hidden for |webwidget_|.
+ bool always_hide_ime_ = false;
};
} // namespace blink
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
index 0fa3495fada..b4ff5ed6bbe 100644
--- a/chromium/third_party/blink/renderer/platform/widget/widget_base_client.h
+++ b/chromium/third_party/blink/renderer/platform/widget/widget_base_client.h
@@ -5,9 +5,14 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_CLIENT_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_CLIENT_H_
+#include <vector>
+
#include "base/time/time.h"
#include "cc/paint/element_id.h"
#include "third_party/blink/public/common/metrics/document_update_reason.h"
+#include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
+#include "third_party/blink/public/platform/input/input_handler_proxy.h"
+#include "third_party/blink/public/platform/web_text_input_type.h"
#include "third_party/blink/public/web/web_lifecycle_update.h"
namespace cc {
@@ -18,6 +23,10 @@ class RenderFrameMetadataObserver;
namespace blink {
+class FrameWidget;
+class WebGestureEvent;
+class WebMouseEvent;
+
// 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.
@@ -93,6 +102,10 @@ class WidgetBaseClient {
virtual void DidBeginMainFrame() {}
virtual void DidCommitAndDrawCompositorFrame() {}
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {}
+
virtual void OnDeferMainFrameUpdatesChanged(bool defer) {}
virtual void OnDeferCommitsChanged(bool defer) {}
@@ -106,6 +119,52 @@ class WidgetBaseClient {
virtual void WillBeginMainFrame() {}
virtual void DidCompletePageScaleAnimation() {}
+
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {}
+ virtual void FocusChangeComplete() {}
+
+ virtual WebInputEventResult DispatchBufferedTouchEvents() = 0;
+ virtual WebInputEventResult HandleInputEvent(
+ const WebCoalescedInputEvent&) = 0;
+ virtual bool SupportsBufferedTouchEvents() = 0;
+
+ virtual void DidHandleKeyEvent() {}
+ virtual bool WillHandleGestureEvent(const WebGestureEvent& event) = 0;
+ virtual bool WillHandleMouseEvent(const WebMouseEvent& event) = 0;
+ virtual void ObserveGestureEventAndResult(
+ const WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& unused_delta,
+ const cc::OverscrollBehavior& overscroll_behavior,
+ bool event_processed) = 0;
+ virtual void QueueSyntheticEvent(std::unique_ptr<WebCoalescedInputEvent>) = 0;
+
+ virtual WebTextInputType GetTextInputType() {
+ return WebTextInputType::kWebTextInputTypeNone;
+ }
+
+ virtual void GetWidgetInputHandler(
+ mojo::PendingReceiver<mojom::blink::WidgetInputHandler> request,
+ mojo::PendingRemote<mojom::blink::WidgetInputHandlerHost> host) = 0;
+
+ // The FrameWidget interface if this is a FrameWidget.
+ virtual FrameWidget* FrameWidget() { return nullptr; }
+
+ // Send the composition change to the browser.
+ virtual void SendCompositionRangeChanged(
+ const gfx::Range& range,
+ const std::vector<gfx::Rect>& character_bounds) = 0;
+
+ // Determine if there is a IME guard.
+ virtual bool HasCurrentImeGuard(bool request_to_show_virtual_keyboard) = 0;
+
+ // Called to inform the Widget that it has gained or lost keyboard focus.
+ virtual void FocusChanged(bool) = 0;
+
+ // Test-specific methods below this point.
+ virtual void ScheduleAnimationForWebTests() {}
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn
index 294afeb47c8..8c627534974 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn
+++ b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -109,6 +109,7 @@ jumbo_component("wtf") {
"text/case_map.cc",
"text/case_map.h",
"text/character_names.h",
+ "text/character_visitor.h",
"text/integer_to_string_conversion.h",
"text/line_ending.cc",
"text/line_ending.h",
diff --git a/chromium/third_party/blink/renderer/platform/wtf/DEPS b/chromium/third_party/blink/renderer/platform/wtf/DEPS
index 43cd2e6f12f..9ccf4961abb 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/DEPS
+++ b/chromium/third_party/blink/renderer/platform/wtf/DEPS
@@ -1,5 +1,5 @@
include_rules = [
- # To whitelist base/ stuff Blink is allowed to include, we list up all
+ # To only allow a subset of base/ in Blink, we explicitly list all
# directories and files instead of writing 'base/'.
"+base/allocator/partition_allocator",
"+base/atomic_ref_count.h",
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md b/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md
index 0a978a43ccb..3bd4780be9f 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/Allocator.md
@@ -4,10 +4,8 @@ All objects in Blink are expected to be allocated with PartitionAlloc or Oilpan.
Blink uses different PartitionAlloc partitions, for different kinds of objects:
-* LayoutObject partition: A partition to allocate `LayoutObject`s.
-The LayoutObject partition is a `SizeSpecificPartitionAllocator`. This means
-that no extra padding is needed to allocate a `LayoutObject` object. Different
-sizes of `LayoutObject`s are allocated in different buckets. Having a dedicated
+* LayoutObject partition: A partition to allocate `LayoutObject`s. The
+LayoutObject partition is a `ThreadUnsafePartitionAllocator`. Having a dedicated
partition for `LayoutObject`s improves cache locality and thus performance.
* Buffer partition: A partition to allocate objects that have a strong risk
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 66fc9943d46..b135f379ddf 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h
@@ -7,6 +7,7 @@
#include <atomic>
+#include "base/check_op.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
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 abb17cad6d9..f2ab11772d1 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -33,8 +33,6 @@
#include "base/allocator/partition_allocator/memory_reclaimer.h"
#include "base/allocator/partition_allocator/oom.h"
#include "base/allocator/partition_allocator/page_allocator.h"
-#include "base/allocator/partition_allocator/partition_alloc.h"
-#include "base/allocator/partition_allocator/partition_root_base.h"
#include "base/debug/alias.h"
#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
@@ -48,10 +46,10 @@ bool Partitions::initialized_ = false;
// 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;
+base::ThreadSafePartitionRoot* Partitions::fast_malloc_root_ = nullptr;
+base::ThreadSafePartitionRoot* Partitions::array_buffer_root_ = nullptr;
+base::ThreadSafePartitionRoot* Partitions::buffer_root_ = nullptr;
+base::ThreadUnsafePartitionRoot* Partitions::layout_root_ = nullptr;
// static
void Partitions::Initialize() {
@@ -61,10 +59,10 @@ void Partitions::Initialize() {
// 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{};
+ static base::PartitionAllocator fast_malloc_allocator{};
+ static base::PartitionAllocator array_buffer_allocator{};
+ static base::PartitionAllocator buffer_allocator{};
+ static base::ThreadUnsafePartitionAllocator layout_allocator{};
base::PartitionAllocGlobalInit(&Partitions::HandleOutOfMemory);
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 4dcc3564ed8..b8b89b1f7ec 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h
@@ -31,17 +31,12 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITIONS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_PARTITIONS_H_
-#include "base/logging.h"
+#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/check.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "third_party/blink/renderer/platform/wtf/wtf_export.h"
-namespace base {
-class PartitionStatsDumper;
-struct PartitionRoot;
-struct PartitionRootGeneric;
-} // namespace base
-
namespace WTF {
class WTF_EXPORT Partitions {
@@ -55,17 +50,17 @@ class WTF_EXPORT Partitions {
static void StartPeriodicReclaim(
scoped_refptr<base::SequencedTaskRunner> task_runner);
- ALWAYS_INLINE static base::PartitionRootGeneric* ArrayBufferPartition() {
+ ALWAYS_INLINE static base::ThreadSafePartitionRoot* ArrayBufferPartition() {
DCHECK(initialized_);
return array_buffer_root_;
}
- ALWAYS_INLINE static base::PartitionRootGeneric* BufferPartition() {
+ ALWAYS_INLINE static base::ThreadSafePartitionRoot* BufferPartition() {
DCHECK(initialized_);
return buffer_root_;
}
- ALWAYS_INLINE static base::PartitionRoot* LayoutPartition() {
+ ALWAYS_INLINE static base::ThreadUnsafePartitionRoot* LayoutPartition() {
DCHECK(initialized_);
return layout_root_;
}
@@ -94,7 +89,7 @@ class WTF_EXPORT Partitions {
static void HandleOutOfMemory(size_t size);
private:
- ALWAYS_INLINE static base::PartitionRootGeneric* FastMallocPartition() {
+ ALWAYS_INLINE static base::ThreadSafePartitionRoot* FastMallocPartition() {
DCHECK(initialized_);
return fast_malloc_root_;
}
@@ -103,10 +98,10 @@ class WTF_EXPORT Partitions {
static bool initialized_;
// See Allocator.md for a description of these partitions.
- static base::PartitionRootGeneric* fast_malloc_root_;
- static base::PartitionRootGeneric* array_buffer_root_;
- static base::PartitionRootGeneric* buffer_root_;
- static base::PartitionRoot* layout_root_;
+ static base::ThreadSafePartitionRoot* fast_malloc_root_;
+ static base::ThreadSafePartitionRoot* array_buffer_root_;
+ static base::ThreadSafePartitionRoot* buffer_root_;
+ static base::ThreadUnsafePartitionRoot* layout_root_;
};
} // namespace WTF
diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions_test.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions_test.cc
index a44f1f9c795..3f6867cfa45 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions_test.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions_test.cc
@@ -3,9 +3,11 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+#include <vector>
+
#include "base/allocator/partition_allocator/memory_reclaimer.h"
#include "build/build_config.h"
-
#include "testing/gtest/include/gtest/gtest.h"
namespace WTF {
@@ -22,16 +24,27 @@ class PartitionsTest : public ::testing::Test {
};
TEST_F(PartitionsTest, MemoryIsInitiallyCommitted) {
+ // std::vector to explicitly not use PartitionAlloc.
+ std::vector<void*> allocated_pointers;
+
size_t committed_before = Partitions::TotalSizeOfCommittedPages();
- void* data = Partitions::BufferMalloc(1, "");
- ASSERT_TRUE(data);
+ // Need to allocate enough memory to require a new super page. Unless nothing
+ // else in the process has allocated anything, this can be after several
+ // iterations.
+ while (Partitions::TotalSizeOfCommittedPages() == committed_before) {
+ void* data = Partitions::BufferMalloc(100, "");
+ ASSERT_TRUE(data);
+ allocated_pointers.push_back(data);
+ }
size_t committed_after = Partitions::TotalSizeOfCommittedPages();
// No buffer data committed initially, hence committed size increases.
EXPECT_GT(committed_after, committed_before);
// Increase is larger than the allocation.
- EXPECT_GT(committed_after, committed_before + 1);
- Partitions::BufferFree(data);
+ EXPECT_GT(committed_after, committed_before + allocated_pointers.size());
+
+ for (void* data : allocated_pointers)
+ Partitions::BufferFree(data);
// Decommit is not triggered by deallocation.
size_t committed_after_free = Partitions::TotalSizeOfCommittedPages();
@@ -39,12 +52,19 @@ TEST_F(PartitionsTest, MemoryIsInitiallyCommitted) {
}
TEST_F(PartitionsTest, Decommit) {
+ std::vector<void*> allocated_pointers;
+
size_t committed_before = Partitions::TotalSizeOfCommittedPages();
- void* data = Partitions::BufferMalloc(1, "");
- ASSERT_TRUE(data);
- Partitions::BufferFree(data);
+ while (Partitions::TotalSizeOfCommittedPages() == committed_before) {
+ void* data = Partitions::BufferMalloc(100, "");
+ ASSERT_TRUE(data);
+ allocated_pointers.push_back(data);
+ }
size_t committed_after = Partitions::TotalSizeOfCommittedPages();
+ for (void* data : allocated_pointers)
+ Partitions::BufferFree(data);
+
// Decommit is not triggered by deallocation.
EXPECT_GT(committed_after, committed_before);
// Decommit works.
diff --git a/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc b/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc
index e5535ff0747..1f0649e3e46 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/assertions_test.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "base/notreached.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/decimal.cc b/chromium/third_party/blink/renderer/platform/wtf/decimal.cc
index 58720c931f3..c1dc5537ed9 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/decimal.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/decimal.cc
@@ -34,6 +34,7 @@
#include <cfloat>
#include "base/macros.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/deque.h b/chromium/third_party/blink/renderer/platform/wtf/deque.h
index 3a3c59e071f..3cad25b9956 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/deque.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/deque.h
@@ -613,7 +613,7 @@ inline void Deque<T, inlineCapacity, Allocator>::erase(wtf_size_t position) {
template <typename T, wtf_size_t inlineCapacity, typename Allocator>
inline DequeIteratorBase<T, inlineCapacity, Allocator>::DequeIteratorBase()
- : deque_(0) {}
+ : deque_(nullptr) {}
template <typename T, wtf_size_t inlineCapacity, typename Allocator>
inline DequeIteratorBase<T, inlineCapacity, Allocator>::DequeIteratorBase(
diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h b/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h
index 9b9ec92aec7..2983b6c4977 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_functions.h
@@ -27,6 +27,7 @@
#include "base/bit_cast.h"
#include "base/memory/scoped_refptr.h"
#include "build/build_config.h"
+#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace WTF {
@@ -277,6 +278,59 @@ struct DefaultHash<std::pair<T, U>> {
using Hash = PairHash<T, U>;
};
+// Wrapper for integral type to extend to have 0 and max keys.
+template <typename T>
+struct IntegralWithAllKeys {
+ IntegralWithAllKeys() : IntegralWithAllKeys(0, ValueType::kEmpty) {}
+ explicit IntegralWithAllKeys(T value)
+ : IntegralWithAllKeys(value, ValueType::kValid) {}
+ explicit IntegralWithAllKeys(HashTableDeletedValueType)
+ : IntegralWithAllKeys(0, ValueType::kDeleted) {}
+
+ bool IsHashTableDeletedValue() const {
+ return value_type_ == ValueType::kDeleted;
+ }
+
+ unsigned Hash() const {
+ return HashInts(value_, static_cast<unsigned>(value_type_));
+ }
+
+ bool operator==(const IntegralWithAllKeys& b) const {
+ return value_ == b.value_ && value_type_ == b.value_type_;
+ }
+
+ private:
+ enum class ValueType : uint8_t { kEmpty, kValid, kDeleted };
+
+ IntegralWithAllKeys(T value, ValueType value_type)
+ : value_(value), value_type_(value_type) {
+ static_assert(std::is_integral<T>::value,
+ "Only integral types are supported.");
+ }
+
+ T value_;
+ ValueType value_type_;
+};
+
+// Specialization for integral type to have all possible values for key
+// including 0 and max.
+template <typename T>
+struct IntegralWithAllKeysHash {
+ static unsigned GetHash(const IntegralWithAllKeys<T>& key) {
+ return key.Hash();
+ }
+ static bool Equal(const IntegralWithAllKeys<T>& a,
+ const IntegralWithAllKeys<T>& b) {
+ return a == b;
+ }
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+template <typename T>
+struct DefaultHash<IntegralWithAllKeys<T>> {
+ using Hash = IntegralWithAllKeysHash<T>;
+};
+
} // namespace WTF
using WTF::DefaultHash;
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 f5149002584..19dfe94dddf 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h
@@ -231,6 +231,12 @@ struct SimpleClassHashTraits : GenericHashTraits<T> {
}
};
+// Default traits disallow both 0 and max as keys -- use these traits to allow
+// all values as keys.
+template <typename T>
+struct HashTraits<IntegralWithAllKeys<T>>
+ : SimpleClassHashTraits<IntegralWithAllKeys<T>> {};
+
template <typename P>
struct HashTraits<scoped_refptr<P>> : SimpleClassHashTraits<scoped_refptr<P>> {
static_assert(sizeof(void*) == sizeof(scoped_refptr<P>),
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 87f30a5e433..a84feeba20c 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
@@ -1037,7 +1037,8 @@ class NewLinkedHashSet {
}
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 {
value_to_index_.Trace(visitor);
list_.Trace(visitor);
}
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 1aaaa1c60d2..f8aa9d89e38 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc
@@ -6,6 +6,7 @@
#include "third_party/blink/renderer/platform/wtf/stack_util.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/ascii_ctype.h b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_ctype.h
index b152126b544..de982bb0e29 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/ascii_ctype.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_ctype.h
@@ -29,6 +29,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_CTYPE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_CTYPE_H_
+#include "base/check_op.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
index bb2566fadfe..7a15023d847 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
@@ -27,6 +27,7 @@
#include "base/compiler_specific.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
#if defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
@@ -77,8 +78,8 @@ inline bool IsAllASCII(MachineWord word) {
// Note: This function assume the input is likely all ASCII, and
// does not leave early if it is not the case.
template <typename CharacterType>
-inline bool CharactersAreAllASCII(const CharacterType* characters,
- size_t length) {
+ALWAYS_INLINE bool CharactersAreAllASCII(const CharacterType* characters,
+ size_t length) {
DCHECK_GT(length, 0u);
MachineWord all_char_bits = 0;
const CharacterType* end = characters + length;
@@ -108,6 +109,88 @@ inline bool CharactersAreAllASCII(const CharacterType* characters,
return !(all_char_bits & non_ascii_bit_mask);
}
+template <typename CharacterType>
+ALWAYS_INLINE bool IsLowerASCII(const CharacterType* characters,
+ size_t length) {
+ bool contains_upper_case = false;
+ for (wtf_size_t i = 0; i < length; i++) {
+ contains_upper_case |= IsASCIIUpper(characters[i]);
+ }
+ return !contains_upper_case;
+}
+
+template <typename CharacterType>
+ALWAYS_INLINE bool IsUpperASCII(const CharacterType* characters,
+ size_t length) {
+ bool contains_lower_case = false;
+ for (wtf_size_t i = 0; i < length; i++) {
+ contains_lower_case |= IsASCIILower(characters[i]);
+ }
+ return !contains_lower_case;
+}
+
+class LowerConverter {
+ public:
+ template <typename CharType>
+ ALWAYS_INLINE static bool IsCorrectCase(CharType* characters, size_t length) {
+ return IsLowerASCII(characters, length);
+ }
+
+ template <typename CharType>
+ ALWAYS_INLINE static CharType Convert(CharType ch) {
+ return ToASCIILower(ch);
+ }
+};
+
+class UpperConverter {
+ public:
+ template <typename CharType>
+ ALWAYS_INLINE static bool IsCorrectCase(CharType* characters, size_t length) {
+ return IsUpperASCII(characters, length);
+ }
+
+ template <typename CharType>
+ ALWAYS_INLINE static CharType Convert(CharType ch) {
+ return ToASCIIUpper(ch);
+ }
+};
+
+template <typename StringType, typename Converter, typename Allocator>
+ALWAYS_INLINE typename Allocator::ResultStringType ConvertASCIICase(
+ const StringType& string,
+ Converter&& converter,
+ Allocator&& allocator) {
+ CHECK_LE(string.length(), std::numeric_limits<wtf_size_t>::max());
+
+ // First scan the string for uppercase and non-ASCII characters:
+ wtf_size_t length = string.length();
+ if (string.Is8Bit()) {
+ if (converter.IsCorrectCase(string.Characters8(), length)) {
+ return allocator.CoerceOriginal(string);
+ }
+
+ LChar* data8;
+ auto new_impl = allocator.Alloc(length, data8);
+
+ for (wtf_size_t i = 0; i < length; ++i) {
+ data8[i] = converter.Convert(string.Characters8()[i]);
+ }
+ return new_impl;
+ }
+
+ if (converter.IsCorrectCase(string.Characters16(), length)) {
+ return allocator.CoerceOriginal(string);
+ }
+
+ UChar* data16;
+ auto new_impl = allocator.Alloc(length, data16);
+
+ for (wtf_size_t i = 0; i < length; ++i) {
+ data16[i] = converter.Convert(string.Characters16()[i]);
+ }
+ return new_impl;
+}
+
inline void CopyLCharsFromUCharSource(LChar* destination,
const UChar* source,
size_t length) {
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc
index 2d9af4b7184..2692f849855 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.cc
@@ -4,40 +4,15 @@
#include "third_party/blink/renderer/platform/wtf/text/atomic_string_table.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/blink/renderer/platform/wtf/text/utf8.h"
namespace WTF {
-AtomicStringTable::AtomicStringTable() {
- for (StringImpl* string : StringImpl::AllStaticStrings().Values())
- Add(string);
-}
-
-AtomicStringTable::~AtomicStringTable() {
- for (StringImpl* string : table_) {
- if (!string->IsStatic()) {
- DCHECK(string->IsAtomic());
- string->SetIsAtomic(false);
- }
- }
-}
-
-void AtomicStringTable::ReserveCapacity(unsigned size) {
- table_.ReserveCapacityForSize(size);
-}
-
-template <typename T, typename HashTranslator>
-scoped_refptr<StringImpl> AtomicStringTable::AddToStringTable(const T& value) {
- HashSet<StringImpl*>::AddResult add_result =
- table_.AddWithTranslator<HashTranslator>(value);
-
- // If the string is newly-translated, then we need to adopt it.
- // The boolean in the pair tells us if that is so.
- return add_result.is_new_entry ? base::AdoptRef(*add_result.stored_value)
- : *add_result.stored_value;
-}
+namespace {
+// TODO(ajwong): consider replacing with a span in the future.
template <typename CharacterType>
struct HashTranslatorCharBuffer {
const CharacterType* s;
@@ -147,6 +122,85 @@ struct HashAndUTF8CharactersTranslator {
}
};
+struct StringViewLookupTranslator {
+ static unsigned GetHash(const StringView& buf) {
+ StringImpl* shared_impl = buf.SharedImpl();
+ if (LIKELY(shared_impl))
+ return shared_impl->GetHash();
+
+ if (buf.Is8Bit()) {
+ return StringHasher::ComputeHashAndMaskTop8Bits(buf.Characters8(),
+ buf.length());
+ } else {
+ return StringHasher::ComputeHashAndMaskTop8Bits(buf.Characters16(),
+ buf.length());
+ }
+ }
+
+ static bool Equal(StringImpl* const& str, const StringView& buf) {
+ return *str == buf;
+ }
+};
+
+struct LowercaseStringViewLookupTranslator {
+ template <typename CharType>
+ static UChar ToASCIILowerUChar(CharType ch) {
+ return ToASCIILower(ch);
+ }
+
+ static unsigned GetHash(const StringView& buf) {
+ // If possible, use cached hash if the string is lowercased.
+ StringImpl* shared_impl = buf.SharedImpl();
+ if (LIKELY(shared_impl && buf.IsLowerASCII()))
+ return shared_impl->GetHash();
+
+ if (buf.Is8Bit()) {
+ return StringHasher::ComputeHashAndMaskTop8Bits<LChar,
+ ToASCIILowerUChar<LChar>>(
+ buf.Characters8(), buf.length());
+ } else {
+ return StringHasher::ComputeHashAndMaskTop8Bits<UChar,
+ ToASCIILowerUChar<UChar>>(
+ buf.Characters16(), buf.length());
+ }
+ }
+
+ static bool Equal(StringImpl* const& str, const StringView& buf) {
+ return EqualIgnoringASCIICase(StringView(str), buf);
+ }
+};
+
+} // namespace
+
+AtomicStringTable::AtomicStringTable() {
+ for (StringImpl* string : StringImpl::AllStaticStrings().Values())
+ Add(string);
+}
+
+AtomicStringTable::~AtomicStringTable() {
+ for (StringImpl* string : table_) {
+ if (!string->IsStatic()) {
+ DCHECK(string->IsAtomic());
+ string->SetIsAtomic(false);
+ }
+ }
+}
+
+void AtomicStringTable::ReserveCapacity(unsigned size) {
+ table_.ReserveCapacityForSize(size);
+}
+
+template <typename T, typename HashTranslator>
+scoped_refptr<StringImpl> AtomicStringTable::AddToStringTable(const T& value) {
+ HashSet<StringImpl*>::AddResult add_result =
+ table_.AddWithTranslator<HashTranslator>(value);
+
+ // If the string is newly-translated, then we need to adopt it.
+ // The boolean in the pair tells us if that is so.
+ return add_result.is_new_entry ? base::AdoptRef(*add_result.stored_value)
+ : *add_result.stored_value;
+}
+
scoped_refptr<StringImpl> AtomicStringTable::Add(const UChar* s,
unsigned length) {
if (!s)
@@ -220,6 +274,67 @@ scoped_refptr<StringImpl> AtomicStringTable::AddUTF8(
HashAndUTF8CharactersTranslator>(buffer);
}
+AtomicStringTable::WeakResult AtomicStringTable::WeakFindSlow(
+ StringImpl* string) {
+ DCHECK(string->length());
+ const auto& it = table_.find(string);
+ if (it == table_.end())
+ return WeakResult();
+ return WeakResult(*it);
+}
+
+AtomicStringTable::WeakResult AtomicStringTable::WeakFindSlow(
+ const StringView& string) {
+ DCHECK(string.length());
+ const auto& it = table_.Find<StringViewLookupTranslator>(string);
+ if (it == table_.end())
+ return WeakResult();
+ return WeakResult(*it);
+}
+
+AtomicStringTable::WeakResult AtomicStringTable::WeakFindLowercasedSlow(
+ const StringView& string) {
+ DCHECK(string.length());
+ const auto& it = table_.Find<LowercaseStringViewLookupTranslator>(string);
+ if (it == table_.end())
+ return WeakResult();
+ return WeakResult(*it);
+}
+
+AtomicStringTable::WeakResult AtomicStringTable::WeakFind(const LChar* chars,
+ unsigned length) {
+ if (!chars)
+ return WeakResult();
+
+ // Mirror the empty logic in Add().
+ if (!length)
+ return WeakResult(StringImpl::empty_);
+
+ LCharBuffer buffer = {chars, length};
+ const auto& it = table_.Find<LCharBufferTranslator>(buffer);
+ if (it == table_.end())
+ return WeakResult();
+
+ return WeakResult(*it);
+}
+
+AtomicStringTable::WeakResult AtomicStringTable::WeakFind(const UChar* chars,
+ unsigned length) {
+ if (!chars)
+ return WeakResult();
+
+ // Mirror the empty logic in Add().
+ if (!length)
+ return WeakResult(StringImpl::empty_);
+
+ UCharBuffer buffer = {chars, length};
+ const auto& it = table_.Find<UCharBufferTranslator>(buffer);
+ if (it == table_.end())
+ return WeakResult();
+
+ return WeakResult(*it);
+}
+
void AtomicStringTable::Remove(StringImpl* string) {
DCHECK(string->IsAtomic());
auto iterator = table_.find(string);
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h
index 33dce8b8485..78c06758a0a 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/atomic_string_table.h
@@ -46,6 +46,78 @@ class WTF_EXPORT AtomicStringTable final {
scoped_refptr<StringImpl> AddUTF8(const char* characters_start,
const char* characters_end);
+ // Returned as part of the WeakFind() APIs below. Represents the result of
+ // the non-creating lookup within the AtomicStringTable. See the WeakFind()
+ // documentation for a description of how it can be used.
+ class WeakResult {
+ public:
+ WeakResult() = default;
+ explicit WeakResult(StringImpl* str)
+ : ptr_value_(reinterpret_cast<uintptr_t>(str)) {
+ CHECK(!str || str->IsAtomic() || str == StringImpl::empty_);
+ }
+
+ bool operator==(const AtomicString& s) const { return *this == s.Impl(); }
+ bool operator==(const String& s) const { return *this == s.Impl(); }
+ bool operator==(const StringImpl* str) const {
+ return reinterpret_cast<uintptr_t>(str) == ptr_value_;
+ }
+
+ bool IsNull() const { return ptr_value_ != 0; }
+
+ private:
+ // Contains the pointer a string in a non-deferenceable form. Do NOT cast
+ // back to a StringImpl and dereference. The object may no longer be alive.
+ uintptr_t ptr_value_ = 0;
+ };
+
+ // Checks for existence of a string in the AtomicStringTable without
+ // unnecessarily creating an AtomicString. Useful to optimize fast-path
+ // non-existence checks inside collections of AtomicStrings.
+ //
+ // Specifically, if WeakFind() returns an IsNull() WeakResult, then a
+ // collection search can be skipped because the AtomicString cannot exist
+ // in the collection. If WeakFind() returns a non-null WeakResult, then
+ // assuming the target collection has no concurrent access, this lookup
+ // can be reused to check for existence in the collection without
+ // requiring either an AtomicString collection or another lookup within
+ // the AtomicStringTable.
+ WeakResult WeakFind(StringImpl* string) {
+ // Mirror the empty logic in Add().
+ if (UNLIKELY(!string->length()))
+ return WeakResult(StringImpl::empty_);
+
+ if (LIKELY(string->IsAtomic()))
+ return WeakResult(string);
+
+ return WeakFindSlow(string);
+ }
+
+ WeakResult WeakFind(const StringView& string) {
+ // Mirror the empty logic in Add().
+ if (UNLIKELY(!string.length()))
+ return WeakResult(StringImpl::empty_);
+
+ if (LIKELY(string.IsAtomic()))
+ return WeakResult(string.SharedImpl());
+
+ return WeakFindSlow(string);
+ }
+
+ WeakResult WeakFind(const LChar* chars, unsigned length);
+ WeakResult WeakFind(const UChar* chars, unsigned length);
+
+ WeakResult WeakFindLowercased(const StringView& string) {
+ // Mirror the empty logic in Add().
+ if (UNLIKELY(!string.length()))
+ return WeakResult(StringImpl::empty_);
+
+ if (LIKELY(string.IsAtomic() && string.IsLowerASCII()))
+ return WeakResult(string.SharedImpl());
+
+ return WeakFindLowercasedSlow(string);
+ }
+
// This is for ~StringImpl to unregister a string before destruction since
// the table is holding weak pointers. It should not be used directly.
void Remove(StringImpl*);
@@ -54,6 +126,10 @@ class WTF_EXPORT AtomicStringTable final {
template <typename T, typename HashTranslator>
inline scoped_refptr<StringImpl> AddToStringTable(const T& value);
+ WeakResult WeakFindSlow(StringImpl*);
+ WeakResult WeakFindSlow(const StringView&);
+ WeakResult WeakFindLowercasedSlow(const StringView& string);
+
HashSet<StringImpl*> table_;
DISALLOW_COPY_AND_ASSIGN(AtomicStringTable);
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/case_map.cc b/chromium/third_party/blink/renderer/platform/wtf/text/case_map.cc
index 729497cf0f0..3b0bcf074ef 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/case_map.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/case_map.cc
@@ -6,6 +6,7 @@
#include <unicode/casemap.h>
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/character_visitor.h b/chromium/third_party/blink/renderer/platform/wtf/text/character_visitor.h
new file mode 100644
index 00000000000..2c41ca5671c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/character_visitor.h
@@ -0,0 +1,42 @@
+// 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_TEXT_CHARACTER_VISITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CHARACTER_VISITOR_H_
+
+namespace WTF {
+
+// Visits the characters of a WTF::String, StringView or compatible type.
+//
+// Intended to be used with a generic lambda or other functor overloaded to
+// handle either LChar* or UChar*. Reduces code duplication in many cases.
+// The functor should return the same type in both branches.
+//
+// Callers should ensure that characters exist (i.e. the string is not null)
+// first.
+//
+// Example:
+//
+// if (string.IsNull())
+// return false;
+//
+// return WTF::VisitCharacters(string, [&](const auto* chars, unsigned len) {
+// bool contains_space = false;
+// for (unsigned i = 0; i < len; i++)
+// contains_space |= IsASCIISpace(chars[i]);
+// return contains_space;
+// });
+//
+// This will instantiate the functor for both LChar (8-bit) and UChar (16-bit)
+// automatically.
+template <typename StringType, typename Functor>
+decltype(auto) VisitCharacters(const StringType& string,
+ const Functor& functor) {
+ return string.Is8Bit() ? functor(string.Characters8(), string.length())
+ : functor(string.Characters16(), string.length());
+}
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CHARACTER_VISITOR_H_
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/math_transform.cc b/chromium/third_party/blink/renderer/platform/wtf/text/math_transform.cc
index d8a257029d6..1d76435dbbb 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/math_transform.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/math_transform.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/wtf/text/math_transform.h"
+#include "base/check.h"
#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h b/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
index 4b5ea50a60d..25f5faa6a3b 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
@@ -5,7 +5,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
-#include "base/logging.h"
+#include "base/check_op.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_hasher.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_hasher.h
index 492cb7bb114..83673c53d79 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/string_hasher.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_hasher.h
@@ -196,8 +196,9 @@ class StringHasher {
}
private:
+ // The StringHasher works on UChar so all converters should normalize input
+ // data into being a UChar.
static UChar DefaultConverter(UChar character) { return character; }
-
static UChar DefaultConverter(LChar character) { return character; }
unsigned AvalancheBits() const {
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 a02a45ef137..6a9fcfd1335 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
@@ -338,79 +338,26 @@ wtf_size_t StringImpl::CopyTo(UChar* buffer,
return number_of_characters_to_copy;
}
-scoped_refptr<StringImpl> StringImpl::LowerASCII() {
- // First scan the string for uppercase and non-ASCII characters:
- if (Is8Bit()) {
- wtf_size_t first_index_to_be_lowered = length_;
- for (wtf_size_t i = 0; i < length_; ++i) {
- LChar ch = Characters8()[i];
- if (IsASCIIUpper(ch)) {
- first_index_to_be_lowered = i;
- break;
- }
- }
-
- // Nothing to do if the string is all ASCII with no uppercase.
- if (first_index_to_be_lowered == length_) {
- return this;
- }
-
- LChar* data8;
- scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
- memcpy(data8, Characters8(), first_index_to_be_lowered);
+class StringImplAllocator {
+ public:
+ using ResultStringType = scoped_refptr<StringImpl>;
- for (wtf_size_t i = first_index_to_be_lowered; i < length_; ++i) {
- LChar ch = Characters8()[i];
- data8[i] = IsASCIIUpper(ch) ? ToASCIILower(ch) : ch;
- }
- return new_impl;
+ template <typename CharType>
+ scoped_refptr<StringImpl> Alloc(wtf_size_t length, CharType*& buffer) {
+ return StringImpl::CreateUninitialized(length, buffer);
}
- bool no_upper = true;
- UChar ored = 0;
- const UChar* end = Characters16() + length_;
- for (const UChar* chp = Characters16(); chp != end; ++chp) {
- if (IsASCIIUpper(*chp))
- no_upper = false;
- ored |= *chp;
+ scoped_refptr<StringImpl> CoerceOriginal(const StringImpl& string) {
+ return const_cast<StringImpl*>(&string);
}
- // Nothing to do if the string is all ASCII with no uppercase.
- if (no_upper && !(ored & ~0x7F))
- return this;
-
- CHECK_LE(length_, static_cast<wtf_size_t>(numeric_limits<wtf_size_t>::max()));
- wtf_size_t length = length_;
-
- UChar* data16;
- scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
+};
- for (wtf_size_t i = 0; i < length; ++i) {
- UChar c = Characters16()[i];
- data16[i] = IsASCIIUpper(c) ? ToASCIILower(c) : c;
- }
- return new_impl;
+scoped_refptr<StringImpl> StringImpl::LowerASCII() {
+ return ConvertASCIICase(*this, LowerConverter(), StringImplAllocator());
}
scoped_refptr<StringImpl> StringImpl::UpperASCII() {
- if (Is8Bit()) {
- LChar* data8;
- scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data8);
-
- for (wtf_size_t i = 0; i < length_; ++i) {
- LChar c = Characters8()[i];
- data8[i] = IsASCIILower(c) ? ToASCIIUpper(c) : c;
- }
- return new_impl;
- }
-
- UChar* data16;
- scoped_refptr<StringImpl> new_impl = CreateUninitialized(length_, data16);
-
- for (wtf_size_t i = 0; i < length_; ++i) {
- UChar c = Characters16()[i];
- data16[i] = IsASCIILower(c) ? ToASCIIUpper(c) : c;
- }
- return new_impl;
+ return ConvertASCIICase(*this, UpperConverter(), StringImplAllocator());
}
scoped_refptr<StringImpl> StringImpl::Fill(UChar character) {
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc
index 6f0bb8874b6..9a56ddff705 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.cc
@@ -9,6 +9,26 @@
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace WTF {
+namespace {
+class StackStringViewAllocator {
+ public:
+ explicit StackStringViewAllocator(
+ StringView::StackBackingStore& backing_store)
+ : backing_store_(backing_store) {}
+ using ResultStringType = StringView;
+
+ template <typename CharType>
+ StringView Alloc(wtf_size_t length, CharType*& buffer) {
+ buffer = backing_store_.Realloc<CharType>(length);
+ return StringView(buffer, length);
+ }
+
+ StringView CoerceOriginal(StringView string) { return string; }
+
+ private:
+ StringView::StackBackingStore& backing_store_;
+};
+} // namespace
StringView::StringView(const UChar* chars)
: StringView(chars, chars ? LengthOfNullTerminatedString(chars) : 0) {}
@@ -109,4 +129,10 @@ bool EqualIgnoringASCIICase(const StringView& a, const StringView& b) {
return EqualIgnoringASCIICase(a.Characters16(), b.Characters16(), a.length());
}
+StringView StringView::LowerASCIIMaybeUsingBuffer(
+ StackBackingStore& buffer) const {
+ return ConvertASCIICase(*this, LowerConverter(),
+ StackStringViewAllocator(buffer));
+}
+
} // namespace WTF
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 06611ab2a58..0178c7b0c59 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
@@ -33,6 +33,47 @@ class WTF_EXPORT StringView {
DISALLOW_NEW();
public:
+ // A buffer that allows for short strings to be held on the stack during a
+ // transform. This is a performance optimization for very hot paths and
+ // should rarely need to be used.
+ class StackBackingStore {
+ public:
+ // Returns a pointer to a buffer of size |length| that is valid for as long
+ // the StackBackingStore object is alive and Realloc() has not been called
+ // again.
+ template <typename CharT>
+ CharT* Realloc(int length) {
+ size_t size = length * sizeof(CharT);
+ if (UNLIKELY(size > sizeof(stackbuf16_))) {
+ heapbuf_.reset(reinterpret_cast<char*>(
+ WTF::Partitions::BufferMalloc(size, "StackBackingStore")));
+ return reinterpret_cast<CharT*>(heapbuf_.get());
+ }
+
+ // If the Realloc() shrinks the buffer size, |heapbuf_| will keep a copy
+ // of the old string. A reset can be added here, but given this is a
+ // transient usage, deferring to the destructor is just as good and avoids
+ // another branch.
+ static_assert(alignof(decltype(stackbuf16_)) % alignof(CharT) == 0,
+ "stack buffer must be sufficiently aligned");
+ return reinterpret_cast<CharT*>(&stackbuf16_[0]);
+ }
+
+ public:
+ struct BufferDeleter {
+ void operator()(void* buffer) { WTF::Partitions::BufferFree(buffer); }
+ };
+
+ static_assert(sizeof(UChar) != sizeof(char),
+ "A char array will trigger -fstack-protect an produce "
+ "overkill stack canaries all over v8 bindings");
+
+ // The size 64 is just a guess on a good size. No data was used in its
+ // selection.
+ UChar stackbuf16_[64];
+ std::unique_ptr<char[], BufferDeleter> heapbuf_;
+ };
+
// Null string.
StringView() { Clear(); }
@@ -70,7 +111,7 @@ class WTF_EXPORT StringView {
// From a literal string or LChar buffer:
StringView(const LChar* chars, unsigned length)
- : impl_(StringImpl::empty_), characters8_(chars), length_(length) {}
+ : impl_(StringImpl::empty_), bytes_(chars), length_(length) {}
StringView(const char* chars, unsigned length)
: StringView(reinterpret_cast<const LChar*>(chars), length) {}
StringView(const LChar* chars)
@@ -83,9 +124,7 @@ class WTF_EXPORT StringView {
// From a wide literal string or UChar buffer.
StringView(const UChar* chars, unsigned length)
- : impl_(StringImpl::empty16_bit_),
- characters16_(chars),
- length_(length) {}
+ : impl_(StringImpl::empty16_bit_), bytes_(chars), length_(length) {}
StringView(const UChar* chars);
StringView(const char16_t* chars)
: StringView(reinterpret_cast<const UChar*>(chars)) {}
@@ -104,6 +143,15 @@ class WTF_EXPORT StringView {
return impl_->Is8Bit();
}
+ bool IsAtomic() const { return SharedImpl() && SharedImpl()->IsAtomic(); }
+
+ bool IsLowerASCII() const {
+ if (Is8Bit()) {
+ return WTF::IsLowerASCII(Characters8(), length());
+ }
+ return WTF::IsLowerASCII(Characters16(), length());
+ }
+
void Clear();
UChar operator[](unsigned i) const {
@@ -115,22 +163,22 @@ class WTF_EXPORT StringView {
const LChar* Characters8() const {
DCHECK(Is8Bit());
- return characters8_;
+ return static_cast<const LChar*>(bytes_);
}
const UChar* Characters16() const {
DCHECK(!Is8Bit());
- return characters16_;
+ return static_cast<const UChar*>(bytes_);
}
base::span<const LChar> Span8() const {
DCHECK(Is8Bit());
- return {characters8_, length_};
+ return {static_cast<const LChar*>(bytes_), length_};
}
base::span<const UChar> Span16() const {
DCHECK(!Is8Bit());
- return {characters16_, length_};
+ return {static_cast<const UChar*>(bytes_), length_};
}
UChar32 CodepointAt(unsigned i) const {
@@ -157,6 +205,15 @@ class WTF_EXPORT StringView {
return nullptr;
}
+ // This will return a StringView with a version of |this| that has all ASCII
+ // characters lowercased. The returned StringView is guarantee to be valid for
+ // as long as |backing_store| is valid.
+ //
+ // The odd lifetime of the returned object occurs because lowercasing may
+ // require allocation. When that happens, |backing_store| is used as the
+ // backing store and the returned StringView has the same lifetime.
+ StringView LowerASCIIMaybeUsingBuffer(StackBackingStore& backing_store) const;
+
String ToString() const;
AtomicString ToAtomicString() const;
@@ -174,11 +231,7 @@ class WTF_EXPORT StringView {
#else
StringImpl* impl_;
#endif
- union {
- const LChar* characters8_;
- const UChar* characters16_;
- const void* bytes_;
- };
+ const void* bytes_;
unsigned length_;
};
@@ -188,9 +241,9 @@ inline StringView::StringView(const StringView& view,
: impl_(view.impl_), length_(length) {
SECURITY_DCHECK(offset + length <= view.length());
if (Is8Bit())
- characters8_ = view.Characters8() + offset;
+ bytes_ = view.Characters8() + offset;
else
- characters16_ = view.Characters16() + offset;
+ bytes_ = view.Characters16() + offset;
}
inline StringView::StringView(const StringImpl* impl) {
@@ -236,9 +289,9 @@ inline void StringView::Set(const StringImpl& impl,
length_ = length;
impl_ = const_cast<StringImpl*>(&impl);
if (impl.Is8Bit())
- characters8_ = impl.Characters8() + offset;
+ bytes_ = impl.Characters8() + offset;
else
- characters16_ = impl.Characters16() + offset;
+ bytes_ = impl.Characters16() + offset;
}
// Unicode aware case insensitive string matching. Non-ASCII characters might
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc
index 2943851dc39..fbe7de479dd 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.cc
@@ -25,6 +25,7 @@
*/
#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "base/notreached.h"
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 3d4f4108d57..4f524c5500e 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
@@ -29,6 +29,7 @@
#include <memory>
#include "base/macros.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/text/unicode.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
index 810d1cd9181..b62573550a1 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
+++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_icu.cc
@@ -32,6 +32,7 @@
#include <unicode/ucnv_cb.h>
#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading.h b/chromium/third_party/blink/renderer/platform/wtf/threading.h
index 0a21c3fb321..0943e791cf5 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/threading.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/threading.h
@@ -33,7 +33,7 @@
#include <stdint.h>
#include <memory>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
diff --git a/chromium/third_party/blink/renderer/platform/wtf/tree_node.h b/chromium/third_party/blink/renderer/platform/wtf/tree_node.h
index b25022a62b9..6aa4e744cfb 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/tree_node.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/tree_node.h
@@ -31,6 +31,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TREE_NODE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TREE_NODE_H_
+#include "base/check_op.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace WTF {
diff --git a/chromium/third_party/blink/renderer/platform/wtf/type_traits.h b/chromium/third_party/blink/renderer/platform/wtf/type_traits.h
index 44c6f9219ca..60893080de3 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/type_traits.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/type_traits.h
@@ -132,6 +132,19 @@ class Visitor;
namespace WTF {
+namespace internal {
+// IsTraceMethodConst is used to verify that all Trace methods are marked as
+// const. It is equivalent to IsTraceable but for a non-const object.
+template <typename T, typename = void>
+struct IsTraceMethodConst : std::false_type {};
+
+template <typename T>
+struct IsTraceMethodConst<T,
+ base::void_t<decltype(std::declval<const T>().Trace(
+ std::declval<blink::Visitor*>()))>>
+ : std::true_type {};
+} // namespace internal
+
template <typename T, typename = void>
struct IsTraceable : std::false_type {
// Fail on incomplete types.
@@ -142,7 +155,13 @@ struct IsTraceable : std::false_type {
template <typename T>
struct IsTraceable<T,
base::void_t<decltype(std::declval<T>().Trace(
- std::declval<blink::Visitor*>()))>> : std::true_type {};
+ std::declval<blink::Visitor*>()))>> : std::true_type {
+ // All Trace methods should be marked as const. If an object of type
+ // 'T' is traceable then any object of type 'const T' should also
+ // be traceable.
+ static_assert(internal::IsTraceMethodConst<T>(),
+ "Trace methods should be marked as const.");
+};
template <typename T, typename U>
struct IsTraceable<std::pair<T, U>>
diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector.h b/chromium/third_party/blink/renderer/platform/wtf/vector.h
index 267eb905013..959dffbf5f7 100644
--- a/chromium/third_party/blink/renderer/platform/wtf/vector.h
+++ b/chromium/third_party/blink/renderer/platform/wtf/vector.h
@@ -74,6 +74,19 @@ class Deque;
// If you want to change the behavior of your type, take a look at VectorTraits
// (defined in VectorTraits.h), too.
+// Tracing assumes the entire backing store is safe to access. To guarantee
+// that, tracing a backing store starts by marking the whole backing store
+// capacity as accessible. With concurrent marking enabled, annotating size
+// changes could conflict with marking the whole store as accessible, causing
+// a race.
+#define MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, buffer, capacity, \
+ old_size, new_size) \
+ if (Allocator::kIsGarbageCollected && Allocator::IsIncrementalMarking()) { \
+ ANNOTATE_CHANGE_SIZE(buffer, capacity, 0, capacity); \
+ } else { \
+ ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size) \
+ }
+
template <bool needsDestruction, typename T>
struct VectorDestructor;
@@ -848,8 +861,10 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> {
DCHECK(other_source_begin);
DCHECK_EQ(Buffer(), InlineBuffer());
DCHECK_EQ(other.Buffer(), other.InlineBuffer());
- ANNOTATE_CHANGE_SIZE(buffer_, inlineCapacity, size_, other.size_);
- ANNOTATE_CHANGE_SIZE(other.buffer_, inlineCapacity, other.size_, size_);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, buffer_, inlineCapacity,
+ size_, other.size_);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, other.buffer_,
+ inlineCapacity, other.size_, size_);
std::swap(size_, other.size_);
}
@@ -950,11 +965,10 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> {
return unsafe_reinterpret_cast_ptr<const T*>(inline_buffer_);
}
- template <bool = Allocator::kIsGarbageCollected>
- void InitInlinedBuffer() {}
- template <>
- void InitInlinedBuffer<true>() {
- memset(&inline_buffer_, 0, kInlineBufferSize);
+ void InitInlinedBuffer() {
+ if (Allocator::kIsGarbageCollected) {
+ memset(&inline_buffer_, 0, kInlineBufferSize);
+ }
}
alignas(T) char inline_buffer_[kInlineBufferSize];
@@ -1536,7 +1550,8 @@ operator=(const Vector<T, inlineCapacity, Allocator>& other) {
DCHECK(begin());
}
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, other.size());
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ other.size());
TypeOperations::Copy(other.begin(), other.begin() + size(), begin());
TypeOperations::UninitializedCopy(other.begin() + size(), other.end(), end());
size_ = other.size();
@@ -1565,7 +1580,8 @@ operator=(const Vector<T, otherCapacity, Allocator>& other) {
DCHECK(begin());
}
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, other.size());
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ other.size());
TypeOperations::Copy(other.begin(), other.begin() + size(), begin());
TypeOperations::UninitializedCopy(other.begin() + size(), other.end(), end());
size_ = other.size();
@@ -1609,7 +1625,8 @@ operator=(std::initializer_list<T> elements) {
DCHECK(begin());
}
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, input_size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ input_size);
TypeOperations::Copy(elements.begin(), elements.begin() + size_, begin());
TypeOperations::UninitializedCopy(elements.begin() + size_, elements.end(),
end());
@@ -1662,7 +1679,8 @@ Vector<T, inlineCapacity, Allocator>::Fill(const T& val, wtf_size_t new_size) {
DCHECK(begin());
}
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ new_size);
std::fill(begin(), end(), val);
TypeOperations::UninitializedFill(end(), begin() + new_size, val);
size_ = new_size;
@@ -1722,11 +1740,13 @@ inline void Vector<T, inlineCapacity, Allocator>::resize(wtf_size_t size) {
if (size <= size_) {
TypeOperations::Destruct(begin() + size, end());
ClearUnusedSlots(begin() + size, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size);
} else {
if (size > capacity())
ExpandCapacity(size);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size);
TypeOperations::Initialize(end(), begin() + size);
}
@@ -1738,7 +1758,8 @@ void Vector<T, inlineCapacity, Allocator>::Shrink(wtf_size_t size) {
DCHECK_LE(size, size_);
TypeOperations::Destruct(begin() + size, end());
ClearUnusedSlots(begin() + size, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size);
size_ = size;
}
@@ -1747,7 +1768,8 @@ void Vector<T, inlineCapacity, Allocator>::Grow(wtf_size_t size) {
DCHECK_GE(size, size_);
if (size > capacity())
ExpandCapacity(size);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size);
TypeOperations::Initialize(end(), begin() + size);
size_ = size;
}
@@ -1831,7 +1853,8 @@ template <typename U>
ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::push_back(U&& val) {
DCHECK(Allocator::IsAllocationAllowed());
if (LIKELY(size() != capacity())) {
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ + 1);
ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
end(), std::forward<U>(val));
++size_;
@@ -1849,7 +1872,8 @@ ALWAYS_INLINE T& Vector<T, inlineCapacity, Allocator>::emplace_back(
if (UNLIKELY(size() == capacity()))
ExpandCapacity(size() + 1);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ + 1);
T* t =
ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
end(), std::forward<Args>(args)...);
@@ -1869,7 +1893,8 @@ void Vector<T, inlineCapacity, Allocator>::Append(const U* data,
}
CHECK_GE(new_size, size_);
T* dest = end();
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ new_size);
VectorCopier<VectorTraits<T>::kCanCopyWithMemcpy, T,
Allocator>::UninitializedCopy(data, &data[data_size], dest);
size_ = new_size;
@@ -1884,7 +1909,8 @@ NOINLINE void Vector<T, inlineCapacity, Allocator>::AppendSlowCase(U&& val) {
ptr = ExpandCapacity(size() + 1, ptr);
DCHECK(begin());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ + 1);
ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
end(), std::forward<U>(*ptr));
++size_;
@@ -1933,7 +1959,8 @@ inline void Vector<T, inlineCapacity, Allocator>::insert(wtf_size_t position,
data = ExpandCapacity(size() + 1, data);
DCHECK(begin());
}
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ + 1);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ + 1);
T* spot = begin() + position;
TypeOperations::MoveOverlapping(spot, end(), spot + 1);
ConstructTraits<T, VectorTraits<T>, Allocator>::ConstructAndNotifyElement(
@@ -1954,7 +1981,8 @@ void Vector<T, inlineCapacity, Allocator>::insert(wtf_size_t position,
DCHECK(begin());
}
CHECK_GE(new_size, size_);
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, new_size);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ new_size);
T* spot = begin() + position;
TypeOperations::MoveOverlapping(spot, end(), spot + data_size);
VectorCopier<VectorTraits<T>::kCanCopyWithMemcpy, T,
@@ -2011,7 +2039,8 @@ inline void Vector<T, inlineCapacity, Allocator>::EraseAt(wtf_size_t position) {
spot->~T();
TypeOperations::MoveOverlapping(spot + 1, end(), spot);
ClearUnusedSlots(end() - 1, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ - 1);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ - 1);
--size_;
}
@@ -2046,7 +2075,8 @@ inline void Vector<T, inlineCapacity, Allocator>::EraseAt(wtf_size_t position,
TypeOperations::Destruct(begin_spot, end_spot);
TypeOperations::MoveOverlapping(end_spot, end(), begin_spot);
ClearUnusedSlots(end() - length, end());
- ANNOTATE_CHANGE_SIZE(begin(), capacity(), size_, size_ - length);
+ MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), capacity(), size_,
+ size_ - length);
size_ -= length;
}
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
index ef44f8f604e..e9f93acd773 100644
--- 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
@@ -52,7 +52,8 @@ class VectorBackedLinkedListNode {
default;
template <typename VisitorDispathcer, typename A = Allocator>
- std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispathcer visitor) {
+ std::enable_if_t<A::kIsGarbageCollected> Trace(
+ VisitorDispathcer visitor) const {
visitor->Trace(value_);
}
@@ -183,7 +184,8 @@ class VectorBackedLinkedList {
}
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 {
nodes_.Trace(visitor);
}