blob: 43e5e4db671c8759915cac5b61de9860af6b026c [file] [log] [blame]
// Copyright 2011 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 "cc/trees/layer_tree_host_impl.h"
#include <stddef.h>
#include <algorithm>
#include <cmath>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cc/animation/animation_events.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/transform_operations.h"
#include "cc/base/math_util.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/page_scale_animation.h"
#include "cc/input/scrollbar_animation_controller_thinning.h"
#include "cc/input/top_controls_manager.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/layers/solid_color_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/layers/video_layer_impl.h"
#include "cc/layers/viewport.h"
#include "cc/output/begin_frame_args.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/compositor_frame_metadata.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/output/gl_renderer.h"
#include "cc/output/latency_info_swap_promise.h"
#include "cc/quads/render_pass_draw_quad.h"
#include "cc/quads/solid_color_draw_quad.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/quads/tile_draw_quad.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/begin_frame_args_test.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_mask_layer_impl.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_layer_impl.h"
#include "cc/test/fake_raster_source.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/gpu_rasterization_enabled_settings.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/test_gpu_memory_buffer_manager.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
#include "media/base/media.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkMallocPixelRef.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#define EXPECT_SCOPED(statements) \
{ \
SCOPED_TRACE(""); \
statements; \
}
using ::testing::Mock;
using ::testing::Return;
using ::testing::AnyNumber;
using ::testing::AtLeast;
using ::testing::_;
using media::VideoFrame;
namespace cc {
namespace {
class LayerTreeHostImplTest : public testing::Test,
public LayerTreeHostImplClient {
public:
LayerTreeHostImplTest()
: task_runner_provider_(base::ThreadTaskRunnerHandle::Get()),
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_complete_page_scale_animation_(false),
reduce_memory_result_(true),
skip_draw_layers_in_on_draw_(false) {
media::InitializeMediaLibrary();
}
LayerTreeSettings DefaultSettings() {
LayerTreeSettings settings;
settings.minimum_occlusion_tracking_size = gfx::Size();
settings.renderer_settings.texture_id_allocation_chunk_size = 1;
settings.gpu_rasterization_enabled = true;
settings.verify_clip_tree_calculations = true;
settings.verify_transform_tree_calculations = true;
return settings;
}
void SetUp() override {
CreateHostImpl(DefaultSettings(), CreateOutputSurface());
}
void TearDown() override {
if (host_impl_)
host_impl_->ReleaseOutputSurface();
}
void UpdateRendererCapabilitiesOnImplThread() override {}
void DidLoseOutputSurfaceOnImplThread() override {}
void CommitVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override {}
void SetBeginFrameSource(BeginFrameSource* source) override {}
void SetEstimatedParentDrawTime(base::TimeDelta draw_time) override {}
void DidSwapBuffersOnImplThread() override {}
void DidSwapBuffersCompleteOnImplThread() override {}
void OnCanDrawStateChanged(bool can_draw) override {
on_can_draw_state_changed_called_ = true;
}
void NotifyReadyToActivate() override {
did_notify_ready_to_activate_ = true;
host_impl_->ActivateSyncTree();
}
void NotifyReadyToDraw() override {}
void SetNeedsRedrawOnImplThread() override { did_request_redraw_ = true; }
void SetNeedsRedrawRectOnImplThread(const gfx::Rect& damage_rect) override {
did_request_redraw_ = true;
}
void SetNeedsOneBeginImplFrameOnImplThread() override {
did_request_next_frame_ = true;
}
void SetNeedsPrepareTilesOnImplThread() override {
did_request_prepare_tiles_ = true;
}
void SetNeedsCommitOnImplThread() override { did_request_commit_ = true; }
void SetVideoNeedsBeginFrames(bool needs_begin_frames) override {}
void PostAnimationEventsToMainThreadOnImplThread(
std::unique_ptr<AnimationEvents> events) override {}
bool IsInsideDraw() override { return false; }
void RenewTreePriority() override {}
void PostDelayedAnimationTaskOnImplThread(const base::Closure& task,
base::TimeDelta delay) override {
animation_task_ = task;
requested_animation_delay_ = delay;
}
void DidActivateSyncTree() override {}
void WillPrepareTiles() override {}
void DidPrepareTiles() override {}
void DidCompletePageScaleAnimationOnImplThread() override {
did_complete_page_scale_animation_ = true;
}
void OnDrawForOutputSurface(bool resourceless_software_draw) override {
std::unique_ptr<LayerTreeHostImpl::FrameData> frame(
new LayerTreeHostImpl::FrameData);
EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(frame.get()));
last_on_draw_render_passes_.clear();
RenderPass::CopyAll(frame->render_passes, &last_on_draw_render_passes_);
if (!skip_draw_layers_in_on_draw_)
host_impl_->DrawLayers(frame.get());
host_impl_->DidDrawAllLayers(*frame);
host_impl_->SwapBuffers(*frame);
last_on_draw_frame_ = std::move(frame);
}
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
}
virtual bool CreateHostImpl(const LayerTreeSettings& settings,
std::unique_ptr<OutputSurface> output_surface) {
return CreateHostImplWithTaskRunnerProvider(
settings, std::move(output_surface), &task_runner_provider_);
}
virtual bool CreateHostImplWithTaskRunnerProvider(
const LayerTreeSettings& settings,
std::unique_ptr<OutputSurface> output_surface,
TaskRunnerProvider* task_runner_provider) {
if (host_impl_)
host_impl_->ReleaseOutputSurface();
host_impl_ = LayerTreeHostImpl::Create(
settings, this, task_runner_provider, &stats_instrumentation_,
&shared_bitmap_manager_, &gpu_memory_buffer_manager_,
&task_graph_runner_,
AnimationHost::CreateForTesting(ThreadInstance::IMPL), 0);
output_surface_ = std::move(output_surface);
host_impl_->SetVisible(true);
bool init = host_impl_->InitializeRenderer(output_surface_.get());
host_impl_->SetViewportSize(gfx::Size(10, 10));
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
// Set the BeginFrameArgs so that methods which use it are able to.
host_impl_->WillBeginImplFrame(CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE,
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1)));
host_impl_->DidFinishImplFrame();
timeline_ =
AnimationTimeline::Create(AnimationIdProvider::NextTimelineId());
host_impl_->animation_host()->AddAnimationTimeline(timeline_);
return init;
}
void SetupRootLayerImpl(std::unique_ptr<LayerImpl> root) {
root->SetPosition(gfx::PointF());
root->SetBounds(gfx::Size(10, 10));
root->SetDrawsContent(true);
root->draw_properties().visible_layer_rect = gfx::Rect(0, 0, 10, 10);
root->test_properties()->force_render_surface = true;
host_impl_->active_tree()->SetRootLayerForTesting(std::move(root));
}
static gfx::Vector2dF ScrollDelta(LayerImpl* layer_impl) {
gfx::ScrollOffset delta =
layer_impl->layer_tree_impl()
->property_trees()
->scroll_tree.GetScrollOffsetDeltaForTesting(layer_impl->id());
return gfx::Vector2dF(delta.x(), delta.y());
}
static void ExpectClearedScrollDeltasRecursive(LayerImpl* root) {
for (auto* layer : *root->layer_tree_impl())
ASSERT_EQ(ScrollDelta(layer), gfx::Vector2d());
}
static ::testing::AssertionResult ScrollInfoContains(
const ScrollAndScaleSet& scroll_info,
int id,
const gfx::Vector2d& scroll_delta) {
int times_encountered = 0;
for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) {
if (scroll_info.scrolls[i].layer_id != id)
continue;
if (scroll_delta != scroll_info.scrolls[i].scroll_delta) {
return ::testing::AssertionFailure()
<< "Expected " << scroll_delta.ToString() << ", not "
<< scroll_info.scrolls[i].scroll_delta.ToString();
}
times_encountered++;
}
if (times_encountered != 1)
return ::testing::AssertionFailure() << "No layer found with id " << id;
return ::testing::AssertionSuccess();
}
static void ExpectNone(const ScrollAndScaleSet& scroll_info, int id) {
int times_encountered = 0;
for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) {
if (scroll_info.scrolls[i].layer_id != id)
continue;
times_encountered++;
}
ASSERT_EQ(0, times_encountered);
}
LayerImpl* CreateScrollAndContentsLayers(LayerTreeImpl* layer_tree_impl,
const gfx::Size& content_size) {
// Create both an inner viewport scroll layer and an outer viewport scroll
// layer. The MaxScrollOffset of the outer viewport scroll layer will be
// 0x0, so the scrolls will be applied directly to the inner viewport.
const int kOuterViewportClipLayerId = 116;
const int kOuterViewportScrollLayerId = 117;
const int kContentLayerId = 118;
const int kInnerViewportScrollLayerId = 2;
const int kInnerViewportClipLayerId = 4;
const int kPageScaleLayerId = 5;
std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1);
root->SetBounds(content_size);
root->SetPosition(gfx::PointF());
root->test_properties()->force_render_surface = true;
std::unique_ptr<LayerImpl> inner_scroll =
LayerImpl::Create(layer_tree_impl, kInnerViewportScrollLayerId);
inner_scroll->test_properties()->is_container_for_fixed_position_layers =
true;
inner_scroll->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(inner_scroll->id(),
gfx::ScrollOffset());
std::unique_ptr<LayerImpl> inner_clip =
LayerImpl::Create(layer_tree_impl, kInnerViewportClipLayerId);
inner_clip->SetBounds(
gfx::Size(content_size.width() / 2, content_size.height() / 2));
std::unique_ptr<LayerImpl> page_scale =
LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
inner_scroll->SetScrollClipLayer(inner_clip->id());
inner_scroll->SetBounds(content_size);
inner_scroll->SetPosition(gfx::PointF());
std::unique_ptr<LayerImpl> outer_clip =
LayerImpl::Create(layer_tree_impl, kOuterViewportClipLayerId);
outer_clip->SetBounds(content_size);
outer_clip->test_properties()->is_container_for_fixed_position_layers =
true;
std::unique_ptr<LayerImpl> outer_scroll =
LayerImpl::Create(layer_tree_impl, kOuterViewportScrollLayerId);
outer_scroll->SetScrollClipLayer(outer_clip->id());
outer_scroll->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(outer_scroll->id(),
gfx::ScrollOffset());
outer_scroll->SetBounds(content_size);
outer_scroll->SetPosition(gfx::PointF());
std::unique_ptr<LayerImpl> contents =
LayerImpl::Create(layer_tree_impl, kContentLayerId);
contents->SetDrawsContent(true);
contents->SetBounds(content_size);
contents->SetPosition(gfx::PointF());
outer_scroll->test_properties()->AddChild(std::move(contents));
outer_clip->test_properties()->AddChild(std::move(outer_scroll));
inner_scroll->test_properties()->AddChild(std::move(outer_clip));
page_scale->test_properties()->AddChild(std::move(inner_scroll));
inner_clip->test_properties()->AddChild(std::move(page_scale));
root->test_properties()->AddChild(std::move(inner_clip));
layer_tree_impl->SetRootLayerForTesting(std::move(root));
layer_tree_impl->BuildPropertyTreesForTesting();
layer_tree_impl->SetViewportLayersFromIds(
Layer::INVALID_ID, kPageScaleLayerId, kInnerViewportScrollLayerId,
kOuterViewportScrollLayerId);
layer_tree_impl->DidBecomeActive();
return layer_tree_impl->InnerViewportScrollLayer();
}
LayerImpl* SetupScrollAndContentsLayers(const gfx::Size& content_size) {
LayerImpl* scroll_layer = CreateScrollAndContentsLayers(
host_impl_->active_tree(), content_size);
host_impl_->active_tree()->DidBecomeActive();
return scroll_layer;
}
// Sets up a typical virtual viewport setup with one child content layer.
// Returns a pointer to the content layer.
LayerImpl* CreateBasicVirtualViewportLayers(const gfx::Size& viewport_size,
const gfx::Size& content_size) {
// CreateScrollAndContentsLayers makes the outer viewport unscrollable and
// the inner a different size from the outer. We'll reuse its layer
// hierarchy but adjust the sizing to our needs.
CreateScrollAndContentsLayers(host_impl_->active_tree(), content_size);
LayerImpl* content_layer = host_impl_->OuterViewportScrollLayer()
->test_properties()
->children.back();
content_layer->SetBounds(content_size);
host_impl_->OuterViewportScrollLayer()->SetBounds(content_size);
LayerImpl* outer_clip =
host_impl_->OuterViewportScrollLayer()->test_properties()->parent;
outer_clip->SetBounds(viewport_size);
LayerImpl* inner_clip_layer = host_impl_->InnerViewportScrollLayer()
->test_properties()
->parent->test_properties()
->parent;
inner_clip_layer->SetBounds(viewport_size);
host_impl_->InnerViewportScrollLayer()->SetBounds(viewport_size);
// Needs to happen before building property trees as the latter propagates
// these element ids to property tree nodes.
host_impl_->active_tree()->SetElementIdsForTesting();
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->SetViewportSize(viewport_size);
host_impl_->active_tree()->DidBecomeActive();
return content_layer;
}
std::unique_ptr<LayerImpl> CreateScrollableLayer(int id,
const gfx::Size& size,
LayerImpl* clip_layer) {
DCHECK(clip_layer);
DCHECK(id != clip_layer->id());
std::unique_ptr<LayerImpl> layer =
LayerImpl::Create(host_impl_->active_tree(), id);
layer->SetScrollClipLayer(clip_layer->id());
layer->SetDrawsContent(true);
layer->SetBounds(size);
clip_layer->SetBounds(gfx::Size(size.width() / 2, size.height() / 2));
return layer;
}
std::unique_ptr<ScrollState> BeginState(const gfx::Point& point) {
ScrollStateData scroll_state_data;
scroll_state_data.is_beginning = true;
scroll_state_data.position_x = point.x();
scroll_state_data.position_y = point.y();
std::unique_ptr<ScrollState> scroll_state(
new ScrollState(scroll_state_data));
return scroll_state;
}
std::unique_ptr<ScrollState> UpdateState(const gfx::Point& point,
const gfx::Vector2dF& delta) {
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();
std::unique_ptr<ScrollState> scroll_state(
new ScrollState(scroll_state_data));
return scroll_state;
}
std::unique_ptr<ScrollState> EndState() {
ScrollStateData scroll_state_data;
scroll_state_data.is_ending = true;
std::unique_ptr<ScrollState> scroll_state(
new ScrollState(scroll_state_data));
return scroll_state;
}
void DrawFrame() {
LayerTreeHostImpl::FrameData frame;
EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
}
void pinch_zoom_pan_viewport_forces_commit_redraw(float device_scale_factor);
void pinch_zoom_pan_viewport_test(float device_scale_factor);
void pinch_zoom_pan_viewport_and_scroll_test(float device_scale_factor);
void pinch_zoom_pan_viewport_and_scroll_boundary_test(
float device_scale_factor);
void SetupMouseMoveAtWithDeviceScale(float device_scale_factor);
scoped_refptr<AnimationTimeline> timeline() { return timeline_; }
protected:
virtual std::unique_ptr<OutputSurface> CreateOutputSurface() {
return FakeOutputSurface::Create3d();
}
void DrawOneFrame() {
LayerTreeHostImpl::FrameData frame_data;
host_impl_->PrepareToDraw(&frame_data);
host_impl_->DidDrawAllLayers(frame_data);
}
static void SetScrollOffsetDelta(LayerImpl* layer_impl,
const gfx::Vector2dF& delta) {
if (layer_impl->layer_tree_impl()
->property_trees()
->scroll_tree.SetScrollOffsetDeltaForTesting(layer_impl->id(),
delta))
layer_impl->layer_tree_impl()->DidUpdateScrollOffset(
layer_impl->id(), layer_impl->transform_tree_index());
}
void BeginImplFrameAndAnimate(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();
}
FakeImplTaskRunnerProvider task_runner_provider_;
DebugScopedSetMainThreadBlocked always_main_thread_blocked_;
TestSharedBitmapManager shared_bitmap_manager_;
TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
TestTaskGraphRunner task_graph_runner_;
std::unique_ptr<OutputSurface> output_surface_;
std::unique_ptr<LayerTreeHostImpl> host_impl_;
FakeRenderingStatsInstrumentation stats_instrumentation_;
bool on_can_draw_state_changed_called_;
bool did_notify_ready_to_activate_;
bool did_request_commit_;
bool did_request_redraw_;
bool did_request_next_frame_;
bool did_request_prepare_tiles_;
bool did_complete_page_scale_animation_;
bool reduce_memory_result_;
base::Closure animation_task_;
base::TimeDelta requested_animation_delay_;
bool skip_draw_layers_in_on_draw_;
std::unique_ptr<LayerTreeHostImpl::FrameData> last_on_draw_frame_;
RenderPassList last_on_draw_render_passes_;
scoped_refptr<AnimationTimeline> timeline_;
};
// A test fixture for new animation timelines tests.
class LayerTreeHostImplTimelinesTest : public LayerTreeHostImplTest {
public:
void SetUp() override {
CreateHostImpl(DefaultSettings(), CreateOutputSurface());
}
};
TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
// Note: It is not possible to disable the renderer once it has been set,
// so we do not need to test that disabling the renderer notifies us
// that can_draw changed.
EXPECT_FALSE(host_impl_->CanDraw());
on_can_draw_state_changed_called_ = false;
// Set up the root layer, which allows us to draw.
SetupScrollAndContentsLayers(gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
// Toggle the root layer to make sure it toggles can_draw
host_impl_->active_tree()->SetRootLayerForTesting(nullptr);
host_impl_->active_tree()->DetachLayers();
EXPECT_FALSE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
SetupScrollAndContentsLayers(gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
// Toggle the device viewport size to make sure it toggles can_draw.
host_impl_->SetViewportSize(gfx::Size());
EXPECT_FALSE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
host_impl_->SetViewportSize(gfx::Size(100, 100));
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
}
TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
CreateHostImpl(DefaultSettings(),
FakeOutputSurface::CreateSoftware(
base::WrapUnique(new SoftwareOutputDevice())));
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
EXPECT_TRUE(host_impl_->CanDraw());
host_impl_->SetViewportSize(gfx::Size());
EXPECT_FALSE(host_impl_->CanDraw());
FakeOutputSurface* fake_output_surface =
static_cast<FakeOutputSurface*>(host_impl_->output_surface());
EXPECT_EQ(fake_output_surface->num_sent_frames(), 0u);
gfx::Transform identity;
gfx::Rect viewport(100, 100);
const bool resourceless_software_draw = true;
host_impl_->OnDraw(identity, viewport, viewport, resourceless_software_draw);
ASSERT_EQ(fake_output_surface->num_sent_frames(), 1u);
EXPECT_EQ(gfx::SizeF(100.f, 100.f),
fake_output_surface->last_sent_frame()->metadata.root_layer_size);
}
TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
ASSERT_FALSE(host_impl_->active_tree()->root_layer_for_testing());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
}
TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
{
std::unique_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 2));
root->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 3));
root->test_properties()->children[1]->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 4));
root->test_properties()->children[1]->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 5));
root->test_properties()
->children[1]
->test_properties()
->children[0]
->test_properties()
->AddChild(LayerImpl::Create(host_impl_->active_tree(), 6));
host_impl_->active_tree()->SetRootLayerForTesting(std::move(root));
}
LayerImpl* root = *host_impl_->active_tree()->begin();
ExpectClearedScrollDeltasRecursive(root);
std::unique_ptr<ScrollAndScaleSet> scroll_info;
scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
}
TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
gfx::ScrollOffset scroll_offset(20, 30);
gfx::Vector2d scroll_delta(11, -15);
auto root_clip_owned = LayerImpl::Create(host_impl_->active_tree(), 2);
auto* root_clip = root_clip_owned.get();
auto root_owned = LayerImpl::Create(host_impl_->active_tree(), 1);
auto* root = root_owned.get();
root_clip->SetBounds(gfx::Size(10, 10));
root_clip->test_properties()->AddChild(std::move(root_owned));
root->SetBounds(gfx::Size(110, 110));
root->SetScrollClipLayer(root_clip->id());
root->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(root->id(), scroll_offset);
host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_clip_owned));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
std::unique_ptr<ScrollAndScaleSet> scroll_info;
root->ScrollBy(scroll_delta);
scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(), scroll_delta));
gfx::Vector2d scroll_delta2(-5, 27);
root->ScrollBy(scroll_delta2);
scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(),
scroll_delta + scroll_delta2));
root->ScrollBy(gfx::Vector2d());
scroll_info = host_impl_->ProcessScrollDeltas();
EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->id(),
scroll_delta + scroll_delta2));
}
TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
InputHandler::WHEEL));
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10),
InputHandler::WHEEL));
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
InputHandler::WHEEL));
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollActiveOnlyAfterScrollMovement) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(host_impl_->IsActivelyScrolling());
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
EXPECT_TRUE(host_impl_->IsActivelyScrolling());
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->IsActivelyScrolling());
}
TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
// We should not crash when trying to scroll an empty layer tree.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
std::unique_ptr<TestWebGraphicsContext3D> context_owned =
TestWebGraphicsContext3D::Create();
context_owned->set_context_lost(true);
// Initialization will fail.
EXPECT_FALSE(
CreateHostImpl(DefaultSettings(),
FakeOutputSurface::Create3d(std::move(context_owned))));
SetupScrollAndContentsLayers(gfx::Size(100, 100));
// We should not crash when trying to scroll after the renderer initialization
// fails.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
// We should not crash if the tree is replaced while we are scrolling.
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::WHEEL)
.thread);
host_impl_->active_tree()->DetachLayers();
scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
// We should still be scrolling, because the scrolled layer also exists in the
// new tree.
gfx::Vector2d scroll_delta(0, 10);
host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_TRUE(
ScrollInfoContains(*scroll_info, scroll_layer->id(), scroll_delta));
}
TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
// Wheel handlers determine whether mouse events block scroll.
host_impl_->active_tree()->set_event_listener_properties(
EventListenerClass::kMouseWheel, EventListenerProperties::kBlocking);
EXPECT_EQ(
EventListenerProperties::kBlocking,
host_impl_->GetEventListenerProperties(EventListenerClass::kMouseWheel));
// But they don't influence the actual handling of the scroll gestures.
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollEnd(EndState().get());
}
TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
LayerImpl* scroll = SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
LayerImpl* child = 0;
{
std::unique_ptr<LayerImpl> child_layer =
LayerImpl::Create(host_impl_->active_tree(), 6);
child = child_layer.get();
child_layer->SetDrawsContent(true);
child_layer->SetPosition(gfx::PointF(0, 20));
child_layer->SetBounds(gfx::Size(50, 50));
scroll->test_properties()->AddChild(std::move(child_layer));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
}
// Touch handler regions determine whether touch events block scroll.
root->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 100, 100));
EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
// But they don't influence the actual handling of the scroll gestures.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollEnd(EndState().get());
EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
root->SetTouchEventHandlerRegion(gfx::Rect());
EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
child->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 50, 50));
EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
}
TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
// Ignore the fling since no layer is being scrolled
InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin();
EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
// Start scrolling a layer
status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
// Now the fling should go ahead since we've started scrolling a layer
status = host_impl_->FlingScrollBegin();
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
// Ignore the fling since no layer is being scrolled
InputHandler::ScrollStatus status = host_impl_->FlingScrollBegin();
EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
// Start scrolling a layer
status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
// Now the fling should go ahead since we've started scrolling a layer
status = host_impl_->FlingScrollBegin();
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
root->set_main_thread_scrolling_reasons(
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
// Start scrolling a layer
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
// The fling should be ignored since there's no layer being scrolled impl-side
status = host_impl_->FlingScrollBegin();
EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
root->set_main_thread_scrolling_reasons(
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
status = host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) {
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);
host_impl_->SetViewportSize(content_size);
std::unique_ptr<LayerImpl> root = LayerImpl::Create(layer_tree_impl, 1);
root->SetBounds(content_size);
root->SetPosition(gfx::PointF());
std::unique_ptr<LayerImpl> clip = LayerImpl::Create(layer_tree_impl, 2);
clip->SetBounds(content_size);
clip->SetPosition(gfx::PointF());
std::unique_ptr<LayerImpl> scroll = LayerImpl::Create(layer_tree_impl, 3);
scroll->SetBounds(scroll_content_size);
scroll->SetScrollClipLayer(clip->id());
scroll->SetDrawsContent(true);
std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar =
SolidColorScrollbarLayerImpl::Create(layer_tree_impl, 4, VERTICAL, 10, 0,
false, true);
scrollbar->SetBounds(scrollbar_size);
scrollbar->SetPosition(gfx::PointF(345, 0));
scrollbar->SetScrollLayerId(scroll->id());
scrollbar->SetDrawsContent(true);
scrollbar->test_properties()->opacity = 1.f;
std::unique_ptr<LayerImpl> squash1 = LayerImpl::Create(layer_tree_impl, 5);
squash1->SetBounds(gfx::Size(140, 300));
squash1->SetPosition(gfx::PointF(220, 0));
squash1->SetDrawsContent(true);
std::unique_ptr<LayerImpl> squash2 = LayerImpl::Create(layer_tree_impl, 6);
squash2->SetBounds(gfx::Size(140, 300));
squash2->SetPosition(gfx::PointF(220, 300));
squash2->SetDrawsContent(true);
scroll->test_properties()->AddChild(std::move(squash2));
clip->test_properties()->AddChild(std::move(scroll));
clip->test_properties()->AddChild(std::move(scrollbar));
clip->test_properties()->AddChild(std::move(squash1));
root->test_properties()->AddChild(std::move(clip));
layer_tree_impl->SetRootLayerForTesting(std::move(root));
layer_tree_impl->BuildPropertyTreesForTesting();
layer_tree_impl->DidBecomeActive();
// The point hits squash1 layer and also scroll layer, because scroll layer is
// not an ancestor of squash1 layer, we cannot scroll on impl thread.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(230, 150)).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
// The point hits squash1 layer and also scrollbar layer.
status = host_impl_->ScrollBegin(BeginState(gfx::Point(350, 150)).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
// The point hits squash2 layer and also scroll layer, because scroll layer is
// an ancestor of squash2 layer, we should scroll on impl.
status = host_impl_->ScrollBegin(BeginState(gfx::Point(230, 450)).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
}
TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
// All scroll types inside the non-fast scrollable region should fail.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(25, 25)).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
InputHandler::WHEEL));
status = host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(),
InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(25, 25), InputHandler::TOUCHSCREEN));
// All scroll types outside this region should succeed.
status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(75, 75), InputHandler::TOUCHSCREEN));
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(25, 25), InputHandler::TOUCHSCREEN));
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(75, 75), InputHandler::TOUCHSCREEN));
status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(),
InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(75, 75), InputHandler::TOUCHSCREEN));
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(
gfx::Point(75, 75), InputHandler::TOUCHSCREEN));
}
TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
LayerImpl* root = *host_impl_->active_tree()->begin();
root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
root->SetPosition(gfx::PointF(-25.f, 0.f));
root->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
// This point would fall into the non-fast scrollable region except that we've
// moved the layer down by 25 pixels.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(40, 10)).get(), InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10),
InputHandler::WHEEL));
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 1)).get());
host_impl_->ScrollEnd(EndState().get());
// This point is still inside the non-fast region.
status = host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
InputHandler::WHEEL);
EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers());
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler());
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
TEST_F(LayerTreeHostImplTest, ScrollByReturnsCorrectValue) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
DrawFrame();
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
// Trying to scroll to the left/top will not succeed.
EXPECT_FALSE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
.did_scroll);
EXPECT_FALSE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
.did_scroll);
EXPECT_FALSE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
.did_scroll);
// Scrolling to the right/bottom will succeed.
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(10, 0)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(10, 10)).get())
.did_scroll);
// Scrolling to left/top will now succeed.
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(0, -10)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, -10)).get())
.did_scroll);
// Scrolling diagonally against an edge will succeed.
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(10, -10)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, 0)).get())
.did_scroll);
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(-10, 10)).get())
.did_scroll);
// Trying to scroll more than the available space will also succeed.
EXPECT_TRUE(
host_impl_->ScrollBy(
UpdateState(gfx::Point(), gfx::Vector2d(5000, 5000)).get())
.did_scroll);
}
TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) {
SetupScrollAndContentsLayers(gfx::Size(200, 2000));
host_impl_->SetViewportSize(gfx::Size(100, 1000));
DrawFrame();
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::WHEEL)
.thread);
// Trying to scroll if not user_scrollable_vertical will fail.
host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(false);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_FORWARD));
EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_BACKWARD));
host_impl_->InnerViewportScrollLayer()->set_user_scrollable_vertical(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_FORWARD));
EXPECT_FLOAT_EQ(875.f,
ScrollDelta(host_impl_->InnerViewportScrollLayer()).y());
EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_BACKWARD));
}
TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
gfx::Size overflow_size(400, 400);
ASSERT_EQ(1u, scroll_layer->test_properties()->children.size());
LayerImpl* overflow = scroll_layer->test_properties()->children[0];
overflow->SetBounds(overflow_size);
overflow->SetScrollClipLayer(
scroll_layer->test_properties()->parent->test_properties()->parent->id());
overflow->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(overflow->id(),
gfx::ScrollOffset());
overflow->SetPosition(gfx::PointF());
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
gfx::Point scroll_position(10, 10);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(scroll_position).get(),
InputHandler::WHEEL)
.thread);
EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset());
gfx::Vector2dF scroll_delta(10, 10);
host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
overflow->set_user_scrollable_horizontal(false);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(scroll_position).get(),
InputHandler::WHEEL)
.thread);
EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
overflow->set_user_scrollable_vertical(false);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(scroll_position).get(),
InputHandler::WHEEL)
.thread);
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
host_impl_->ScrollBy(UpdateState(scroll_position, scroll_delta).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
}
TEST_F(LayerTreeHostImplTest, AnimationSchedulingPendingTree) {
EXPECT_FALSE(host_impl_->CommitToActiveTree());
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->CreatePendingTree();
auto root_owned = LayerImpl::Create(host_impl_->pending_tree(), 1);
auto* root = root_owned.get();
host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned));
root->SetBounds(gfx::Size(50, 50));
root->test_properties()->force_render_surface = true;
root->test_properties()->AddChild(
LayerImpl::Create(host_impl_->pending_tree(), 2));
LayerImpl* child = root->test_properties()->children[0];
child->SetBounds(gfx::Size(10, 10));
child->draw_properties().visible_layer_rect = gfx::Rect(10, 10);
child->SetDrawsContent(true);
host_impl_->pending_tree()->SetElementIdsForTesting();
AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 10.0,
3, 0);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
host_impl_->AnimatePendingTreeAfterCommit();
// An animation exists on the pending layer. Doing
// AnimatePendingTreeAfterCommit() requests another frame.
// In reality, animations without has_set_start_time() == true do not need to
// be continuously ticked on the pending tree, so it should not request
// another animation frame here. But we currently do so blindly if any
// animation exists.
EXPECT_TRUE(did_request_next_frame_);
// The pending tree with an animation does not need to draw after animating.
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
host_impl_->ActivateSyncTree();
// When the animation activates, we should request another animation frame
// to keep the animation moving.
EXPECT_TRUE(did_request_next_frame_);
// On activation we don't need to request a redraw for the animation,
// activating will draw on its own when it's ready.
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, AnimationSchedulingActiveTree) {
EXPECT_FALSE(host_impl_->CommitToActiveTree());
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->SetRootLayerForTesting(
LayerImpl::Create(host_impl_->active_tree(), 1));
LayerImpl* root = *host_impl_->active_tree()->begin();
root->SetBounds(gfx::Size(50, 50));
root->test_properties()->force_render_surface = true;
root->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 2));
LayerImpl* child = root->test_properties()->children[0];
child->SetBounds(gfx::Size(10, 10));
child->draw_properties().visible_layer_rect = gfx::Rect(10, 10);
child->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->active_tree()->SetElementIdsForTesting();
// Add a translate from 6,7 to 8,9.
TransformOperations start;
start.AppendTranslate(6.f, 7.f, 0.f);
TransformOperations end;
end.AppendTranslate(8.f, 9.f, 0.f);
AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 4.0,
start, end);
base::TimeTicks now = base::TimeTicks::Now();
host_impl_->WillBeginImplFrame(
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
// TODO(crbug.com/551134): We always request a new frame and a draw for
// animations that are on the pending tree, but we don't need to do that
// unless they are waiting for some future time to start.
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
host_impl_->ActivateAnimations();
// On activating an animation, we should request another frame so that we'll
// continue ticking the animation.
EXPECT_TRUE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
did_request_next_frame_ = false;
did_request_redraw_ = false;
did_request_commit_ = false;
// The next frame after activating, we'll tick the animation again.
host_impl_->Animate();
// An animation exists on the active layer. Doing Animate() requests another
// frame after the current one.
EXPECT_TRUE(did_request_next_frame_);
// The animation should cause us to draw at the frame's deadline.
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) {
FakeImplTaskRunnerProvider provider(nullptr);
CreateHostImplWithTaskRunnerProvider(DefaultSettings(), CreateOutputSurface(),
&provider);
EXPECT_TRUE(host_impl_->CommitToActiveTree());
host_impl_->SetViewportSize(gfx::Size(50, 50));
auto root_owned = LayerImpl::Create(host_impl_->active_tree(), 1);
auto* root = root_owned.get();
host_impl_->active_tree()->SetRootLayerForTesting(std::move(root_owned));
root->SetBounds(gfx::Size(50, 50));
root->SetHasRenderSurface(true);
auto child_owned = LayerImpl::Create(host_impl_->active_tree(), 2);
auto* child = child_owned.get();
root->test_properties()->AddChild(std::move(child_owned));
child->SetBounds(gfx::Size(10, 10));
child->draw_properties().visible_layer_rect = gfx::Rect(10, 10);
child->SetDrawsContent(true);
host_impl_->active_tree()->SetElementIdsForTesting();
AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 10.0,
3, 0);
// Set up the property trees so that UpdateDrawProperties will work in
// CommitComplete below.
LayerImplList list;
LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
root, gfx::Size(50, 50), &list);
LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
host_impl_->CommitComplete();
// Animations on the active tree should be started and ticked, and a new frame
// should be requested to continue ticking them.
EXPECT_TRUE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
// Delete the LayerTreeHostImpl before the TaskRunnerProvider goes away.
host_impl_->ReleaseOutputSurface();
host_impl_ = nullptr;
}
TEST_F(LayerTreeHostImplTest, AnimationSchedulingOnLayerDestruction) {
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->SetRootLayerForTesting(
LayerImpl::Create(host_impl_->active_tree(), 1));
LayerImpl* root = *host_impl_->active_tree()->begin();
root->SetBounds(gfx::Size(50, 50));
root->test_properties()->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 2));
LayerImpl* child = root->test_properties()->children[0];
child->SetBounds(gfx::Size(10, 10));
child->draw_properties().visible_layer_rect = gfx::Rect(10, 10);
child->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->active_tree()->SetElementIdsForTesting();
// Add a translate animation.
TransformOperations start;
start.AppendTranslate(6.f, 7.f, 0.f);
TransformOperations end;
end.AppendTranslate(8.f, 9.f, 0.f);
AddAnimatedTransformToElementWithPlayer(child->element_id(), timeline(), 4.0,
start, end);
base::TimeTicks now = base::TimeTicks::Now();
host_impl_->WillBeginImplFrame(
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
host_impl_->ActivateAnimations();
// On activating an animation, we should request another frame so that we'll
// continue ticking the animation.
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
// The next frame after activating, we'll tick the animation again.
host_impl_->Animate();
// An animation exists on the active layer. Doing Animate() requests another
// frame after the current one.
EXPECT_TRUE(did_request_next_frame_);
did_request_next_frame_ = false;
// Destroy layer, unregister animation target (element).
child->test_properties()->parent = nullptr;
root->test_properties()->RemoveChild(child);
child = nullptr;
// Doing Animate() doesn't request another frame after the current one.
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
}
class MissingTilesLayer : public LayerImpl {
public:
MissingTilesLayer(LayerTreeImpl* layer_tree_impl, int id)
: LayerImpl(layer_tree_impl, id), has_missing_tiles_(true) {}
void set_has_missing_tiles(bool has_missing_tiles) {
has_missing_tiles_ = has_missing_tiles;
}
void AppendQuads(RenderPass* render_pass,
AppendQuadsData* append_quads_data) override {
append_quads_data->num_missing_tiles += has_missing_tiles_;
}
private:
bool has_missing_tiles_;
};
TEST_F(LayerTreeHostImplTest, AnimationMarksLayerNotReady) {
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->SetRootLayerForTesting(
LayerImpl::Create(host_impl_->active_tree(), 1));
LayerImpl* root = *host_impl_->active_tree()->begin();
root->SetBounds(gfx::Size(50, 50));
root->SetHasRenderSurface(true);
root->test_properties()->AddChild(std::unique_ptr<MissingTilesLayer>(
new MissingTilesLayer(host_impl_->active_tree(), 2)));
MissingTilesLayer* child =
static_cast<MissingTilesLayer*>(root->test_properties()->children[0]);
child->SetBounds(gfx::Size(10, 10));
child->draw_properties().visible_layer_rect = gfx::Rect(10, 10);
child->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->active_tree()->SetElementIdsForTesting();
EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation());
// Add a translate from 6,7 to 8,9.
TransformOperations start;
start.AppendTranslate(6.f, 7.f, 0.f);
TransformOperations end;
end.AppendTranslate(8.f, 9.f, 0.f);
int animation_id = AddAnimatedTransformToElementWithPlayer(
child->element_id(), timeline(), 4.0, start, end);
base::TimeTicks now = base::TimeTicks::Now();
host_impl_->WillBeginImplFrame(
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, now));
host_impl_->ActivateAnimations();
host_impl_->Animate();
EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation());
host_impl_->ResetRequiresHighResToDraw();
// Child layer has an animating transform but missing tiles.
FakeLayerTreeHostImpl::FrameData frame;
DrawResult result = host_impl_->PrepareToDraw(&frame);
EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result);
host_impl_->DidDrawAllLayers(frame);
child->set_has_missing_tiles(false);
// Child layer has an animating and no missing tiles.
result = host_impl_->PrepareToDraw(&frame);
EXPECT_EQ(DRAW_SUCCESS, result);
EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation());
host_impl_->DidDrawAllLayers(frame);
// Remove the animation.
child->set_has_missing_tiles(true);
RemoveAnimationFromElementWithExistingPlayer(child->element_id(), timeline(),
animation_id);
child->draw_properties().screen_space_transform_is_animating = false;
// Child layer doesn't have an animation, but was never ready since the last
// time it animated (and has missing tiles).
result = host_impl_->PrepareToDraw(&frame);
EXPECT_EQ(DRAW_ABORTED_CHECKERBOARD_ANIMATIONS, result);
EXPECT_FALSE(child->was_ever_ready_since_last_transform_animation());
host_impl_->DidDrawAllLayers(frame);
child->set_has_missing_tiles(false);
// Child layer doesn't have an animation and all tiles are ready.
result = host_impl_->PrepareToDraw(&frame);
EXPECT_EQ(DRAW_SUCCESS, result);
EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation());
host_impl_->DidDrawAllLayers(frame);
child->set_has_missing_tiles(true);
// Child layer doesn't have an animation, and was ready at least once since
// the last time it animated.
result = host_impl_->PrepareToDraw(&frame);
EXPECT_EQ(DRAW_SUCCESS, result);
EXPECT_TRUE(child->was_ever_ready_since_last_transform_animation());
host_impl_->DidDrawAllLayers(frame);
}
TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
EXPECT_EQ(scroll_layer, host_impl_->InnerViewportScrollLayer());
LayerImpl* container_layer = scroll_layer->scroll_clip_layer();
EXPECT_EQ(gfx::Size(50, 50), container_layer->bounds());
float min_page_scale = 1.f, max_page_scale = 4.f;
float page_scale_factor = 1.f;
// The impl-based pinch zoom should adjust the max scroll position.
{
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
EXPECT_EQ(gfx::Size(50, 50), container_layer->bounds());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
EXPECT_EQ(gfx::ScrollOffset(75.0, 75.0).ToString(),
scroll_layer->MaxScrollOffset().ToString());
}
// Scrolling after a pinch gesture should always be in local space. The
// scroll deltas have the page scale factor applied.
{
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
gfx::Vector2d scroll_delta(0, 10);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(BeginState(gfx::Point(5, 5)).get(),
InputHandler::WHEEL)
.thread);
host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_TRUE(ScrollInfoContains(
*scroll_info.get(), scroll_layer->id(),
gfx::Vector2d(0, scroll_delta.y() / page_scale_delta)));
}
}
TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings,
CreateOutputSurface());
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.25f, 4.f);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
CreateBasicVirtualViewportLayers(viewport_size, content_size);
LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
EXPECT_VECTOR_EQ(
gfx::Vector2dF(500, 500),
outer_scroll_layer->MaxScrollOffset());
host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
// Sanity check - we're zoomed in, starting from the origin.
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
inner_scroll_layer->CurrentScrollOffset());
// Scroll down - only the inner viewport should scroll.
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(50, 50),
inner_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
outer_scroll_layer->CurrentScrollOffset());
// Scroll down - outer viewport should start scrolling after the inner is at
// its maximum.
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(1000.f, 1000.f)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(250, 250),
inner_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(300, 300),
outer_scroll_layer->CurrentScrollOffset());
}
// Make sure scrolls smaller than a unit applied to the viewport don't get
// dropped. crbug.com/539334.
TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateOutputSurface());
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
CreateBasicVirtualViewportLayers(viewport_size, content_size);
LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
// Sanity checks.
EXPECT_VECTOR_EQ(
gfx::Vector2dF(500, 500),
outer_scroll_layer->MaxScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(), outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(gfx::Vector2dF(), inner_scroll_layer->CurrentScrollOffset());
// Scroll only the layout viewport.
host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.125f, 0.125f)).get());
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0.125f, 0.125f),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0, 0),
inner_scroll_layer->CurrentScrollOffset());
host_impl_->ScrollEnd(EndState().get());
host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
// Now that we zoomed in, the scroll should be applied to the inner viewport.
host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(0.5f, 0.5f)).get());
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0.125f, 0.125f),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(0.25f, 0.25f),
inner_scroll_layer->CurrentScrollOffset());
host_impl_->ScrollEnd(EndState().get());
}
// Tests that scrolls during a pinch gesture (i.e. "two-finger" scrolls) work
// as expected. That is, scrolling during a pinch should bubble from the inner
// to the outer viewport.
TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings,
CreateOutputSurface());
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
CreateBasicVirtualViewportLayers(viewport_size, content_size);
LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer();
outer_scroll_layer->SetDrawsContent(true);
LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer();
inner_scroll_layer->SetDrawsContent(true);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
EXPECT_VECTOR_EQ(
gfx::Vector2dF(500, 500),
outer_scroll_layer->MaxScrollOffset());
host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2, gfx::Point(250, 250));
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(125, 125),
inner_scroll_layer->CurrentScrollOffset());
// Needed so that the pinch is accounted for in draw properties.
DrawFrame();
host_impl_->ScrollBy(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(10.f, 10.f)).get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(130, 130),
inner_scroll_layer->CurrentScrollOffset());
DrawFrame();
host_impl_->ScrollBy(
UpdateState(gfx::Point(250, 250), gfx::Vector2dF(400.f, 400.f)).get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(80, 80),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(250, 250),
inner_scroll_layer->CurrentScrollOffset());
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
}
// Tests the "snapping" of pinch-zoom gestures to the screen edge. That is, when
// a pinch zoom is anchored within a certain margin of the screen edge, we
// should assume the user means to scroll into the edge of the screen.
TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings,
CreateOutputSurface());
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 2.f);
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(500, 500);
CreateBasicVirtualViewportLayers(viewport_size, content_size);
int offsetFromEdge = Viewport::kPinchZoomSnapMarginDips - 5;
gfx::Point anchor(viewport_size.width() - offsetFromEdge,
viewport_size.height() - offsetFromEdge);
// Pinch in within the margins. The scroll should stay exactly locked to the
// bottom and right.
host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2, anchor);
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(250, 250),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
// Reset.
host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f);
SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d());
// Pinch in within the margins. The scroll should stay exactly locked to the
// top and left.
anchor = gfx::Point(offsetFromEdge, offsetFromEdge);
host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2, anchor);
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
// Reset.
host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f);
SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d());
// Pinch in just outside the margin. There should be no snapping.
offsetFromEdge = Viewport::kPinchZoomSnapMarginDips;
anchor = gfx::Point(offsetFromEdge, offsetFromEdge);
host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2, anchor);
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(50, 50),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
// Reset.
host_impl_->active_tree()->SetPageScaleOnActiveTree(1.f);
SetScrollOffsetDelta(host_impl_->InnerViewportScrollLayer(), gfx::Vector2d());
SetScrollOffsetDelta(host_impl_->OuterViewportScrollLayer(), gfx::Vector2d());
// Pinch in just outside the margin. There should be no snapping.
offsetFromEdge = Viewport::kPinchZoomSnapMarginDips;
anchor = gfx::Point(viewport_size.width() - offsetFromEdge,
viewport_size.height() - offsetFromEdge);
host_impl_->ScrollBegin(BeginState(anchor).get(), InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2, anchor);
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(200, 200),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
}
TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
CreateBasicVirtualViewportLayers(viewport_size, content_size);
LayerImpl* outer_scroll_layer = host_impl_->OuterViewportScrollLayer();
LayerImpl* inner_scroll_layer = host_impl_->InnerViewportScrollLayer();
// Zoom into the page by a 2X factor
float min_page_scale = 1.f, max_page_scale = 4.f;
float page_scale_factor = 2.f;
host_impl_->active_tree()->PushPageScaleFromMainThread(
page_scale_factor, min_page_scale, max_page_scale);
host_impl_->active_tree()->SetPageScaleOnActiveTree(page_scale_factor);
// Scroll by a small amount, there should be no bubbling to the outer
// viewport.
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::WHEEL);
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(10.f, 20.f)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(5, 10),
inner_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(),
outer_scroll_layer->CurrentScrollOffset());
// Scroll by the inner viewport's max scroll extent, the remainder
// should bubble up to the outer viewport.
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::WHEEL);
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(100.f, 100.f)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(50, 50),
inner_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(5, 10),
outer_scroll_layer->CurrentScrollOffset());
// Scroll by the outer viewport's max scroll extent, it should all go to the
// outer viewport.
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::WHEEL);
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2dF(190.f, 180.f)).get());
host_impl_->ScrollEnd(EndState().get());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(100, 100),
outer_scroll_layer->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(50, 50),
inner_scroll_layer->CurrentScrollOffset());
}
TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
ui::LatencyInfo latency_info;
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0,
1234);
std::unique_ptr<SwapPromise> swap_promise(
new LatencyInfoSwapPromise(latency_info));
SetupScrollAndContentsLayers(gfx::Size(100, 100));
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN)
.thread);
host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get());
host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(
std::move(swap_promise));
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(1u, scroll_info->swap_promises.size());
EXPECT_EQ(latency_info.trace_id(), scroll_info->swap_promises[0]->TraceId());
}
// Test that scrolls targeting a layer with a non-null scroll_parent() don't
// bubble up.
TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) {
LayerImpl* viewport_scroll =
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
// Set up two scrolling children of the root, one of which is a scroll parent
// to the other. Scrolls shouldn't bubbling from the child.
LayerImpl *parent;
LayerImpl *child;
LayerImpl *child_clip;
std::unique_ptr<LayerImpl> scroll_parent_clip =
LayerImpl::Create(host_impl_->active_tree(), 6);
std::unique_ptr<LayerImpl> scroll_parent =
CreateScrollableLayer(7, gfx::Size(10, 10), scroll_parent_clip.get());
parent = scroll_parent.get();
scroll_parent_clip->test_properties()->AddChild(std::move(scroll_parent));
viewport_scroll->test_properties()->AddChild(std::move(scroll_parent_clip));
std::unique_ptr<LayerImpl> scroll_child_clip =
LayerImpl::Create(host_impl_->active_tree(), 8);
std::unique_ptr<LayerImpl> scroll_child =
CreateScrollableLayer(9, gfx::Size(10, 10), scroll_child_clip.get());
child = scroll_child.get();
scroll_child->SetPosition(gfx::PointF(20.f, 20.f));
scroll_child_clip->test_properties()->AddChild(std::move(scroll_child));
child_clip = scroll_child_clip.get();
viewport_scroll->test_properties()->AddChild(std::move(scroll_child_clip));
child_clip->test_properties()->scroll_parent = parent;
std::unique_ptr<std::set<LayerImpl*>> scroll_children(
new std::set<LayerImpl*>);
scroll_children->insert(child_clip);
parent->test_properties()->scroll_children.reset(scroll_children.release());
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
{
host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(5, 5)).get());
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(100, 100)).get());
host_impl_->ScrollEnd(EndState().get());
// The child should be fully scrolled by the first ScrollBy.
EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 5), child->CurrentScrollOffset());
// The scroll_parent shouldn't receive the second ScrollBy.
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), parent->CurrentScrollOffset());
// The viewport shouldn't have been scrolled at all.
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
host_impl_->OuterViewportScrollLayer()->CurrentScrollOffset());
}
{
host_impl_->ScrollBegin(BeginState(gfx::Point(21, 21)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(3, 4)).get());
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get());
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get());
host_impl_->ScrollBy(
UpdateState(gfx::Point(21, 21), gfx::Vector2d(2, 1)).get());
host_impl_->ScrollEnd(EndState().get());
// The ScrollBy's should scroll the parent to its extent.
EXPECT_VECTOR_EQ(gfx::Vector2dF(5, 5), parent->CurrentScrollOffset());
// The viewport shouldn't receive any scroll delta.
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
host_impl_->InnerViewportScrollLayer()->CurrentScrollOffset());
EXPECT_VECTOR_EQ(
gfx::Vector2dF(0, 0),
host_impl_->OuterViewportScrollLayer()->CurrentScrollOffset());
}
}
TEST_F(LayerTreeHostImplTest, PinchGesture) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 1.f;
float max_page_scale = 4.f;
// Basic pinch zoom in gesture
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
}
// Zoom-in clamping
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
float page_scale_delta = 10.f;
host_impl_->ScrollBegin(BeginState(gfx::Point(50, 50)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, max_page_scale);
}
// Zoom-out clamping
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.CollectScrollDeltasForTesting();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(50, 50));
float page_scale_delta = 0.1f;
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale);
EXPECT_TRUE(scroll_info->scrolls.empty());
}
// Two-finger panning should not happen based on pinch events only
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.CollectScrollDeltasForTesting();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(20, 20));
float page_scale_delta = 1.f;
host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
EXPECT_TRUE(scroll_info->scrolls.empty());
}
// Two-finger panning should work with interleaved scroll events
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.CollectScrollDeltasForTesting();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(20, 20));
float page_scale_delta = 1.f;
host_impl_->ScrollBegin(BeginState(gfx::Point(10, 10)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
host_impl_->ScrollBy(
UpdateState(gfx::Point(10, 10), gfx::Vector2d(-10, -10)).get());
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(),
gfx::Vector2d(-10, -10)));
}
// Two-finger panning should work when starting fully zoomed out.
{
host_impl_->active_tree()->PushPageScaleFromMainThread(0.5f, 0.5f, 4.f);
SetScrollOffsetDelta(scroll_layer, gfx::Vector2d());
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.CollectScrollDeltasForTesting();
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(),
gfx::ScrollOffset(0, 0));
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
InputHandler::TOUCHSCREEN);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0));
host_impl_->PinchGestureUpdate(1.f, gfx::Point(0, 0));
// Needed so layer transform includes page scale.
DrawFrame();
host_impl_->ScrollBy(
UpdateState(gfx::Point(0, 0), gfx::Vector2d(10, 10)).get());
host_impl_->PinchGestureUpdate(1.f, gfx::Point(10, 10));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd(EndState().get());
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, 2.f);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(),
gfx::Vector2d(10, 10)));
}
}
TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4.f;
base::TimeTicks start_time = base::TimeTicks() +
base::TimeDelta::FromSeconds(1);
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
BeginFrameArgs begin_frame_args =
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
// Non-anchor zoom-in
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(50, 50));
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(
new PendingPageScaleAnimation(gfx::Vector2d(), false, 2.f,
duration)));
host_impl_->ActivateSyncTree();
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, 2);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(),
gfx::Vector2d(-50, -50)));
}
start_time += base::TimeDelta::FromSeconds(10);
halfway_through_animation += base::TimeDelta::FromSeconds(10);
end_time += base::TimeDelta::FromSeconds(10);
// Anchor zoom-out
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(50, 50));
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(
new PendingPageScaleAnimation(gfx::Vector2d(25, 25), true,
min_page_scale, duration)));
host_impl_->ActivateSyncTree();
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
did_request_redraw_ = false;
did_request_commit_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = end_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_FALSE(did_request_next_frame_);
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame();
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale);
// Pushed to (0,0) via clamping against contents layer size.
EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(),
gfx::Vector2d(-50, -50)));
}
}
TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4.f;
base::TimeTicks start_time = base::TimeTicks() +
base::TimeDelta::FromSeconds(1);
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
BeginFrameArgs begin_frame_args =
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
// Anchor zoom with unchanged page scale should not change scroll or scale.
{
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(
scroll_layer->id(), gfx::ScrollOffset(50, 50));
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(
new PendingPageScaleAnimation(gfx::Vector2d(), true, 1.f,
duration)));
host_impl_->ActivateSyncTree();
begin_frame_args.frame_time = start_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
host_impl_->DidFinishImplFrame();
begin_frame_args.frame_time = halfway_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
host_impl_->DidFinishImplFrame();
begin_frame_args.frame_time = end_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame();
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, 1);
ExpectNone(*scroll_info, scroll_layer->id());
}
}
TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
host_impl_->CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
CreateScrollAndContentsLayers(
host_impl_->pending_tree(),
gfx::Size(100, 100));
host_impl_->pending_tree()->BuildPropertyTreesForTesting();
host_impl_->ActivateSyncTree();
DrawFrame();
LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 0.5f;
float max_page_scale = 4.f;
host_impl_->sync_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
host_impl_->ActivateSyncTree();
base::TimeTicks start_time = base::TimeTicks() +
base::TimeDelta::FromSeconds(1);
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
base::TimeTicks third_through_animation = start_time + duration / 3;
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
float target_scale = 2.f;
BeginFrameArgs begin_frame_args =
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(),
gfx::ScrollOffset(50, 50));
// Make sure TakePageScaleAnimation works properly.
host_impl_->sync_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation(
gfx::Vector2d(), false, target_scale, duration)));
std::unique_ptr<PendingPageScaleAnimation> psa =
host_impl_->sync_tree()->TakePendingPageScaleAnimation();
EXPECT_EQ(target_scale, psa->scale);
EXPECT_EQ(duration, psa->duration);
EXPECT_EQ(nullptr, host_impl_->sync_tree()->TakePendingPageScaleAnimation());
// Recreate the PSA. Nothing should happen here since the tree containing the
// PSA hasn't been activated yet.
did_request_redraw_ = false;
did_request_next_frame_ = false;
host_impl_->sync_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation(
gfx::Vector2d(), false, target_scale, duration)));
begin_frame_args.frame_time = halfway_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
host_impl_->DidFinishImplFrame();
// Activate the sync tree. This should cause the animation to become enabled.
// It should also clear the pointer on the sync tree.
host_impl_->ActivateSyncTree();
EXPECT_EQ(nullptr,
host_impl_->sync_tree()->TakePendingPageScaleAnimation().get());
EXPECT_FALSE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
start_time += base::TimeDelta::FromSeconds(10);
third_through_animation += base::TimeDelta::FromSeconds(10);
halfway_through_animation += base::TimeDelta::FromSeconds(10);
end_time += base::TimeDelta::FromSeconds(10);
// From here on, make sure the animation runs as normal.
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = start_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = third_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
// Another activation shouldn't have any effect on the animation.
host_impl_->ActivateSyncTree();
did_request_redraw_ = false;
did_request_next_frame_ = false;
begin_frame_args.frame_time = halfway_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
did_request_redraw_ = false;
did_request_next_frame_ = false;
did_request_commit_ = false;
begin_frame_args.frame_time = end_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_request_commit_);
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame();
std::unique_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, target_scale);
EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->id(),
gfx::Vector2d(-50, -50)));
}
TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
DrawFrame();
LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
DCHECK(scroll_layer);
base::TimeTicks start_time =
base::TimeTicks() + base::TimeDelta::FromSeconds(1);
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(100);
base::TimeTicks halfway_through_animation = start_time + duration / 2;
base::TimeTicks end_time = start_time + duration;
BeginFrameArgs begin_frame_args =
CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE);
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f);
scroll_layer->layer_tree_impl()
->property_trees()
->scroll_tree.UpdateScrollOffsetBaseForTesting(scroll_layer->id(),
gfx::ScrollOffset(50, 50));
did_complete_page_scale_animation_ = false;
host_impl_->active_tree()->SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation>(new PendingPageScaleAnimation(
gfx::Vector2d(), false, 2.f, duration)));
host_impl_->ActivateSyncTree();
begin_frame_args.frame_time = start_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame();
begin_frame_args.frame_time = halfway_through_animation;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_FALSE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame();
begin_frame_args.frame_time = end_time;
host_impl_->WillBeginImplFrame(begin_frame_args);
host_impl_->Animate();
EXPECT_TRUE(did_complete_page_scale_animation_);
host_impl_->DidFinishImplFrame();
}
TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByBoundsDelta) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 0.5f, 4.f);
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
LayerImpl* inner_scroll = host_impl_->InnerViewportScrollLayer();
LayerImpl* inner_container = inner_scroll->scroll_clip_layer();
DCHECK(inner_scroll);
DCHECK(inner_container);
EXPECT_EQ(gfx::ScrollOffset(50, 50), inner_scroll->MaxScrollOffset());
inner_container->SetBoundsDelta(gfx::Vector2dF(15.f, 15.f));
inner_scroll->SetBoundsDelta(gfx::Vector2dF(7.f, 7.f));
EXPECT_EQ(gfx::ScrollOffset(42, 42), inner_scroll->MaxScrollOffset());
inner_container->SetBoundsDelta(gfx::Vector2dF());
inner_scroll->SetBoundsDelta(gfx::Vector2dF());
inner_scroll->SetBounds(gfx::Size());
host_impl_->active_tree()->BuildPropertyTreesForTesting();
DrawFrame();
inner_scroll->SetBoundsDelta(gfx::Vector2dF(60.f, 60.f));
EXPECT_EQ(gfx::ScrollOffset(10, 10), inner_scroll->MaxScrollOffset());
}
class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl {
public:
LayerTreeHostImplOverridePhysicalTime(
const LayerTreeSettings& settings,
LayerTreeHostImplClient* client,
TaskRunnerProvider* task_runner_provider,
SharedBitmapManager* manager,
TaskGraphRunner* task_graph_runner,
RenderingStatsInstrumentation* rendering_stats_instrumentation)
: LayerTreeHostImpl(settings,
client,
task_runner_provider,
rendering_stats_instrumentation,
manager,
nullptr,
task_graph_runner,
AnimationHost::CreateForTesting(ThreadInstance::IMPL),
0) {}
BeginFrameArgs CurrentBeginFrameArgs() const override {
return CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE,
fake_current_physical_time_);
}
void SetCurrentPhysicalTimeTicksForTest(base::TimeTicks fake_now) {
fake_current_physical_time_ = fake_now;
}
private:
base::TimeTicks fake_current_physical_time_;
};
class LayerTreeHostImplTestScrollbarAnimation : public LayerTreeHostImplTest {
protected:
void SetupLayers(LayerTreeSettings settings) {
host_impl_->ReleaseOutputSurface();
host_impl_ = nullptr;
gfx::Size content_size(100, 100);
LayerTreeHostImplOverridePhysicalTime* host_impl_override_time =
new LayerTreeHostImplOverridePhysicalTime(
settings, this, &task_runner_provider_, &shared_bitmap_manager_,
&task_graph_runner_, &stats_instrumentation_);
host_impl_ = base::WrapUnique(host_impl_override_time);
output_surface_ = CreateOutputSurface();
host_impl_->SetVisible(true);
host_impl_->InitializeRenderer(output_surface_.get());
SetupScrollAndContentsLayers(content_size);
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, 1.f, 4.f);
host_impl_->SetViewportSize(
gfx::Size(content_size.width() / 2, content_size.height() / 2));
std::unique_ptr<SolidColorScrollbarLayerImpl> scrollbar =
SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(), 400,
VERTICAL, 10, 0, false, true);
scrollbar->test_properties()->opacity = 0.f;
EXPECT_FLOAT_EQ(0.f, scrollbar->test_properties()->opacity);
LayerImpl* scroll = host_impl_->active_tree()->OuterViewportScrollLayer();
LayerImpl* root = host_impl_->active_tree()->InnerViewportContainerLayer();
scrollbar->SetScrollLayerId(scroll->id());
root->test_properties()->AddChild(std::move(scrollbar));
host_impl_->active_tree()->BuildPropertyTreesForTesting();
host_impl_->active_tree()->DidBecomeActive();
DrawFrame();
}
void RunTest(LayerTreeSettings::ScrollbarAnimator animator) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = animator;
settings.scrollbar_fade_delay_ms = 20;
settings.scrollbar_fade_duration_ms = 20;
SetupLayers(settings);
base::TimeTicks fake_now = base::TimeTicks::Now();
// A task will be posted to fade the initial scrollbar.
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(animation_task_.Equals(base::Closure()));
requested_animation_delay_ = base::TimeDelta();
animation_task_ = base::Closure();
// If no scroll happened during a scroll gesture, it should have no effect.
host_impl_->ScrollBegin(BeginState(gfx::Point()).get(),
InputHandler::WHEEL);
host_impl_->ScrollEnd(EndState().get());
EXPECT_FALSE(did_request_next_frame_);
EXPECT_FALSE(did_request_redraw_<