diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform')
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, ©_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>(®istry); - - // 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>(®istry); - 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), ¬ifier); + + 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), ¬ifier); + + 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), ¬ifier); + + 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), ¬ifier); + + 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 Binary files differnew file mode 100644 index 00000000000..9ca796d0d9f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/testing/data/red-full-ranged-8bpc.avif 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( + ¶ms->edit_context_control_bounds, + ¶ms->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); } |