#include "cc/trees/layer_tree_host_impl_test_base.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/test/fake_layer_tree_frame_sink.h"
#include "cc/trees/compositor_commit_data.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "media/base/media.h"
namespace cc {
using ScrollThread = InputHandler::ScrollThread;
TestFrameData::TestFrameData() {
begin_frame_ack = viz::BeginFrameAck::CreateManualAckWithDamage();
}
TestFrameData::~TestFrameData() = default;
std::unique_ptr<DidDrawCheckLayer> DidDrawCheckLayer::Create(
LayerTreeImpl* tree_impl,
int id) {
return base::WrapUnique(new DidDrawCheckLayer(tree_impl, id));
}
bool DidDrawCheckLayer::WillDraw(DrawMode draw_mode,
viz::ClientResourceProvider* provider) {
if (!LayerImpl::WillDraw(draw_mode, provider)) {
return false;
}
if (will_draw_returns_false_) {
return false;
}
will_draw_returned_true_ = true;
return true;
}
void DidDrawCheckLayer::AppendQuads(const AppendQuadsContext& context,
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) {
append_quads_called_ = true;
LayerImpl::AppendQuads(context, render_pass, append_quads_data);
}
void DidDrawCheckLayer::DidDraw(viz::ClientResourceProvider* provider) {
did_draw_called_ = true;
LayerImpl::DidDraw(provider);
}
void DidDrawCheckLayer::ClearDidDrawCheck() {
will_draw_returned_true_ = false;
append_quads_called_ = false;
did_draw_called_ = false;
}
DidDrawCheckLayer::DidDrawCheckLayer(LayerTreeImpl* tree_impl, int id)
: LayerImpl(tree_impl, id),
will_draw_returns_false_(false),
will_draw_returned_true_(false),
append_quads_called_(false),
did_draw_called_(false) {
SetBounds(gfx::Size(10, 10));
SetDrawsContent(true);
draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10);
}
LayerTreeHostImplTestBase::LayerTreeHostImplTestBase()
: task_runner_provider_(base::SingleThreadTaskRunner::GetCurrentDefault()),
always_main_thread_blocked_(&task_runner_provider_),
on_can_draw_state_changed_called_(false),
did_notify_ready_to_activate_(false),
did_request_commit_(false),
did_request_redraw_(false),
did_request_next_frame_(false),
did_request_prepare_tiles_(false),
did_prepare_tiles_(false),
did_complete_page_scale_animation_(false),
reduce_memory_result_(true),
did_request_impl_side_invalidation_(false) {
media::InitializeMediaLibrary();
}
LayerTreeHostImplTestBase::~LayerTreeHostImplTestBase() = default;
LayerTreeSettings LayerTreeHostImplTestBase::DefaultSettings() {
LayerTreeSettings settings = CommitToPendingTreeLayerListSettings();
settings.minimum_occlusion_tracking_size = gfx::Size();
settings.enable_smooth_scroll = true;
settings.single_thread_proxy_scheduler = false;
return settings;
}
LayerTreeSettings LayerTreeHostImplTestBase::LegacySWSettings() {
LayerTreeSettings settings = DefaultSettings();
settings.gpu_rasterization_disabled = true;
return settings;
}
void LayerTreeHostImplTestBase::SetUp() {
CreateHostImpl(DefaultSettings(), CreateLayerTreeFrameSink());
host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
}
void LayerTreeHostImplTestBase::TearDown() {
if (host_impl_) {
host_impl_->ReleaseLayerTreeFrameSink();
}
}
void LayerTreeHostImplTestBase::EnsureSyncTree() {
if (!host_impl_->CommitsToActiveTree() && !host_impl_->pending_tree()) {
CreatePendingTree();
}
}
void LayerTreeHostImplTestBase::CreatePendingTree() {
host_impl_->CreatePendingTree();
LayerTreeImpl* pending_tree = host_impl_->pending_tree();
pending_tree->SetDeviceViewportRect(
host_impl_->active_tree()->GetDeviceViewport());
pending_tree->SetDeviceScaleFactor(
host_impl_->active_tree()->device_scale_factor());
host_impl_->set_pending_tree_fully_painted_for_testing(true);
}
void LayerTreeHostImplTestBase::DidLoseLayerTreeFrameSinkOnImplThread() {}
void LayerTreeHostImplTestBase::SetBeginFrameSource(
viz::BeginFrameSource* source) {}
void LayerTreeHostImplTestBase::DidReceiveCompositorFrameAckOnImplThread() {}
void LayerTreeHostImplTestBase::OnCanDrawStateChanged(bool can_draw) {
on_can_draw_state_changed_called_ = true;
}
void LayerTreeHostImplTestBase::NotifyReadyToActivate() {
did_notify_ready_to_activate_ = true;
host_impl_->ActivateSyncTree();
}
bool LayerTreeHostImplTestBase::IsReadyToActivate() {
return false;
}
void LayerTreeHostImplTestBase::NotifyReadyToDraw() {}
void LayerTreeHostImplTestBase::SetNeedsRedrawOnImplThread() {
did_request_redraw_ = true;
}
void LayerTreeHostImplTestBase::SetNeedsOneBeginImplFrameOnImplThread() {
did_request_next_frame_ = true;
}
void LayerTreeHostImplTestBase::SetNeedsPrepareTilesOnImplThread() {
did_request_prepare_tiles_ = true;
}
void LayerTreeHostImplTestBase::SetNeedsCommitOnImplThread(bool urgent) {
did_request_commit_ = true;
}
void LayerTreeHostImplTestBase::SetVideoNeedsBeginFrames(
bool needs_begin_frames) {}
void LayerTreeHostImplTestBase::DidChangeBeginFrameSourcePaused(bool paused) {}
void LayerTreeHostImplTestBase::SetDeferBeginMainFrameFromImpl(
bool defer_begin_main_frame) {}
#if BUILDFLAG(ARKWEB_WEBGL)
void LayerTreeHostImplTestBase::SetDeferInvalidationForFastMainFrameFromImpl(
bool defer_invalidation_for_fast_main_frame) {}
#endif
bool LayerTreeHostImplTestBase::IsInsideDraw() {
return false;
}
void LayerTreeHostImplTestBase::RenewTreePriority() {}
void LayerTreeHostImplTestBase::PostDelayedAnimationTaskOnImplThread(
base::OnceClosure task,
base::TimeDelta delay) {
animation_task_ = std::move(task);
requested_animation_delay_ = delay;
}
void LayerTreeHostImplTestBase::DidActivateSyncTree() {
host_impl_->active_tree()->SetLocalSurfaceIdFromParent(
viz::LocalSurfaceId(1, base::UnguessableToken::CreateForTesting(2u, 3u)));
}
void LayerTreeHostImplTestBase::DidPrepareTiles() {
did_prepare_tiles_ = true;
}
void LayerTreeHostImplTestBase::DidCompletePageScaleAnimationOnImplThread() {
did_complete_page_scale_animation_ = true;
}
void LayerTreeHostImplTestBase::OnDrawForLayerTreeFrameSink(
bool resourceless_software_draw,
bool skip_draw) {
std::unique_ptr<TestFrameData> frame = std::make_unique<TestFrameData>();
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(frame.get()));
last_on_draw_render_passes_.clear();
viz::CompositorRenderPass::CopyAllForTest(frame->render_passes,
&last_on_draw_render_passes_);
host_impl_->DrawLayers(frame.get());
host_impl_->DidDrawAllLayers(*frame);
last_on_draw_frame_ = std::move(frame);
}
void LayerTreeHostImplTestBase::SetNeedsImplSideInvalidation(
bool needs_first_draw_on_activation) {
did_request_impl_side_invalidation_ = true;
}
void LayerTreeHostImplTestBase::NotifyImageDecodeRequestFinished(
int request_id,
bool speculative,
bool decode_succeeded) {}
void LayerTreeHostImplTestBase::DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
PresentationTimeCallbackBuffer::PendingCallbacks activated,
const viz::FrameTimingDetails& details) {
activated.main_callbacks.clear();
activated.main_successful_callbacks.clear();
host_impl_->NotifyDidPresentCompositorFrameOnImplThread(
frame_token, std::move(activated.compositor_successful_callbacks),
details);
}
void LayerTreeHostImplTestBase::NotifyAnimationWorkletStateChange(
AnimationWorkletMutationState state,
ElementListType tree_type) {}
void LayerTreeHostImplTestBase::NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) {}
void LayerTreeHostImplTestBase::NotifyCompositorMetricsTrackerResults(
CustomTrackerResults results) {}
void LayerTreeHostImplTestBase::DidObserveFirstScrollDelay(
int source_frame_number,
base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp) {
first_scroll_observed++;
}
bool LayerTreeHostImplTestBase::IsInSynchronousComposite() const {
return false;
}
void LayerTreeHostImplTestBase::FrameSinksToThrottleUpdated(
const base::flat_set<viz::FrameSinkId>& ids) {}
void LayerTreeHostImplTestBase::ClearHistory() {}
void LayerTreeHostImplTestBase::SetHasActiveThreadedScroll(bool is_scrolling) {}
void LayerTreeHostImplTestBase::SetWaitingForScrollEvent(
bool waiting_for_scroll_event) {}
size_t LayerTreeHostImplTestBase::CommitDurationSampleCountForTesting() const {
return 0;
}
void LayerTreeHostImplTestBase::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) {}
AnimationHost* LayerTreeHostImplTestBase::GetImplAnimationHost() const {
return static_cast<AnimationHost*>(host_impl_->mutator_host());
}
bool LayerTreeHostImplTestBase::CreateHostImpl(
const LayerTreeSettings& settings,
std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink) {
if (host_impl_) {
host_impl_->ReleaseLayerTreeFrameSink();
}
host_impl_.reset();
InitializeImageWorker(settings);
host_impl_ = LayerTreeHostImpl::Create(
settings, this, &task_runner_provider_, &stats_instrumentation_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::kImpl), nullptr, 0,
image_worker_ ? image_worker_->task_runner() : nullptr, nullptr);
InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl_));
layer_tree_frame_sink_ = std::move(layer_tree_frame_sink);
host_impl_->SetVisible(true);
bool init = host_impl_->InitializeFrameSink(layer_tree_frame_sink_.get());
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(10, 10));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 1);
host_impl_->active_tree()->SetLocalSurfaceIdFromParent(
viz::LocalSurfaceId(1, base::UnguessableToken::CreateForTesting(2u, 3u)));
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks() + base::Milliseconds(1));
host_impl_->WillBeginImplFrame(args);
host_impl_->DidFinishImplFrame(args);
timeline_ = AnimationTimeline::Create(AnimationIdProvider::NextTimelineId());
GetImplAnimationHost()->AddAnimationTimeline(timeline_);
return init;
}
LayerImpl* LayerTreeHostImplTestBase::SetupDefaultRootLayer(
const gfx::Size& viewport_size) {
return SetupRootLayer<LayerImpl>(host_impl_->active_tree(), viewport_size);
}
LayerImpl* LayerTreeHostImplTestBase::root_layer() {
return host_impl_->active_tree()->root_layer();
}
gfx::Size LayerTreeHostImplTestBase::DipSizeToPixelSize(const gfx::Size& size) {
return gfx::ScaleToRoundedSize(
size, host_impl_->active_tree()->device_scale_factor());
}
void LayerTreeHostImplTestBase::PushScrollOffsetsToPendingTree(
const base::flat_map<ElementId, gfx::PointF>& offsets) {
PropertyTrees property_trees(*host_impl_);
auto& scroll_tree =
host_impl_->active_tree()->property_trees()->scroll_tree_mutable();
if (auto* layer = InnerViewportScrollLayer()) {
property_trees.scroll_tree_mutable().SetBaseScrollOffset(
layer->element_id(),
scroll_tree.current_scroll_offset(layer->element_id()));
}
if (auto* layer = OuterViewportScrollLayer()) {
property_trees.scroll_tree_mutable().SetBaseScrollOffset(
layer->element_id(),
scroll_tree.current_scroll_offset(layer->element_id()));
}
for (auto& entry : offsets) {
property_trees.scroll_tree_mutable().SetBaseScrollOffset(entry.first,
entry.second);
}
host_impl_->sync_tree()
->property_trees()
->scroll_tree_mutable()
.PushScrollUpdatesFromMainThread(
property_trees, host_impl_->sync_tree(),
host_impl_->settings().commit_fractional_scroll_deltas);
}
void LayerTreeHostImplTestBase::ClearNonScrollSyncTreeDeltasForTesting() {
auto* tree = host_impl_->sync_tree();
tree->page_scale_factor()->AbortCommit(
false, false);
tree->top_controls_shown_ratio()->AbortCommit(
false, false);
tree->bottom_controls_shown_ratio()->AbortCommit(
false, false);
}
void LayerTreeHostImplTestBase::ExpectClearedScrollDeltasRecursive(
LayerImpl* root) {
for (auto* layer : *root->layer_tree_impl()) {
ASSERT_EQ(ScrollDelta(layer), gfx::Vector2d());
}
}
::testing::AssertionResult LayerTreeHostImplTestBase::ScrollInfoContains(
const CompositorCommitData& commit_data,
ElementId id,
const gfx::Vector2dF& scroll_delta) {
int times_encountered = 0;
for (size_t i = 0; i < commit_data.scrolls.size(); ++i) {
if (commit_data.scrolls[i].element_id != id) {
continue;
}
if (scroll_delta != commit_data.scrolls[i].scroll_delta) {
return ::testing::AssertionFailure()
<< "Expected " << scroll_delta.ToString() << ", not "
<< commit_data.scrolls[i].scroll_delta.ToString();
}
times_encountered++;
}
if (id == commit_data.inner_viewport_scroll.element_id) {
if (scroll_delta != commit_data.inner_viewport_scroll.scroll_delta) {
return ::testing::AssertionFailure()
<< "Expected " << scroll_delta.ToString() << ", not "
<< commit_data.inner_viewport_scroll.scroll_delta.ToString();
}
times_encountered++;
}
if (times_encountered != 1) {
return ::testing::AssertionFailure() << "No scroll found with id " << id;
}
return ::testing::AssertionSuccess();
}
void LayerTreeHostImplTestBase::ExpectNone(
const CompositorCommitData& commit_data,
ElementId id) {
int times_encountered = 0;
for (size_t i = 0; i < commit_data.scrolls.size(); ++i) {
if (commit_data.scrolls[i].element_id != id) {
continue;
}
times_encountered++;
}
ASSERT_EQ(0, times_encountered);
}
LayerImpl* LayerTreeHostImplTestBase::AddLayerInActiveTree() {
return AddLayer<LayerImpl>(host_impl_->active_tree());
}
void LayerTreeHostImplTestBase::SetupViewportLayers(
LayerTreeImpl* layer_tree_impl,
const gfx::Size& inner_viewport_size,
const gfx::Size& outer_viewport_size,
const gfx::Size& content_size) {
DCHECK(!layer_tree_impl->root_layer());
auto* root = SetupRootLayer<LayerImpl>(layer_tree_impl, inner_viewport_size);
SetupViewport(root, outer_viewport_size, content_size);
host_impl_->SetVisualDeviceViewportSize(inner_viewport_size);
UpdateDrawProperties(layer_tree_impl);
layer_tree_impl->DidBecomeActive();
}
void LayerTreeHostImplTestBase::SetupViewportLayersInnerScrolls(
const gfx::Size& inner_viewport_size,
const gfx::Size& content_size) {
const auto& outer_viewport_size = content_size;
SetupViewportLayers(host_impl_->active_tree(), inner_viewport_size,
outer_viewport_size, content_size);
}
void LayerTreeHostImplTestBase::SetupViewportLayersOuterScrolls(
const gfx::Size& viewport_size,
const gfx::Size& content_size) {
SetupViewportLayers(host_impl_->active_tree(), viewport_size, viewport_size,
content_size);
}
LayerImpl* LayerTreeHostImplTestBase::AddContentLayer() {
LayerImpl* scroll_layer = OuterViewportScrollLayer();
DCHECK(scroll_layer);
LayerImpl* layer = AddLayerInActiveTree();
layer->SetBounds(scroll_layer->bounds());
layer->SetDrawsContent(true);
CopyProperties(scroll_layer, layer);
return layer;
}
void LayerTreeHostImplTestBase::SetupViewportLayersNoScrolls(
const gfx::Size& bounds) {
SetupViewportLayers(host_impl_->active_tree(), bounds, bounds, bounds);
}
void LayerTreeHostImplTestBase::CreateAndTestNonScrollableLayers(
bool transparent_layer) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size content_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(345, 3800);
gfx::Size scrollbar_size = gfx::Size(15, 600);
SetupViewportLayersNoScrolls(content_size);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
LayerImpl* scroll =
AddScrollableLayer(outer_scroll, content_size, scroll_content_size);
auto* squash2 = AddLayer<LayerImpl>(layer_tree_impl);
squash2->SetBounds(gfx::Size(140, 300));
squash2->SetDrawsContent(true);
squash2->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(scroll, squash2);
squash2->SetOffsetToTransformParent(gfx::Vector2dF(220, 300));
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
layer_tree_impl, ScrollbarOrientation::kVertical, false, true);
SetupScrollbarLayer(scroll, scrollbar);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
auto* squash1 = AddLayer<LayerImpl>(layer_tree_impl);
squash1->SetBounds(gfx::Size(140, 300));
CopyProperties(outer_scroll, squash1);
squash1->SetOffsetToTransformParent(gfx::Vector2dF(220, 0));
if (transparent_layer) {
CreateEffectNode(squash1).opacity = 0.0f;
squash1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
} else {
squash1->SetDrawsContent(true);
squash1->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
}
UpdateDrawProperties(layer_tree_impl);
layer_tree_impl->DidBecomeActive();
InputHandler::ScrollStatus status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(230, 150), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(350, 150), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_hit_test_reasons);
status = GetInputHandler().ScrollBegin(
BeginState(gfx::Point(230, 450), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
ASSERT_EQ(ScrollThread::kScrollOnImplThread, status.thread);
}
LayerImpl* LayerTreeHostImplTestBase::AddScrollableLayer(
LayerImpl* container,
const gfx::Size& scroll_container_bounds,
const gfx::Size& content_size) {
LayerImpl* layer = AddLayer<LayerImpl>(container->layer_tree_impl());
layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
layer->SetDrawsContent(true);
layer->SetBounds(content_size);
layer->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CopyProperties(container, layer);
CreateTransformNode(layer);
CreateScrollNode(layer, scroll_container_bounds);
return layer;
}
void LayerTreeHostImplTestBase::SetupScrollbarLayerCommon(
LayerImpl* scroll_layer,
ScrollbarLayerImplBase* scrollbar) {
auto* tree_impl = scroll_layer->layer_tree_impl();
scrollbar->SetScrollElementId(scroll_layer->element_id());
scrollbar->SetDrawsContent(true);
CopyProperties(scroll_layer, scrollbar);
if (scroll_layer == tree_impl->OuterViewportScrollLayerForTesting()) {
scrollbar->SetTransformTreeIndex(tree_impl->PageScaleTransformNode()->id);
scrollbar->SetScrollTreeIndex(tree_impl->root_layer()->scroll_tree_index());
} else {
scrollbar->SetTransformTreeIndex(GetTransformNode(scroll_layer)->parent_id);
scrollbar->SetScrollTreeIndex(GetScrollNode(scroll_layer)->parent_id);
}
}
void LayerTreeHostImplTestBase::SetupScrollbarLayer(
LayerImpl* scroll_layer,
SolidColorScrollbarLayerImpl* scrollbar) {
scrollbar->SetElementId(LayerIdToElementIdForTesting(scrollbar->id()));
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
auto& effect = CreateEffectNode(scrollbar);
effect.opacity = 0;
effect.has_potential_opacity_animation = true;
}
void LayerTreeHostImplTestBase::SetupScrollbarLayer(
LayerImpl* scroll_layer,
PaintedScrollbarLayerImpl* scrollbar) {
SetupScrollbarLayerCommon(scroll_layer, scrollbar);
scrollbar->SetHitTestOpaqueness(HitTestOpaqueness::kMixed);
CreateEffectNode(scrollbar).opacity = 1;
}
LayerImpl* LayerTreeHostImplTestBase::InnerViewportScrollLayer() {
return host_impl_->active_tree()->InnerViewportScrollLayerForTesting();
}
LayerImpl* LayerTreeHostImplTestBase::OuterViewportScrollLayer() {
return host_impl_->active_tree()->OuterViewportScrollLayerForTesting();
}
std::unique_ptr<ScrollState> LayerTreeHostImplTestBase::BeginState(
const gfx::Point& point,
const gfx::Vector2dF& delta_hint,
ui::ScrollInputType type) {
ScrollStateData scroll_state_data;
scroll_state_data.is_beginning = true;
scroll_state_data.position_x = point.x();
scroll_state_data.position_y = point.y();
scroll_state_data.delta_x_hint = delta_hint.x();
scroll_state_data.delta_y_hint = delta_hint.y();
scroll_state_data.is_direct_manipulation =
type == ui::ScrollInputType::kTouchscreen;
scroll_state_data.is_scrollbar_interaction =
type == ui::ScrollInputType::kScrollbar;
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
return scroll_state;
}
ScrollState LayerTreeHostImplTestBase::UpdateState(const gfx::Point& point,
const gfx::Vector2dF& delta,
ui::ScrollInputType type) {
ScrollStateData scroll_state_data;
scroll_state_data.delta_x = delta.x();
scroll_state_data.delta_y = delta.y();
scroll_state_data.position_x = point.x();
scroll_state_data.position_y = point.y();
scroll_state_data.is_direct_manipulation =
type == ui::ScrollInputType::kTouchscreen;
scroll_state_data.is_scrollbar_interaction =
type == ui::ScrollInputType::kScrollbar;
return ScrollState(scroll_state_data);
}
ScrollState LayerTreeHostImplTestBase::AnimatedUpdateState(
const gfx::Point& point,
const gfx::Vector2dF& delta) {
auto state = UpdateState(point, delta, ui::ScrollInputType::kWheel);
state.data()->delta_granularity = ui::ScrollGranularity::kScrollByPixel;
return state;
}
void LayerTreeHostImplTestBase::DrawFrame() {
auto args = viz::CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, viz::BeginFrameArgs::kManualSourceId, 1,
base::TimeTicks() + base::Milliseconds(1));
DrawFrameWithArgs(args);
}
void LayerTreeHostImplTestBase::DrawFrameWithArgs(
const viz::BeginFrameArgs& args) {
PrepareForUpdateDrawProperties(host_impl_->active_tree());
TestFrameData frame;
host_impl_->WillBeginImplFrame(args);
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(args);
}
RenderFrameMetadata
LayerTreeHostImplTestBase::StartDrawAndProduceRenderFrameMetadata() {
TestFrameData frame;
EXPECT_EQ(DrawResult::kSuccess, host_impl_->PrepareToDraw(&frame));
return host_impl_->MakeRenderFrameMetadata(&frame);
}
void LayerTreeHostImplTestBase::AllowedTouchActionTestHelper(
float device_scale_factor,
float page_scale_factor) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
float min_page_scale_factor = 0.1f;
float max_page_scale_factor = 5.0f;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale_factor, max_page_scale_factor);
host_impl_->active_tree()->SetDeviceScaleFactor(device_scale_factor);
LayerImpl* child = AddLayerInActiveTree();
child->SetDrawsContent(true);
child->SetBounds(gfx::Size(25, 25));
CopyProperties(InnerViewportScrollLayer(), child);
TouchActionRegion root_touch_action_region;
root_touch_action_region.Union(TouchAction::kPanX, gfx::Rect(0, 0, 50, 50));
root_layer()->SetTouchActionRegion(root_touch_action_region);
TouchActionRegion child_touch_action_region;
child_touch_action_region.Union(TouchAction::kPanLeft,
gfx::Rect(0, 0, 25, 25));
child->SetTouchActionRegion(child_touch_action_region);
TouchAction touch_action = TouchAction::kAuto;
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 10), gfx::Size()), &touch_action);
EXPECT_EQ(TouchAction::kPanLeft, touch_action);
touch_action = TouchAction::kAuto;
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(30, 30), gfx::Size()), &touch_action);
EXPECT_EQ(TouchAction::kPanX, touch_action);
TouchActionRegion new_child_region;
new_child_region.Union(TouchAction::kPanY, gfx::Rect(0, 0, 25, 25));
child->SetTouchActionRegion(new_child_region);
touch_action = TouchAction::kAuto;
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(10, 10), gfx::Size()), &touch_action);
EXPECT_EQ(TouchAction::kPanY, touch_action);
touch_action = TouchAction::kAuto;
GetInputHandler().EventListenerTypeForTouchStartOrMoveAt(
gfx::Rect(gfx::Point(30, 30), gfx::Size()), &touch_action);
EXPECT_EQ(TouchAction::kPanX, touch_action);
}
LayerImpl* LayerTreeHostImplTestBase::CreateLayerForSnapping() {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
gfx::Size overflow_size(400, 400);
LayerImpl* overflow = AddScrollableLayer(OuterViewportScrollLayer(),
gfx::Size(100, 100), overflow_size);
SnapContainerData container_data(
ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
gfx::RectF(0, 0, 200, 200), gfx::PointF(300, 300));
SnapAreaData area_data(ScrollSnapAlign(SnapAlignment::kStart),
gfx::RectF(50, 50, 100, 100), false, false,
ElementId(10));
container_data.AddSnapAreaData(area_data);
GetScrollNode(overflow)->snap_container_data.emplace(container_data);
DrawFrame();
return overflow;
}
std::optional<SnapContainerData>
LayerTreeHostImplTestBase::GetSnapContainerData(LayerImpl* layer) {
return GetScrollNode(layer) ? GetScrollNode(layer)->snap_container_data
: std::nullopt;
}
void LayerTreeHostImplTestBase::ClearLayersAndPropertyTrees(
LayerTreeImpl* layer_tree_impl) {
layer_tree_impl->SetRootLayerForTesting(nullptr);
layer_tree_impl->DetachLayers();
layer_tree_impl->property_trees()->clear();
layer_tree_impl->SetViewportPropertyIds(ViewportPropertyIds());
}
std::unique_ptr<LayerTreeFrameSink>
LayerTreeHostImplTestBase::CreateLayerTreeFrameSink() {
return FakeLayerTreeFrameSink::Create3dForGpuRasterization();
}
void LayerTreeHostImplTestBase::DrawOneFrame() {
PrepareForUpdateDrawProperties(host_impl_->active_tree());
TestFrameData frame_data;
host_impl_->PrepareToDraw(&frame_data);
host_impl_->DidDrawAllLayers(frame_data);
}
void LayerTreeHostImplTestBase::SetScrollOffsetDelta(
LayerImpl* layer_impl,
const gfx::Vector2dF& delta) {
if (layer_impl->layer_tree_impl()
->property_trees()
->scroll_tree_mutable()
.SetScrollOffsetDeltaForTesting(layer_impl->element_id(), delta)) {
layer_impl->layer_tree_impl()->DidUpdateScrollOffset(
layer_impl->element_id(), false);
}
}
void LayerTreeHostImplTestBase::BeginImplFrameAndAnimate(
viz::BeginFrameArgs begin_frame_args,
base::TimeTicks frame_time) {
begin_frame_args.frame_time = frame_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
}
void LayerTreeHostImplTestBase::InitializeImageWorker(
const LayerTreeSettings& settings) {
if (settings.enable_checker_imaging) {
image_worker_ = std::make_unique<base::Thread>("ImageWorker");
ASSERT_TRUE(image_worker_->Start());
} else {
image_worker_.reset();
}
}
InputHandler& LayerTreeHostImplTestBase::GetInputHandler() {
return host_impl_->GetInputHandler();
}
void LayerTreeHostImplTestBase::SetupMouseMoveAtWithDeviceScale(
float device_scale_factor) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
const int thumb_thickness = 15;
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
gfx::Size scrollbar_size(gfx::Size(thumb_thickness, viewport_size.height()));
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetDeviceScaleFactor(device_scale_factor);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* root_scroll = OuterViewportScrollLayer();
auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical,
thumb_thickness, 0, true);
SetupScrollbarLayer(root_scroll, scrollbar);
scrollbar->SetBounds(scrollbar_size);
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kNone, gfx::Rect(scrollbar_size));
scrollbar->SetTouchActionRegion(touch_action_region);
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
ScrollbarAnimationController* scrollbar_animation_controller =
host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id());
const float kMouseMoveDistanceToTriggerFadeIn =
scrollbar_animation_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerFadeIn();
const float kMouseMoveDistanceToTriggerExpand =
scrollbar_animation_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerExpand();
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 1));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 10));
EXPECT_TRUE(scrollbar_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_TRUE(scrollbar_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 100));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
did_request_redraw_ = false;
EXPECT_FALSE(scrollbar_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(gfx::Point(10, 10));
EXPECT_TRUE(scrollbar_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(gfx::Point(10, 0));
EXPECT_TRUE(scrollbar_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(gfx::Point(150, 120));
EXPECT_FALSE(scrollbar_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
}
void LayerTreeHostImplTestBase::SetupMouseMoveAtTestScrollbarStates(
bool main_thread_scrolling) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::Milliseconds(500);
settings.scrollbar_fade_duration = base::Milliseconds(300);
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
const int thumb_thickness = 15;
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
gfx::Size child_layer_size(250, 150);
gfx::Size scrollbar_size_1(
gfx::Size(thumb_thickness, viewport_size.height()));
gfx::Size scrollbar_size_2(
gfx::Size(thumb_thickness, child_layer_size.height()));
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->SetDeviceScaleFactor(1);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
LayerImpl* root_scroll = OuterViewportScrollLayer();
if (main_thread_scrolling) {
GetScrollNode(root_scroll)->main_thread_repaint_reasons =
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
}
auto* scrollbar_1 = AddLayer<SolidColorScrollbarLayerImpl>(
host_impl_->active_tree(), ScrollbarOrientation::kVertical,
thumb_thickness, 0, true);
SetupScrollbarLayer(root_scroll, scrollbar_1);
scrollbar_1->SetBounds(scrollbar_size_1);
TouchActionRegion touch_action_region;
touch_action_region.Union(TouchAction::kNone, gfx::Rect(scrollbar_size_1));
scrollbar_1->SetTouchActionRegion(touch_action_region);
host_impl_->active_tree()->UpdateAllScrollbarGeometriesForTesting();
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
ScrollbarAnimationController* scrollbar_1_animation_controller =
host_impl_->ScrollbarAnimationControllerForElementId(
root_scroll->element_id());
EXPECT_TRUE(scrollbar_1_animation_controller);
const float kMouseMoveDistanceToTriggerFadeIn =
scrollbar_1_animation_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerFadeIn();
const float kMouseMoveDistanceToTriggerExpand =
scrollbar_1_animation_controller
->GetScrollbarAnimationController(ScrollbarOrientation::kVertical)
.MouseMoveDistanceToTriggerExpand();
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerFadeIn + 1, 0));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand + 1, 0));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 0));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(gfx::Point(10, 0));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
GetInputHandler().MouseMoveAt(
gfx::Point(thumb_thickness + kMouseMoveDistanceToTriggerExpand, 0));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbar(
ScrollbarOrientation::kVertical));
EXPECT_TRUE(scrollbar_1_animation_controller->MouseIsNearScrollbarThumb(
ScrollbarOrientation::kVertical));
EXPECT_FALSE(scrollbar_1_animation_controller->MouseIsOverScrollbarThumb(
ScrollbarOrientation::kVertical));
}
LayerTreeHostImplTest::LayerTreeHostImplTest() {
const auto test_mode = GetParam();
switch (test_mode) {
case CommitToActiveTreeTreesInVizClient:
case CommitToPendingTreeTreesInVizClient:
case CommitToActiveTreeTreesInVizService:
scoped_feature_list_.InitAndEnableFeature(features::kTreesInViz);
break;
case CommitToActiveTree:
case CommitToPendingTree:
scoped_feature_list_.InitAndDisableFeature(features::kTreesInViz);
break;
}
}
LayerTreeHostImplTest::~LayerTreeHostImplTest() = default;
bool LayerTreeHostImplTest::CommitsToActiveTree() {
const auto test_mode = GetParam();
switch (test_mode) {
case CommitToActiveTree:
case CommitToActiveTreeTreesInVizClient:
case CommitToActiveTreeTreesInVizService:
return true;
case CommitToPendingTree:
case CommitToPendingTreeTreesInVizClient:
return false;
}
}
LayerTreeSettings LayerTreeHostImplTest::DefaultSettings() {
const auto test_mode = GetParam();
LayerTreeSettings settings = LayerTreeHostImplTestBase::DefaultSettings();
settings.commit_to_active_tree = CommitsToActiveTree();
settings.trees_in_viz_in_viz_process =
(test_mode == CommitToActiveTreeTreesInVizService);
return settings;
}
}