| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/render_frame_metadata_observer_impl.h" |
| |
| #include <cmath> |
| |
| #include "build/build_config.h" |
| #include "components/viz/common/quads/compositor_frame_metadata.h" |
| |
| namespace content { |
| |
| namespace { |
| #if defined(OS_ANDROID) |
| constexpr float kEdgeThreshold = 10.0f; |
| #endif |
| } |
| |
| RenderFrameMetadataObserverImpl::RenderFrameMetadataObserverImpl( |
| mojom::RenderFrameMetadataObserverRequest request, |
| mojom::RenderFrameMetadataObserverClientPtrInfo client_info) |
| : request_(std::move(request)), |
| client_info_(std::move(client_info)), |
| render_frame_metadata_observer_binding_(this) {} |
| |
| RenderFrameMetadataObserverImpl::~RenderFrameMetadataObserverImpl() {} |
| |
| void RenderFrameMetadataObserverImpl::BindToCurrentThread() { |
| DCHECK(request_.is_pending()); |
| render_frame_metadata_observer_binding_.Bind(std::move(request_)); |
| render_frame_metadata_observer_client_.Bind(std::move(client_info_)); |
| } |
| |
| void RenderFrameMetadataObserverImpl::OnRenderFrameSubmission( |
| const cc::RenderFrameMetadata& render_frame_metadata, |
| viz::CompositorFrameMetadata* compositor_frame_metadata, |
| bool force_send) { |
| // By default only report metadata changes for fields which have a low |
| // frequency of change. However if there are changes in high frequency |
| // fields these can be reported while testing is enabled. |
| bool send_metadata = false; |
| bool needs_activation_notification = true; |
| if (render_frame_metadata_observer_client_) { |
| if (report_all_frame_submissions_for_testing_enabled_) { |
| last_frame_token_ = compositor_frame_metadata->frame_token; |
| compositor_frame_metadata->send_frame_token_to_embedder = true; |
| render_frame_metadata_observer_client_->OnFrameSubmissionForTesting( |
| last_frame_token_); |
| send_metadata = !last_render_frame_metadata_ || |
| *last_render_frame_metadata_ != render_frame_metadata; |
| } else { |
| send_metadata = !last_render_frame_metadata_ || |
| ShouldSendRenderFrameMetadata( |
| *last_render_frame_metadata_, render_frame_metadata, |
| &needs_activation_notification); |
| } |
| send_metadata |= force_send; |
| } |
| |
| // Allways cache the full metadata, so that it can correctly be sent upon |
| // ReportAllFrameSubmissionsForTesting. This must only be done after we've |
| // compared the two for changes. |
| last_render_frame_metadata_ = render_frame_metadata; |
| |
| // If the metadata is different, updates all the observers; or the metadata is |
| // generated for first time and same as the default value, update the default |
| // value to all the observers. |
| if (send_metadata && render_frame_metadata_observer_client_) { |
| // Sending |root_scroll_offset| outside of tests would leave the browser |
| // process with out of date information. It is an optional parameter |
| // which we clear here. |
| auto metadata_copy = render_frame_metadata; |
| #if !defined(OS_ANDROID) |
| if (!report_all_frame_submissions_for_testing_enabled_) |
| metadata_copy.root_scroll_offset = base::nullopt; |
| #endif |
| |
| last_frame_token_ = compositor_frame_metadata->frame_token; |
| compositor_frame_metadata->send_frame_token_to_embedder = |
| needs_activation_notification; |
| render_frame_metadata_observer_client_->OnRenderFrameMetadataChanged( |
| needs_activation_notification ? last_frame_token_ : 0u, metadata_copy); |
| } |
| |
| // Always cache the initial frame token, so that if a test connects later on |
| // it can be notified of the initial state. |
| if (!last_frame_token_) { |
| last_frame_token_ = compositor_frame_metadata->frame_token; |
| compositor_frame_metadata->send_frame_token_to_embedder = |
| needs_activation_notification; |
| } |
| } |
| |
| void RenderFrameMetadataObserverImpl::ReportAllFrameSubmissionsForTesting( |
| bool enabled) { |
| report_all_frame_submissions_for_testing_enabled_ = enabled; |
| |
| if (!enabled || !last_frame_token_) |
| return; |
| |
| // When enabled for testing send the cached metadata. |
| DCHECK(render_frame_metadata_observer_client_); |
| DCHECK(last_render_frame_metadata_.has_value()); |
| render_frame_metadata_observer_client_->OnRenderFrameMetadataChanged( |
| last_frame_token_, *last_render_frame_metadata_); |
| } |
| |
| // static |
| bool RenderFrameMetadataObserverImpl::ShouldSendRenderFrameMetadata( |
| const cc::RenderFrameMetadata& rfm1, |
| const cc::RenderFrameMetadata& rfm2, |
| bool* needs_activation_notification) { |
| if (rfm1.root_background_color != rfm2.root_background_color || |
| rfm1.is_scroll_offset_at_top != rfm2.is_scroll_offset_at_top || |
| rfm1.selection != rfm2.selection || |
| rfm1.page_scale_factor != rfm2.page_scale_factor || |
| rfm1.external_page_scale_factor != rfm2.external_page_scale_factor || |
| rfm1.is_mobile_optimized != rfm2.is_mobile_optimized || |
| rfm1.device_scale_factor != rfm2.device_scale_factor || |
| rfm1.viewport_size_in_pixels != rfm2.viewport_size_in_pixels || |
| rfm1.top_controls_height != rfm2.top_controls_height || |
| rfm1.top_controls_shown_ratio != rfm2.top_controls_shown_ratio || |
| rfm1.local_surface_id_allocation != rfm2.local_surface_id_allocation) { |
| *needs_activation_notification = true; |
| return true; |
| } |
| |
| #if defined(OS_ANDROID) |
| if (rfm1.bottom_controls_height != rfm2.bottom_controls_height || |
| rfm1.bottom_controls_shown_ratio != rfm2.bottom_controls_shown_ratio || |
| rfm1.min_page_scale_factor != rfm2.min_page_scale_factor || |
| rfm1.max_page_scale_factor != rfm2.max_page_scale_factor || |
| rfm1.root_overflow_y_hidden != rfm2.root_overflow_y_hidden || |
| rfm1.scrollable_viewport_size != rfm2.scrollable_viewport_size || |
| rfm1.root_layer_size != rfm2.root_layer_size || |
| rfm1.has_transparent_background != rfm2.has_transparent_background) { |
| *needs_activation_notification = true; |
| return true; |
| } |
| |
| gfx::Vector2dF old_root_scroll_offset = |
| rfm1.root_scroll_offset.value_or(gfx::Vector2dF()); |
| gfx::Vector2dF new_root_scroll_offset = |
| rfm2.root_scroll_offset.value_or(gfx::Vector2dF()); |
| gfx::RectF old_viewport_rect( |
| gfx::PointF(old_root_scroll_offset.x(), old_root_scroll_offset.y()), |
| rfm1.scrollable_viewport_size); |
| gfx::RectF new_viewport_rect( |
| gfx::PointF(new_root_scroll_offset.x(), new_root_scroll_offset.y()), |
| rfm2.scrollable_viewport_size); |
| gfx::RectF new_root_layer_rect(rfm2.root_layer_size); |
| bool at_left_or_right_edge = |
| rfm2.root_layer_size.width() > rfm2.scrollable_viewport_size.width() && |
| (std::abs(new_viewport_rect.right() - new_root_layer_rect.right()) < |
| kEdgeThreshold || |
| std::abs(new_viewport_rect.x() - new_root_layer_rect.x()) < |
| kEdgeThreshold); |
| |
| bool at_top_or_bottom_edge = |
| rfm2.root_layer_size.height() > rfm2.scrollable_viewport_size.height() && |
| (std::abs(new_viewport_rect.y() - new_root_layer_rect.y()) < |
| kEdgeThreshold || |
| std::abs(new_viewport_rect.bottom() - new_root_layer_rect.bottom()) < |
| kEdgeThreshold); |
| |
| if (old_viewport_rect != new_viewport_rect && |
| (at_left_or_right_edge || at_top_or_bottom_edge)) { |
| *needs_activation_notification = false; |
| return true; |
| } |
| #endif |
| |
| *needs_activation_notification = false; |
| return false; |
| } |
| |
| } // namespace content |