blob: 85c9836d3d601ecdd43fa03ccc086a14623bec4c [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 <cmath>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/containers/hash_tables.h"
#include "cc/base/math_util.h"
#include "cc/input/top_controls_manager.h"
#include "cc/layers/delegated_renderer_layer_impl.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/io_surface_layer_impl.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/quad_sink.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/layers/scrollbar_layer_impl.h"
#include "cc/layers/solid_color_layer_impl.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/layers/tiled_layer_impl.h"
#include "cc/layers/video_layer_impl.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/gl_renderer.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/resources/layer_tiling_data.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/fake_rendering_stats_instrumentation.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/render_pass_test_common.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 "ui/gfx/size_conversions.h"
#include "ui/gfx/vector2d_conversions.h"
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()
: proxy_(),
always_impl_thread_(&proxy_),
always_main_thread_blocked_(&proxy_),
did_try_initialize_renderer_(false),
on_can_draw_state_changed_called_(false),
has_pending_tree_(false),
did_request_commit_(false),
did_request_redraw_(false),
did_upload_visible_tile_(false),
reduce_memory_result_(true),
current_limit_bytes_(0),
current_priority_cutoff_value_(0) {
media::InitializeMediaLibraryForTesting();
}
virtual void SetUp() OVERRIDE {
LayerTreeSettings settings;
settings.minimum_occlusion_tracking_size = gfx::Size();
settings.impl_side_painting = true;
settings.solid_color_scrollbars = true;
host_impl_ = LayerTreeHostImpl::Create(settings,
this,
&proxy_,
&stats_instrumentation_);
host_impl_->InitializeRenderer(CreateOutputSurface());
host_impl_->SetViewportSize(gfx::Size(10, 10));
}
virtual void TearDown() OVERRIDE {}
virtual void DidTryInitializeRendererOnImplThread(
bool success,
scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {
did_try_initialize_renderer_ = true;
}
virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {}
virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
OVERRIDE {}
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {
on_can_draw_state_changed_called_ = true;
}
virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE {
has_pending_tree_ = has_pending_tree;
}
virtual void SetNeedsRedrawOnImplThread() OVERRIDE {
did_request_redraw_ = true;
}
virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) OVERRIDE {
did_request_redraw_ = true;
}
virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE {
did_upload_visible_tile_ = true;
}
virtual void SetNeedsCommitOnImplThread() OVERRIDE {
did_request_commit_ = true;
}
virtual void PostAnimationEventsToMainThreadOnImplThread(
scoped_ptr<AnimationEventsVector> events,
base::Time wall_clock_time) OVERRIDE {}
virtual bool ReduceContentsTextureMemoryOnImplThread(
size_t limit_bytes, int priority_cutoff) OVERRIDE {
current_limit_bytes_ = limit_bytes;
current_priority_cutoff_value_ = priority_cutoff;
return reduce_memory_result_;
}
virtual void ReduceWastedContentsTextureMemoryOnImplThread() OVERRIDE {}
virtual void SendManagedMemoryStats() OVERRIDE {}
virtual bool IsInsideDraw() OVERRIDE { return false; }
virtual void RenewTreePriority() OVERRIDE {}
virtual void RequestScrollbarAnimationOnImplThread(base::TimeDelta delay)
OVERRIDE { requested_scrollbar_animation_delay_ = delay; }
virtual void DidActivatePendingTree() OVERRIDE {}
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
}
void CreateLayerTreeHost(bool partial_swap,
scoped_ptr<OutputSurface> output_surface) {
LayerTreeSettings settings;
settings.minimum_occlusion_tracking_size = gfx::Size();
settings.partial_swap_enabled = partial_swap;
host_impl_ = LayerTreeHostImpl::Create(settings,
this,
&proxy_,
&stats_instrumentation_);
host_impl_->InitializeRenderer(output_surface.Pass());
host_impl_->SetViewportSize(gfx::Size(10, 10));
}
void SetupRootLayerImpl(scoped_ptr<LayerImpl> root) {
root->SetAnchorPoint(gfx::PointF());
root->SetPosition(gfx::PointF());
root->SetBounds(gfx::Size(10, 10));
root->SetContentBounds(gfx::Size(10, 10));
root->SetDrawsContent(true);
root->draw_properties().visible_content_rect = gfx::Rect(0, 0, 10, 10);
host_impl_->active_tree()->SetRootLayer(root.Pass());
}
static void ExpectClearedScrollDeltasRecursive(LayerImpl* layer) {
ASSERT_EQ(layer->ScrollDelta(), gfx::Vector2d());
for (size_t i = 0; i < layer->children().size(); ++i)
ExpectClearedScrollDeltasRecursive(layer->children()[i]);
}
static void ExpectContains(const ScrollAndScaleSet& scroll_info,
int id,
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;
EXPECT_VECTOR_EQ(scroll_delta, scroll_info.scrolls[i].scroll_delta);
times_encountered++;
}
ASSERT_EQ(times_encountered, 1);
}
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* SetupScrollAndContentsLayers(gfx::Size content_size) {
scoped_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetBounds(content_size);
root->SetContentBounds(content_size);
root->SetPosition(gfx::PointF());
root->SetAnchorPoint(gfx::PointF());
scoped_ptr<LayerImpl> scroll =
LayerImpl::Create(host_impl_->active_tree(), 2);
LayerImpl* scroll_layer = scroll.get();
scroll->SetScrollable(true);
scroll->SetScrollOffset(gfx::Vector2d());
scroll->SetMaxScrollOffset(gfx::Vector2d(content_size.width(),
content_size.height()));
scroll->SetBounds(content_size);
scroll->SetContentBounds(content_size);
scroll->SetPosition(gfx::PointF());
scroll->SetAnchorPoint(gfx::PointF());
scoped_ptr<LayerImpl> contents =
LayerImpl::Create(host_impl_->active_tree(), 3);
contents->SetDrawsContent(true);
contents->SetBounds(content_size);
contents->SetContentBounds(content_size);
contents->SetPosition(gfx::PointF());
contents->SetAnchorPoint(gfx::PointF());
scroll->AddChild(contents.Pass());
root->AddChild(scroll.Pass());
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
return scroll_layer;
}
scoped_ptr<LayerImpl> CreateScrollableLayer(int id, gfx::Size size) {
scoped_ptr<LayerImpl> layer =
LayerImpl::Create(host_impl_->active_tree(), id);
layer->SetScrollable(true);
layer->SetDrawsContent(true);
layer->SetBounds(size);
layer->SetContentBounds(size);
layer->SetMaxScrollOffset(gfx::Vector2d(size.width() * 2,
size.height() * 2));
return layer.Pass();
}
void InitializeRendererAndDrawFrame() {
host_impl_->InitializeRenderer(CreateOutputSurface());
DrawFrame();
}
void DrawFrame() {
LayerTreeHostImpl::FrameData frame;
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
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);
protected:
virtual scoped_ptr<OutputSurface> CreateOutputSurface() {
return CreateFakeOutputSurface();
}
void DrawOneFrame() {
LayerTreeHostImpl::FrameData frame_data;
host_impl_->PrepareToDraw(&frame_data, gfx::Rect());
host_impl_->DidDrawAllLayers(frame_data);
}
FakeProxy proxy_;
DebugScopedSetImplThread always_impl_thread_;
DebugScopedSetMainThreadBlocked always_main_thread_blocked_;
scoped_ptr<LayerTreeHostImpl> host_impl_;
FakeRenderingStatsInstrumentation stats_instrumentation_;
bool did_try_initialize_renderer_;
bool on_can_draw_state_changed_called_;
bool has_pending_tree_;
bool did_request_commit_;
bool did_request_redraw_;
bool did_upload_visible_tile_;
bool reduce_memory_result_;
base::TimeDelta requested_scrollbar_animation_delay_;
size_t current_limit_bytes_;
int current_priority_cutoff_value_;
};
class TestWebGraphicsContext3DMakeCurrentFails
: public TestWebGraphicsContext3D {
public:
virtual bool makeContextCurrent() OVERRIDE { return false; }
};
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;
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()->SetRootLayer(scoped_ptr<LayerImpl>());
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;
// Toggle contents textures purged without causing any evictions,
// and make sure that it does not change can_draw.
set_reduce_memory_result(false);
host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
host_impl_->memory_allocation_limit_bytes() - 1), true);
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_FALSE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
// Toggle contents textures purged to make sure it toggles can_draw.
set_reduce_memory_result(true);
host_impl_->SetMemoryPolicy(ManagedMemoryPolicy(
host_impl_->memory_allocation_limit_bytes() - 1), true);
EXPECT_FALSE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
host_impl_->active_tree()->ResetContentsTexturesPurged();
EXPECT_TRUE(host_impl_->CanDraw());
EXPECT_TRUE(on_can_draw_state_changed_called_);
on_can_draw_state_changed_called_ = false;
}
TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
ASSERT_FALSE(host_impl_->active_tree()->root_layer());
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
}
TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
{
scoped_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->AddChild(LayerImpl::Create(host_impl_->active_tree(), 2));
root->AddChild(LayerImpl::Create(host_impl_->active_tree(), 3));
root->children()[1]->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 4));
root->children()[1]->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 5));
root->children()[1]->children()[0]->AddChild(
LayerImpl::Create(host_impl_->active_tree(), 6));
host_impl_->active_tree()->SetRootLayer(root.Pass());
}
LayerImpl* root = host_impl_->active_tree()->root_layer();
ExpectClearedScrollDeltasRecursive(root);
scoped_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::Vector2d scroll_offset(20, 30);
gfx::Vector2d scroll_delta(11, -15);
{
scoped_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetScrollOffset(scroll_offset);
root->SetScrollable(true);
root->SetMaxScrollOffset(gfx::Vector2d(100, 100));
root->ScrollBy(scroll_delta);
host_impl_->active_tree()->SetRootLayer(root.Pass());
}
LayerImpl* root = host_impl_->active_tree()->root_layer();
scoped_ptr<ScrollAndScaleSet> scroll_info;
scroll_info = host_impl_->ProcessScrollDeltas();
ASSERT_EQ(scroll_info->scrolls.size(), 1u);
EXPECT_VECTOR_EQ(root->sent_scroll_delta(), scroll_delta);
ExpectContains(*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_VECTOR_EQ(root->sent_scroll_delta(), scroll_delta + scroll_delta2);
ExpectContains(*scroll_info, root->id(), scroll_delta + scroll_delta2);
root->ScrollBy(gfx::Vector2d());
scroll_info = host_impl_->ProcessScrollDeltas();
EXPECT_EQ(root->sent_scroll_delta(), scroll_delta + scroll_delta2);
}
TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
host_impl_->ScrollEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
// We should not crash when trying to scroll an empty layer tree.
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
LayerTreeSettings settings;
host_impl_ = LayerTreeHostImpl::Create(settings,
this,
&proxy_,
&stats_instrumentation_);
// Initialization will fail here.
host_impl_->InitializeRenderer(FakeOutputSurface::Create3d(
scoped_ptr<WebKit::WebGraphicsContext3D>(
new TestWebGraphicsContext3DMakeCurrentFails))
.PassAs<OutputSurface>());
host_impl_->SetViewportSize(gfx::Size(10, 10));
SetupScrollAndContentsLayers(gfx::Size(100, 100));
// We should not crash when trying to scroll after the renderer initialization
// fails.
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
// We should not crash if the tree is replaced while we are scrolling.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
host_impl_->active_tree()->DetachLayerTree();
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(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info, scroll_layer->id(), scroll_delta);
}
TEST_F(LayerTreeHostImplTest, ClearRootRenderSurfaceAndScroll) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
// We should be able to scroll even if the root layer loses its render surface
// after the most recent render.
host_impl_->active_tree()->root_layer()->ClearRenderSurface();
host_impl_->active_tree()->set_needs_update_draw_properties();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, WheelEventHandlers) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->SetHaveWheelEventHandlers(true);
// With registered event handlers, wheel scrolls have to go to the main
// thread.
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
// But gesture scrolls can still be handled.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
}
TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
// Ignore the fling since no layer is being scrolled
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->FlingScrollBegin());
// Start scrolling a layer
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
// Now the fling should go ahead since we've started scrolling a layer
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->FlingScrollBegin());
}
TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
// Ignore the fling since no layer is being scrolled
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->FlingScrollBegin());
// Start scrolling a layer
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
// Now the fling should go ahead since we've started scrolling a layer
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->FlingScrollBegin());
}
TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->SetShouldScrollOnMainThread(true);
// Start scrolling a layer
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
// The fling should be ignored since there's no layer being scrolled impl-side
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->FlingScrollBegin());
}
TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->SetShouldScrollOnMainThread(true);
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
}
TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->SetContentsScale(2.f, 2.f);
root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
InitializeRendererAndDrawFrame();
// All scroll types inside the non-fast scrollable region should fail.
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(25, 25),
InputHandler::Wheel));
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(25, 25),
InputHandler::Gesture));
// All scroll types outside this region should succeed.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(75, 75),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
host_impl_->ScrollEnd();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(75, 75),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
host_impl_->ScrollEnd();
}
TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->SetContentsScale(2.f, 2.f);
root->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 50));
root->SetPosition(gfx::PointF(-25.f, 0.f));
InitializeRendererAndDrawFrame();
// This point would fall into the non-fast scrollable region except that we've
// moved the layer down by 25 pixels.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(40, 10),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 1));
host_impl_->ScrollEnd();
// This point is still inside the non-fast region.
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(10, 10),
InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, ScrollByReturnsCorrectValue) {
SetupScrollAndContentsLayers(gfx::Size(200, 200));
host_impl_->SetViewportSize(gfx::Size(100, 100));
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
// Trying to scroll to the left/top will not succeed.
EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)));
EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)));
EXPECT_FALSE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10)));
// Scrolling to the right/bottom will succeed.
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 0)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, 10)));
// Scrolling to left/top will now succeed.
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, -10)));
// Scrolling diagonally against an edge will succeed.
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(10, -10)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 0)));
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(-10, 10)));
// Trying to scroll more than the available space will also succeed.
EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(5000, 5000)));
}
TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) {
SetupScrollAndContentsLayers(gfx::Size(200, 2000));
host_impl_->SetViewportSize(gfx::Size(100, 1000));
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Wheel));
// Trying to scroll without a vertical scrollbar will fail.
EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_FORWARD));
EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_BACKWARD));
scoped_ptr<cc::ScrollbarLayerImpl> vertical_scrollbar(
cc::ScrollbarLayerImpl::Create(
host_impl_->active_tree(),
20,
VERTICAL));
vertical_scrollbar->SetBounds(gfx::Size(15, 1000));
host_impl_->RootScrollLayer()->SetVerticalScrollbarLayer(
vertical_scrollbar.get());
// Trying to scroll with a vertical scrollbar will succeed.
EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_FORWARD));
EXPECT_FLOAT_EQ(875.f, host_impl_->RootScrollLayer()->ScrollDelta().y());
EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_BACKWARD));
}
TEST_F(LayerTreeHostImplTest,
ClearRootRenderSurfaceAndHitTestTouchHandlerRegion) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
// We should be able to hit test for touch event handlers even if the root
// layer loses its render surface after the most recent render.
host_impl_->active_tree()->root_layer()->ClearRenderSurface();
host_impl_->active_tree()->set_needs_update_draw_properties();
EXPECT_EQ(host_impl_->HaveTouchEventHandlersAt(gfx::Point()), false);
}
TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
EXPECT_EQ(scroll_layer, host_impl_->RootScrollLayer());
float min_page_scale = 1.f, max_page_scale = 4.f;
// The impl-based pinch zoom should adjust the max scroll position.
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
host_impl_->active_tree()->SetPageScaleDelta(1.f);
scroll_layer->SetScrollDelta(gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
EXPECT_EQ(gfx::Vector2d(75, 75).ToString(),
scroll_layer->max_scroll_offset().ToString());
}
// Scrolling after a pinch gesture should always be in local space. The
// scroll deltas do not have the page scale factor applied.
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
host_impl_->active_tree()->SetPageScaleDelta(1.f);
scroll_layer->SetScrollDelta(gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
host_impl_->PinchGestureEnd();
gfx::Vector2d scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
scroll_delta);
}
}
TEST_F(LayerTreeHostImplTest, PinchGesture) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
InitializeRendererAndDrawFrame();
LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
DCHECK(scroll_layer);
float min_page_scale = 1.f;
float max_page_scale = 4.f;
// Basic pinch zoom in gesture
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
float page_scale_delta = 2.f;
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
}
// Zoom-in clamping
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
float page_scale_delta = 10.f;
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
host_impl_->PinchGestureEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, max_page_scale);
}
// Zoom-out clamping
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
float page_scale_delta = 0.1f;
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
host_impl_->PinchGestureEnd();
scoped_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()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
scroll_layer->SetScrollOffset(gfx::Vector2d(20, 20));
float page_scale_delta = 1.f;
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();
scoped_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()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
scroll_layer->SetScrollOffset(gfx::Vector2d(20, 20));
float page_scale_delta = 1.f;
host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Wheel);
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
host_impl_->ScrollBy(gfx::Point(10, 10), gfx::Vector2d(-10, -10));
host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
host_impl_->PinchGestureEnd();
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
ExpectContains(*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));
InitializeRendererAndDrawFrame();
LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
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;
// Non-anchor zoom-in
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
host_impl_->StartPageScaleAnimation(gfx::Vector2d(),
false,
2.f,
start_time,
duration);
host_impl_->Animate(halfway_through_animation, base::Time());
EXPECT_TRUE(did_request_redraw_);
host_impl_->Animate(end_time, base::Time());
EXPECT_TRUE(did_request_commit_);
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, 2);
ExpectContains(*scroll_info, scroll_layer->id(), gfx::Vector2d(-50, -50));
}
// Anchor zoom-out
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
host_impl_->StartPageScaleAnimation(gfx::Vector2d(25, 25),
true,
min_page_scale,
start_time, duration);
host_impl_->Animate(end_time, base::Time());
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
scoped_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.
ExpectContains(*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));
InitializeRendererAndDrawFrame();
LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
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;
// Anchor zoom with unchanged page scale should not change scroll or scale.
{
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f,
min_page_scale,
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
host_impl_->StartPageScaleAnimation(gfx::Vector2d(),
true,
1.f,
start_time,
duration);
host_impl_->Animate(halfway_through_animation, base::Time());
EXPECT_TRUE(did_request_redraw_);
host_impl_->Animate(end_time, base::Time());
EXPECT_TRUE(did_request_commit_);
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
EXPECT_EQ(scroll_info->page_scale_delta, 1);
ExpectNone(*scroll_info, scroll_layer->id());
}
}
class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl {
public:
LayerTreeHostImplOverridePhysicalTime(
const LayerTreeSettings& settings,
LayerTreeHostImplClient* client,
Proxy* proxy,
RenderingStatsInstrumentation* rendering_stats_instrumentation)
: LayerTreeHostImpl(settings,
client,
proxy,
rendering_stats_instrumentation) {}
virtual base::TimeTicks CurrentPhysicalTimeTicks() const OVERRIDE {
return fake_current_physical_time_;
}
void SetCurrentPhysicalTimeTicksForTest(base::TimeTicks fake_now) {
fake_current_physical_time_ = fake_now;
}
private:
base::TimeTicks fake_current_physical_time_;
};
TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
LayerTreeSettings settings;
settings.use_linear_fade_scrollbar_animator = true;
settings.scrollbar_linear_fade_delay_ms = 20;
settings.scrollbar_linear_fade_length_ms = 20;
LayerTreeHostImplOverridePhysicalTime* host_impl_override_time =
new LayerTreeHostImplOverridePhysicalTime(
settings, this, &proxy_, &stats_instrumentation_);
host_impl_ = make_scoped_ptr<LayerTreeHostImpl>(host_impl_override_time);
host_impl_->InitializeRenderer(CreateOutputSurface());
host_impl_->SetViewportSize(gfx::Size(10, 10));
gfx::Size content_size(100, 100);
scoped_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetBounds(content_size);
root->SetContentBounds(content_size);
scoped_ptr<LayerImpl> scroll =
LayerImpl::Create(host_impl_->active_tree(), 2);
scroll->SetScrollable(true);
scroll->SetScrollOffset(gfx::Vector2d());
scroll->SetMaxScrollOffset(gfx::Vector2d(content_size.width(),
content_size.height()));
scroll->SetBounds(content_size);
scroll->SetContentBounds(content_size);
scoped_ptr<LayerImpl> contents =
LayerImpl::Create(host_impl_->active_tree(), 3);
contents->SetDrawsContent(true);
contents->SetBounds(content_size);
contents->SetContentBounds(content_size);
scoped_ptr<ScrollbarLayerImpl> scrollbar = ScrollbarLayerImpl::Create(
host_impl_->active_tree(),
4,
VERTICAL);
scroll->SetVerticalScrollbarLayer(scrollbar.get());
scroll->AddChild(contents.Pass());
root->AddChild(scroll.Pass());
root->AddChild(scrollbar.PassAs<LayerImpl>());
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
InitializeRendererAndDrawFrame();
base::TimeTicks fake_now = base::TimeTicks::Now();
host_impl_override_time->SetCurrentPhysicalTimeTicksForTest(fake_now);
// If no scroll happened recently, StartScrollbarAnimation should have no
// effect.
host_impl_->StartScrollbarAnimation();
EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
EXPECT_FALSE(did_request_redraw_);
// After a scroll, a fade animation should be scheduled about 20ms from now.
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
host_impl_->ScrollEnd();
host_impl_->StartScrollbarAnimation();
EXPECT_LT(base::TimeDelta::FromMilliseconds(19),
requested_scrollbar_animation_delay_);
EXPECT_FALSE(did_request_redraw_);
requested_scrollbar_animation_delay_ = base::TimeDelta();
// After the fade begins, we should start getting redraws instead of a
// scheduled animation.
fake_now += base::TimeDelta::FromMilliseconds(25);
host_impl_override_time->SetCurrentPhysicalTimeTicksForTest(fake_now);
host_impl_->StartScrollbarAnimation();
EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
EXPECT_TRUE(did_request_redraw_);
did_request_redraw_ = false;
// If no scroll happened recently, StartScrollbarAnimation should have no
// effect.
fake_now += base::TimeDelta::FromMilliseconds(25);
host_impl_override_time->SetCurrentPhysicalTimeTicksForTest(fake_now);
host_impl_->StartScrollbarAnimation();
EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
EXPECT_FALSE(did_request_redraw_);
// Setting the scroll offset outside a scroll should also cause the scrollbar
// to appear and to schedule a fade.
host_impl_->RootScrollLayer()->SetScrollOffset(gfx::Vector2d(5, 5));
host_impl_->StartScrollbarAnimation();
EXPECT_LT(base::TimeDelta::FromMilliseconds(19),
requested_scrollbar_animation_delay_);
EXPECT_FALSE(did_request_redraw_);
requested_scrollbar_animation_delay_ = base::TimeDelta();
// None of the above should have called CurrentFrameTimeTicks, so if we call
// it now we should get the current time.
fake_now += base::TimeDelta::FromMilliseconds(10);
host_impl_override_time->SetCurrentPhysicalTimeTicksForTest(fake_now);
EXPECT_EQ(fake_now, host_impl_->CurrentFrameTimeTicks());
}
TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f);
InitializeRendererAndDrawFrame();
{
CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::Vector2dF(), metadata.root_scroll_offset);
EXPECT_EQ(1.f, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(50.f, 50.f), metadata.viewport_size);
EXPECT_EQ(gfx::SizeF(100.f, 100.f), metadata.root_layer_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4.f, metadata.max_page_scale_factor);
}
// Scrolling should update metadata immediately.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
{
CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::Vector2dF(0.f, 10.f), metadata.root_scroll_offset);
}
host_impl_->ScrollEnd();
{
CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::Vector2dF(0.f, 10.f), metadata.root_scroll_offset);
}
// Page scale should update metadata correctly (shrinking only the viewport).
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(2.f, gfx::Point());
host_impl_->PinchGestureEnd();
{
CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::Vector2dF(0.f, 10.f), metadata.root_scroll_offset);
EXPECT_EQ(2.f, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(25.f, 25.f), metadata.viewport_size);
EXPECT_EQ(gfx::SizeF(100.f, 100.f), metadata.root_layer_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4.f, metadata.max_page_scale_factor);
}
// Likewise if set from the main thread.
host_impl_->ProcessScrollDeltas();
host_impl_->active_tree()->SetPageScaleFactorAndLimits(4.f, 0.5f, 4.f);
host_impl_->active_tree()->SetPageScaleDelta(1.f);
{
CompositorFrameMetadata metadata =
host_impl_->MakeCompositorFrameMetadata();
EXPECT_EQ(gfx::Vector2dF(0.f, 10.f), metadata.root_scroll_offset);
EXPECT_EQ(4.f, metadata.page_scale_factor);
EXPECT_EQ(gfx::SizeF(12.5f, 12.5f), metadata.viewport_size);
EXPECT_EQ(gfx::SizeF(100.f, 100.f), metadata.root_layer_size);
EXPECT_EQ(0.5f, metadata.min_page_scale_factor);
EXPECT_EQ(4.f, metadata.max_page_scale_factor);
}
}
class DidDrawCheckLayer : public TiledLayerImpl {
public:
static scoped_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
return scoped_ptr<LayerImpl>(new DidDrawCheckLayer(tree_impl, id));
}
virtual bool WillDraw(DrawMode draw_mode, ResourceProvider* provider)
OVERRIDE {
will_draw_called_ = true;
if (will_draw_returns_false_)
return false;
return TiledLayerImpl::WillDraw(draw_mode, provider);
}
virtual void AppendQuads(QuadSink* quad_sink,
AppendQuadsData* append_quads_data) OVERRIDE {
append_quads_called_ = true;
TiledLayerImpl::AppendQuads(quad_sink, append_quads_data);
}
virtual void DidDraw(ResourceProvider* provider) OVERRIDE {
did_draw_called_ = true;
TiledLayerImpl::DidDraw(provider);
}
bool will_draw_called() const { return will_draw_called_; }
bool append_quads_called() const { return append_quads_called_; }
bool did_draw_called() const { return did_draw_called_; }
void set_will_draw_returns_false() { will_draw_returns_false_ = true; }
void ClearDidDrawCheck() {
will_draw_called_ = false;
append_quads_called_ = false;
did_draw_called_ = false;
}
protected:
DidDrawCheckLayer(LayerTreeImpl* tree_impl, int id)
: TiledLayerImpl(tree_impl, id),
will_draw_returns_false_(false),
will_draw_called_(false),
append_quads_called_(false),
did_draw_called_(false) {
SetAnchorPoint(gfx::PointF());
SetBounds(gfx::Size(10, 10));
SetContentBounds(gfx::Size(10, 10));
SetDrawsContent(true);
set_skips_draw(false);
draw_properties().visible_content_rect = gfx::Rect(0, 0, 10, 10);
scoped_ptr<LayerTilingData> tiler =
LayerTilingData::Create(gfx::Size(100, 100),
LayerTilingData::HAS_BORDER_TEXELS);
tiler->SetBounds(content_bounds());
SetTilingData(*tiler.get());
}
private:
bool will_draw_returns_false_;
bool will_draw_called_;
bool append_quads_called_;
bool did_draw_called_;
};
TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 1));
DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(
host_impl_->active_tree()->root_layer());
root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 2));
DidDrawCheckLayer* layer =
static_cast<DidDrawCheckLayer*>(root->children()[0]);
{
LayerTreeHostImpl::FrameData frame;
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect(10, 10)));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(layer->will_draw_called());
EXPECT_TRUE(layer->append_quads_called());
EXPECT_TRUE(layer->did_draw_called());
}
{
LayerTreeHostImpl::FrameData frame;
layer->set_will_draw_returns_false();
layer->ClearDidDrawCheck();
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect(10, 10)));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(layer->will_draw_called());
EXPECT_FALSE(layer->append_quads_called());
EXPECT_FALSE(layer->did_draw_called());
}
}
TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 1));
DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(
host_impl_->active_tree()->root_layer());
root->SetMasksToBounds(true);
root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 2));
DidDrawCheckLayer* layer =
static_cast<DidDrawCheckLayer*>(root->children()[0]);
// Ensure visible_content_rect for layer is empty.
layer->SetPosition(gfx::PointF(100.f, 100.f));
layer->SetBounds(gfx::Size(10, 10));
layer->SetContentBounds(gfx::Size(10, 10));
LayerTreeHostImpl::FrameData frame;
EXPECT_FALSE(layer->will_draw_called());
EXPECT_FALSE(layer->did_draw_called());
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_FALSE(layer->will_draw_called());
EXPECT_FALSE(layer->did_draw_called());
EXPECT_TRUE(layer->visible_content_rect().IsEmpty());
// Ensure visible_content_rect for layer is not empty
layer->SetPosition(gfx::PointF());
EXPECT_FALSE(layer->will_draw_called());
EXPECT_FALSE(layer->did_draw_called());
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(layer->will_draw_called());
EXPECT_TRUE(layer->did_draw_called());
EXPECT_FALSE(layer->visible_content_rect().IsEmpty());
}
TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
gfx::Size big_size(1000, 1000);
host_impl_->SetViewportSize(big_size);
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 1));
DidDrawCheckLayer* root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 2));
DidDrawCheckLayer* occluded_layer =
static_cast<DidDrawCheckLayer*>(root->children()[0]);
root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 3));
DidDrawCheckLayer* top_layer =
static_cast<DidDrawCheckLayer*>(root->children()[1]);
// This layer covers the occluded_layer above. Make this layer large so it can
// occlude.
top_layer->SetBounds(big_size);
top_layer->SetContentBounds(big_size);
top_layer->SetContentsOpaque(true);
LayerTreeHostImpl::FrameData frame;
EXPECT_FALSE(occluded_layer->will_draw_called());
EXPECT_FALSE(occluded_layer->did_draw_called());
EXPECT_FALSE(top_layer->will_draw_called());
EXPECT_FALSE(top_layer->did_draw_called());
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_FALSE(occluded_layer->will_draw_called());
EXPECT_FALSE(occluded_layer->did_draw_called());
EXPECT_TRUE(top_layer->will_draw_called());
EXPECT_TRUE(top_layer->did_draw_called());
}
TEST_F(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 1));
DidDrawCheckLayer* root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 2));
DidDrawCheckLayer* layer1 =
static_cast<DidDrawCheckLayer*>(root->children()[0]);
layer1->AddChild(DidDrawCheckLayer::Create(host_impl_->active_tree(), 3));
DidDrawCheckLayer* layer2 =
static_cast<DidDrawCheckLayer*>(layer1->children()[0]);
layer1->SetOpacity(0.3f);
layer1->SetPreserves3d(false);
EXPECT_FALSE(root->did_draw_called());
EXPECT_FALSE(layer1->did_draw_called());
EXPECT_FALSE(layer2->did_draw_called());
LayerTreeHostImpl::FrameData frame;
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_TRUE(root->did_draw_called());
EXPECT_TRUE(layer1->did_draw_called());
EXPECT_TRUE(layer2->did_draw_called());
EXPECT_NE(root->render_surface(), layer1->render_surface());
EXPECT_TRUE(!!layer1->render_surface());
}
class MissingTextureAnimatingLayer : public DidDrawCheckLayer {
public:
static scoped_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl,
int id,
bool tile_missing,
bool skips_draw,
bool animating,
ResourceProvider* resource_provider) {
return scoped_ptr<LayerImpl>(new MissingTextureAnimatingLayer(
tree_impl,
id,
tile_missing,
skips_draw,
animating,
resource_provider));
}
private:
MissingTextureAnimatingLayer(LayerTreeImpl* tree_impl,
int id,
bool tile_missing,
bool skips_draw,
bool animating,
ResourceProvider* resource_provider)
: DidDrawCheckLayer(tree_impl, id) {
scoped_ptr<LayerTilingData> tiling_data =
LayerTilingData::Create(gfx::Size(10, 10),
LayerTilingData::NO_BORDER_TEXELS);
tiling_data->SetBounds(bounds());
SetTilingData(*tiling_data.get());
set_skips_draw(skips_draw);
if (!tile_missing) {
ResourceProvider::ResourceId resource =
resource_provider->CreateResource(gfx::Size(1, 1),
GL_RGBA,
ResourceProvider::TextureUsageAny);
resource_provider->AllocateForTesting(resource);
PushTileProperties(0, 0, resource, gfx::Rect(), false);
}
if (animating)
AddAnimatedTransformToLayer(this, 10.0, 3, 0);
}
};
TEST_F(LayerTreeHostImplTest, PrepareToDrawFailsWhenAnimationUsesCheckerboard) {
// When the texture is not missing, we draw as usual.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 1));
DidDrawCheckLayer* root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(
MissingTextureAnimatingLayer::Create(host_impl_->active_tree(),
2,
false,
false,
true,
host_impl_->resource_provider()));
LayerTreeHostImpl::FrameData frame;
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
// When a texture is missing and we're not animating, we draw as usual with
// checkerboarding.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 3));
root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(
MissingTextureAnimatingLayer::Create(host_impl_->active_tree(),
4,
true,
false,
false,
host_impl_->resource_provider()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
// When a texture is missing and we're animating, we don't want to draw
// anything.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 5));
root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(
MissingTextureAnimatingLayer::Create(host_impl_->active_tree(),
6,
true,
false,
true,
host_impl_->resource_provider()));
EXPECT_FALSE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
// When the layer skips draw and we're animating, we still draw the frame.
host_impl_->active_tree()->SetRootLayer(
DidDrawCheckLayer::Create(host_impl_->active_tree(), 7));
root =
static_cast<DidDrawCheckLayer*>(host_impl_->active_tree()->root_layer());
root->AddChild(
MissingTextureAnimatingLayer::Create(host_impl_->active_tree(),
8,
false,
true,
true,
host_impl_->resource_provider()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
}
TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetScrollable(false);
host_impl_->active_tree()->SetRootLayer(root.Pass());
InitializeRendererAndDrawFrame();
// Scroll event is ignored because layer is not scrollable.
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollNonScrollableRootWithTopControls) {
LayerTreeSettings settings;
settings.calculate_top_controls_position = true;
settings.top_controls_height = 50;
host_impl_ = LayerTreeHostImpl::Create(settings,
this,
&proxy_,
&stats_instrumentation_);
host_impl_->InitializeRenderer(CreateOutputSurface());
host_impl_->SetViewportSize(gfx::Size(10, 10));
gfx::Size layer_size(5, 5);
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetScrollable(true);
root->SetMaxScrollOffset(gfx::Vector2d(layer_size.width(),
layer_size.height()));
root->SetBounds(layer_size);
root->SetContentBounds(layer_size);
root->SetPosition(gfx::PointF());
root->SetAnchorPoint(gfx::PointF());
root->SetDrawsContent(false);
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->FindRootScrollLayer();
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
host_impl_->top_controls_manager()->ScrollBegin();
host_impl_->top_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 50.f));
host_impl_->top_controls_manager()->ScrollEnd();
EXPECT_EQ(host_impl_->top_controls_manager()->content_top_offset(), 0.f);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Gesture));
}
TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
// Test the configuration where a non-composited root layer is embedded in a
// scrollable outer layer.
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> content_layer =
LayerImpl::Create(host_impl_->active_tree(), 1);
content_layer->SetDrawsContent(true);
content_layer->SetPosition(gfx::PointF());
content_layer->SetAnchorPoint(gfx::PointF());
content_layer->SetBounds(surface_size);
content_layer->SetContentBounds(gfx::Size(surface_size.width() * 2,
surface_size.height() * 2));
content_layer->SetContentsScale(2.f, 2.f);
scoped_ptr<LayerImpl> scroll_layer =
LayerImpl::Create(host_impl_->active_tree(), 2);
scroll_layer->SetScrollable(true);
scroll_layer->SetMaxScrollOffset(gfx::Vector2d(surface_size.width(),
surface_size.height()));
scroll_layer->SetBounds(surface_size);
scroll_layer->SetContentBounds(surface_size);
scroll_layer->SetPosition(gfx::PointF());
scroll_layer->SetAnchorPoint(gfx::PointF());
scroll_layer->AddChild(content_layer.Pass());
host_impl_->active_tree()->SetRootLayer(scroll_layer.Pass());
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
host_impl_->ScrollEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetBounds(surface_size);
root->SetContentBounds(surface_size);
root->AddChild(CreateScrollableLayer(2, surface_size));
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
host_impl_->ScrollEnd();
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollMissesChild) {
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
root->AddChild(CreateScrollableLayer(2, surface_size));
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
// Scroll event is ignored because the input coordinate is outside the layer
// boundaries.
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(15, 5),
InputHandler::Wheel));
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
host_impl_->SetViewportSize(surface_size);
gfx::Transform matrix;
matrix.RotateAboutXAxis(180.0);
child->SetTransform(matrix);
child->SetDoubleSided(false);
root->AddChild(child.Pass());
host_impl_->active_tree()->SetRootLayer(root.Pass());
InitializeRendererAndDrawFrame();
// Scroll event is ignored because the scrollable layer is not facing the
// viewer and there is nothing scrollable behind it.
EXPECT_EQ(InputHandler::ScrollIgnored,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> content_layer = CreateScrollableLayer(1, surface_size);
content_layer->SetShouldScrollOnMainThread(true);
content_layer->SetScrollable(false);
scoped_ptr<LayerImpl> scroll_layer = CreateScrollableLayer(2, surface_size);
scroll_layer->AddChild(content_layer.Pass());
host_impl_->active_tree()->SetRootLayer(scroll_layer.Pass());
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
// Scrolling fails because the content layer is asking to be scrolled on the
// main thread.
EXPECT_EQ(InputHandler::ScrollOnMainThread,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
gfx::Size surface_size(10, 10);
float page_scale = 2.f;
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2d expected_scroll_delta = scroll_delta;
gfx::Vector2d expected_max_scroll =
host_impl_->active_tree()->root_layer()->max_scroll_offset();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
// Set new page scale from main thread.
host_impl_->active_tree()->SetPageScaleFactorAndLimits(page_scale,
page_scale,
page_scale);
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
host_impl_->active_tree()->root_layer()->id(),
expected_scroll_delta);
// The scroll range should also have been updated.
EXPECT_EQ(expected_max_scroll,
host_impl_->active_tree()->root_layer()->max_scroll_offset());
// The page scale delta remains constant because the impl thread did not
// scale.
EXPECT_EQ(1.f, host_impl_->active_tree()->page_scale_delta());
}
TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
gfx::Size surface_size(10, 10);
float page_scale = 2.f;
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 1.f, page_scale);
InitializeRendererAndDrawFrame();
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2d expected_scroll_delta = scroll_delta;
gfx::Vector2d expected_max_scroll =
host_impl_->active_tree()->root_layer()->max_scroll_offset();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
// Set new page scale on impl thread by pinching.
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(page_scale, gfx::Point());
host_impl_->PinchGestureEnd();
DrawOneFrame();
// The scroll delta is not scaled because the main thread did not scale.
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
host_impl_->active_tree()->root_layer()->id(),
expected_scroll_delta);
// The scroll range should also have been updated.
EXPECT_EQ(expected_max_scroll,
host_impl_->active_tree()->root_layer()->max_scroll_offset());
// The page scale delta should match the new scale on the impl side.
EXPECT_EQ(page_scale, host_impl_->active_tree()->total_page_scale_factor());
}
TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
gfx::Size surface_size(10, 10);
float default_page_scale = 1.f;
gfx::Transform default_page_scale_matrix;
default_page_scale_matrix.Scale(default_page_scale, default_page_scale);
float new_page_scale = 2.f;
gfx::Transform new_page_scale_matrix;
new_page_scale_matrix.Scale(new_page_scale, new_page_scale);
// Create a normal scrollable root layer and another scrollable child layer.
LayerImpl* scroll = SetupScrollAndContentsLayers(surface_size);
LayerImpl* root = host_impl_->active_tree()->root_layer();
LayerImpl* child = scroll->children()[0];
scoped_ptr<LayerImpl> scrollable_child =
CreateScrollableLayer(4, surface_size);
child->AddChild(scrollable_child.Pass());
LayerImpl* grand_child = child->children()[0];
// Set new page scale on impl thread by pinching.
host_impl_->PinchGestureBegin();
host_impl_->PinchGestureUpdate(new_page_scale, gfx::Point());
host_impl_->PinchGestureEnd();
DrawOneFrame();
EXPECT_EQ(1.f, root->contents_scale_x());
EXPECT_EQ(1.f, root->contents_scale_y());
EXPECT_EQ(1.f, scroll->contents_scale_x());
EXPECT_EQ(1.f, scroll->contents_scale_y());
EXPECT_EQ(1.f, child->contents_scale_x());
EXPECT_EQ(1.f, child->contents_scale_y());
EXPECT_EQ(1.f, grand_child->contents_scale_x());
EXPECT_EQ(1.f, grand_child->contents_scale_y());
// Make sure all the layers are drawn with the page scale delta applied, i.e.,
// the page scale delta on the root layer is applied hierarchically.
LayerTreeHostImpl::FrameData frame;
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
host_impl_->DidDrawAllLayers(frame);
EXPECT_EQ(1.f, root->draw_transform().matrix().getDouble(0, 0));
EXPECT_EQ(1.f, root->draw_transform().matrix().getDouble(1, 1));
EXPECT_EQ(new_page_scale, scroll->draw_transform().matrix().getDouble(0, 0));
EXPECT_EQ(new_page_scale, scroll->draw_transform().matrix().getDouble(1, 1));
EXPECT_EQ(new_page_scale, child->draw_transform().matrix().getDouble(0, 0));
EXPECT_EQ(new_page_scale, child->draw_transform().matrix().getDouble(1, 1));
EXPECT_EQ(new_page_scale,
grand_child->draw_transform().matrix().getDouble(0, 0));
EXPECT_EQ(new_page_scale,
grand_child->draw_transform().matrix().getDouble(1, 1));
}
TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetBounds(surface_size);
root->SetContentBounds(surface_size);
// Also mark the root scrollable so it becomes the root scroll layer.
root->SetScrollable(true);
int scroll_layer_id = 2;
root->AddChild(CreateScrollableLayer(scroll_layer_id, surface_size));
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
LayerImpl* child = host_impl_->active_tree()->root_layer()->children()[0];
gfx::Vector2d scroll_delta(0, 10);
gfx::Vector2d expected_scroll_delta(scroll_delta);
gfx::Vector2d expected_max_scroll(child->max_scroll_offset());
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
float page_scale = 2.f;
host_impl_->active_tree()->SetPageScaleFactorAndLimits(page_scale,
1.f,
page_scale);
DrawOneFrame();
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(), scroll_layer_id, expected_scroll_delta);
// The scroll range should not have changed.
EXPECT_EQ(child->max_scroll_offset(), expected_max_scroll);
// The page scale delta remains constant because the impl thread did not
// scale.
EXPECT_EQ(1.f, host_impl_->active_tree()->page_scale_delta());
}
TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// parent layer is scrolled on the axis on which the child was unable to
// scroll.
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size);
grand_child->SetScrollOffset(gfx::Vector2d(0, 5));
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
child->SetScrollOffset(gfx::Vector2d(3, 0));
child->AddChild(grand_child.Pass());
root->AddChild(child.Pass());
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
{
gfx::Vector2d scroll_delta(-8, -7);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
// The grand child should have scrolled up to its limit.
LayerImpl* child = host_impl_->active_tree()->root_layer()->children()[0];
LayerImpl* grand_child = child->children()[0];
ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, -5));
// The child should have only scrolled on the other axis.
ExpectContains(*scroll_info.get(), child->id(), gfx::Vector2d(-3, 0));
}
}
TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// the scroll doesn't bubble up to the parent layer.
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size);
grand_child->SetScrollOffset(gfx::Vector2d(0, 2));
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
child->SetScrollOffset(gfx::Vector2d(0, 3));
child->AddChild(grand_child.Pass());
root->AddChild(child.Pass());
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::NonBubblingGesture));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
// The grand child should have scrolled up to its limit.
LayerImpl* child = host_impl_->active_tree()->root_layer()->children()[0];
LayerImpl* grand_child = child->children()[0];
ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, -2));
// The child should not have scrolled.
ExpectNone(*scroll_info.get(), child->id());
// The next time we scroll we should only scroll the parent.
scroll_delta = gfx::Vector2d(0, -3);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::NonBubblingGesture));
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
host_impl_->ScrollEnd();
scroll_info = host_impl_->ProcessScrollDeltas();
// The child should have scrolled up to its limit.
ExpectContains(*scroll_info.get(), child->id(), gfx::Vector2d(0, -3));
// The grand child should not have scrolled.
ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, -2));
// After scrolling the parent, another scroll on the opposite direction
// should still scroll the child.
scroll_delta = gfx::Vector2d(0, 7);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::NonBubblingGesture));
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
host_impl_->ScrollEnd();
scroll_info = host_impl_->ProcessScrollDeltas();
// The grand child should have scrolled.
ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, 5));
// The child should not have scrolled.
ExpectContains(*scroll_info.get(), child->id(), gfx::Vector2d(0, -3));
// Scrolling should be adjusted from viewport space.
host_impl_->active_tree()->SetPageScaleFactorAndLimits(2.f, 2.f, 2.f);
host_impl_->active_tree()->SetPageScaleDelta(1.f);
scroll_delta = gfx::Vector2d(0, -2);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(1, 1),
InputHandler::NonBubblingGesture));
EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer());
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scroll_info = host_impl_->ProcessScrollDeltas();
// Should have scrolled by half the amount in layer space (5 - 2/2)
ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, 4));
}
}
TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible.
gfx::Size surface_size(10, 10);
gfx::Size content_size(20, 20);
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
child->SetScrollable(false);
root->AddChild(child.Pass());
host_impl_->SetViewportSize(surface_size);
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
InitializeRendererAndDrawFrame();
{
gfx::Vector2d scroll_delta(0, 4);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
// Only the root should have scrolled.
ASSERT_EQ(scroll_info->scrolls.size(), 1u);
ExpectContains(*scroll_info.get(),
host_impl_->active_tree()->root_layer()->id(),
scroll_delta);
}
}
TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
gfx::Size surface_size(10, 10);
host_impl_->active_tree()->SetRootLayer(
CreateScrollableLayer(1, surface_size));
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
// Draw one frame and then immediately rebuild the layer tree to mimic a tree
// synchronization.
InitializeRendererAndDrawFrame();
host_impl_->active_tree()->DetachLayerTree();
host_impl_->active_tree()->SetRootLayer(
CreateScrollableLayer(2, surface_size));
host_impl_->active_tree()->DidBecomeActive();
// Scrolling should still work even though we did not draw yet.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
}
TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
// Rotate the root layer 90 degrees counter-clockwise about its center.
gfx::Transform rotate_transform;
rotate_transform.Rotate(-90.0);
host_impl_->active_tree()->root_layer()->SetTransform(rotate_transform);
gfx::Size surface_size(50, 50);
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
// Scroll to the right in screen coordinates with a gesture.
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
host_impl_->ScrollEnd();
// The layer should have scrolled down in its local coordinates.
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
gfx::Vector2d(0, gesture_scroll_delta.x()));
// Reset and scroll down with the wheel.
scroll_layer->SetScrollDelta(gfx::Vector2dF());
gfx::Vector2d wheel_scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
host_impl_->ScrollEnd();
// The layer should have scrolled down in its local coordinates.
scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
wheel_scroll_delta);
}
TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
int child_layer_id = 4;
float child_layer_angle = -20.f;
// Create a child layer that is rotated to a non-axis-aligned angle.
scoped_ptr<LayerImpl> child = CreateScrollableLayer(
child_layer_id,
scroll_layer->content_bounds());
gfx::Transform rotate_transform;
rotate_transform.Translate(-50.0, -50.0);
rotate_transform.Rotate(child_layer_angle);
rotate_transform.Translate(50.0, 50.0);
child->SetTransform(rotate_transform);
// Only allow vertical scrolling.
child->SetMaxScrollOffset(gfx::Vector2d(0, child->content_bounds().height()));
scroll_layer->AddChild(child.Pass());
gfx::Size surface_size(50, 50);
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
{
// Scroll down in screen coordinates with a gesture.
gfx::Vector2d gesture_scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
host_impl_->ScrollEnd();
// The child layer should have scrolled down in its local coordinates an
// amount proportional to the angle between it and the input scroll delta.
gfx::Vector2d expected_scroll_delta(
0,
gesture_scroll_delta.y() *
std::cos(MathUtil::Deg2Rad(child_layer_angle)));
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(), child_layer_id, expected_scroll_delta);
// The root scroll layer should not have scrolled, because the input delta
// was close to the layer's axis of movement.
EXPECT_EQ(scroll_info->scrolls.size(), 1u);
}
{
// Now reset and scroll the same amount horizontally.
scroll_layer->children()[1]->SetScrollDelta(
gfx::Vector2dF());
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
host_impl_->ScrollEnd();
// The child layer should have scrolled down in its local coordinates an
// amount proportional to the angle between it and the input scroll delta.
gfx::Vector2d expected_scroll_delta(
0,
-gesture_scroll_delta.x() *
std::sin(MathUtil::Deg2Rad(child_layer_angle)));
scoped_ptr<ScrollAndScaleSet> scroll_info =
host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(), child_layer_id, expected_scroll_delta);
// The root scroll layer should have scrolled more, since the input scroll
// delta was mostly orthogonal to the child layer's vertical scroll axis.
gfx::Vector2d expected_root_scroll_delta(
gesture_scroll_delta.x() *
std::pow(std::cos(MathUtil::Deg2Rad(child_layer_angle)), 2),
0);
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
expected_root_scroll_delta);
}
}
TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
LayerImpl* scroll_layer =
SetupScrollAndContentsLayers(gfx::Size(100, 100));
// Scale the layer to twice its normal size.
int scale = 2;
gfx::Transform scale_transform;
scale_transform.Scale(scale, scale);
scroll_layer->SetTransform(scale_transform);
gfx::Size surface_size(50, 50);
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
// Scroll down in screen coordinates with a gesture.
gfx::Vector2d scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
host_impl_->ScrollEnd();
// The layer should have scrolled down in its local coordinates, but half the
// amount.
scoped_ptr<ScrollAndScaleSet> scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
gfx::Vector2d(0, scroll_delta.y() / scale));
// Reset and scroll down with the wheel.
scroll_layer->SetScrollDelta(gfx::Vector2dF());
gfx::Vector2d wheel_scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
host_impl_->ScrollEnd();
// The scale should not have been applied to the scroll delta.
scroll_info = host_impl_->ProcessScrollDeltas();
ExpectContains(*scroll_info.get(),
scroll_layer->id(),
wheel_scroll_delta);
}
class TestScrollOffsetDelegate : public LayerScrollOffsetDelegate {
public:
TestScrollOffsetDelegate() {}
virtual ~TestScrollOffsetDelegate() {}
virtual void SetTotalScrollOffset(gfx::Vector2dF new_value) OVERRIDE {
last_set_scroll_offset_ = new_value;
}
virtual gfx::Vector2dF GetTotalScrollOffset() OVERRIDE {
return getter_return_value_;
}
gfx::Vector2dF last_set_scroll_offset() {
return last_set_scroll_offset_;
}
void set_getter_return_value(gfx::Vector2dF value) {
getter_return_value_ = value;
}
private:
gfx::Vector2dF last_set_scroll_offset_;
gfx::Vector2dF getter_return_value_;
};
TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
TestScrollOffsetDelegate scroll_delegate;
LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
// Setting the delegate results in the current scroll offset being set.
gfx::Vector2dF initial_scroll_delta(10.f, 10.f);
scroll_layer->SetScrollOffset(gfx::Vector2d());
scroll_layer->SetScrollDelta(initial_scroll_delta);
host_impl_->SetRootLayerScrollOffsetDelegate(&scroll_delegate);
EXPECT_EQ(initial_scroll_delta.ToString(),
scroll_delegate.last_set_scroll_offset().ToString());
// Scrolling should be relative to the offset as returned by the delegate.
gfx::Vector2dF scroll_delta(0.f, 10.f);
gfx::Vector2dF current_offset(7.f, 8.f);
scroll_delegate.set_getter_return_value(current_offset);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(current_offset + scroll_delta,
scroll_delegate.last_set_scroll_offset());
current_offset = gfx::Vector2dF(42.f, 41.f);
scroll_delegate.set_getter_return_value(current_offset);
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(current_offset + scroll_delta,
scroll_delegate.last_set_scroll_offset());
host_impl_->ScrollEnd();
// Un-setting the delegate should propagate the delegate's current offset to
// the root scrollable layer.
current_offset = gfx::Vector2dF(13.f, 12.f);
scroll_delegate.set_getter_return_value(current_offset);
host_impl_->SetRootLayerScrollOffsetDelegate(NULL);
EXPECT_EQ(current_offset.ToString(),
scroll_layer->TotalScrollOffset().ToString());
}
TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
SetupScrollAndContentsLayers(gfx::Size(100, 100));
host_impl_->SetViewportSize(gfx::Size(50, 50));
host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f);
InitializeRendererAndDrawFrame();
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
EXPECT_EQ(gfx::Vector2dF(), host_impl_->current_fling_velocity());
// In-bounds scrolling does not affect overscroll.
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
EXPECT_EQ(gfx::Vector2dF(), host_impl_->current_fling_velocity());
// Overscroll events are reflected immediately.
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 50));
EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
EXPECT_EQ(gfx::Vector2dF(), host_impl_->current_fling_velocity());
// In-bounds scrolling resets accumulated overscroll.
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -50));
EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -10));
EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
// Overscroll accumulates within the scope of ScrollBegin/ScrollEnd as long
// as no scroll occurs.
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20));
EXPECT_EQ(gfx::Vector2dF(0, -30), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20));
EXPECT_EQ(gfx::Vector2dF(0, -50), host_impl_->accumulated_root_overscroll());
// Overscroll resets on valid scroll.
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20));
EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollEnd();
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
// Fling velocity is reflected immediately.
host_impl_->NotifyCurrentFlingVelocity(gfx::Vector2dF(10, 0));
EXPECT_EQ(gfx::Vector2dF(10, 0), host_impl_->current_fling_velocity());
host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, -20));
EXPECT_EQ(gfx::Vector2dF(0, -20), host_impl_->accumulated_root_overscroll());
EXPECT_EQ(gfx::Vector2dF(10, 0), host_impl_->current_fling_velocity());
}
TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
// Scroll child layers beyond their maximum scroll range and make sure root
// overscroll does not accumulate.
gfx::Size surface_size(10, 10);
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size);
grand_child->SetScrollOffset(gfx::Vector2d(0, 2));
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
child->SetScrollOffset(gfx::Vector2d(0, 3));
child->AddChild(grand_child.Pass());
root->AddChild(child.Pass());
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
host_impl_->SetViewportSize(surface_size);
InitializeRendererAndDrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(),
InputHandler::NonBubblingGesture));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollEnd();
LayerImpl* child = host_impl_->active_tree()->root_layer()->children()[0];
LayerImpl* grand_child = child->children()[0];
// The next time we scroll we should only scroll the parent, but overscroll
// should still not reach the root layer.
scroll_delta = gfx::Vector2d(0, -30);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::NonBubblingGesture));
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollEnd();
// After scrolling the parent, another scroll on the opposite direction
// should scroll the child, resetting the fling velocity.
scroll_delta = gfx::Vector2d(0, 70);
host_impl_->NotifyCurrentFlingVelocity(gfx::Vector2dF(10, 0));
EXPECT_EQ(gfx::Vector2dF(10, 0), host_impl_->current_fling_velocity());
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::NonBubblingGesture));
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
EXPECT_EQ(gfx::Vector2dF(), host_impl_->current_fling_velocity());
host_impl_->ScrollEnd();
}
}
TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible. Overscroll should
// be reflected only when it has bubbled up to the root scrolling layer.
gfx::Size surface_size(10, 10);
gfx::Size content_size(20, 20);
scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
child->SetScrollable(false);
root->AddChild(child.Pass());
host_impl_->SetViewportSize(surface_size);
host_impl_->active_tree()->SetRootLayer(root.Pass());
host_impl_->active_tree()->DidBecomeActive();
InitializeRendererAndDrawFrame();
{
gfx::Vector2d scroll_delta(0, 8);
EXPECT_EQ(InputHandler::ScrollStarted,
host_impl_->ScrollBegin(gfx::Point(5, 5),
InputHandler::Wheel));
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, 6), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollBy(gfx::Point(), scroll_delta);
EXPECT_EQ(gfx::Vector2dF(0, 14), host_impl_->accumulated_root_overscroll());
host_impl_->ScrollEnd();
}
}
class BlendStateTrackerContext: public TestWebGraphicsContext3D {
public:
BlendStateTrackerContext() : blend_(false) {}
virtual void enable(WebKit::WGC3Denum cap) OVERRIDE {
if (cap == GL_BLEND)
blend_ = true;
}
virtual void disable(WebKit::WGC3Denum cap) OVERRIDE {
if (cap == GL_BLEND)
blend_ = false;
}
bool blend() const { return blend_; }
private:
bool blend_;
};
class BlendStateCheckLayer : public LayerImpl {
public:
static scoped_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl,
int id,
ResourceProvider* resource_provider) {
return scoped_ptr<LayerImpl>(new BlendStateCheckLayer(tree_impl,
id,
resource_provider));
}
virtual void AppendQuads(QuadSink* quad_sink,
AppendQuadsData* append_quads_data) OVERRIDE {
quads_appended_ = true;
gfx::Rect opaque_rect;
if (contents_opaque())
opaque_rect = quad_rect_;
else
opaque_rect = opaque_content_rect_;
SharedQuadState* shared_quad_state =
quad_sink->UseSharedQuadState(CreateSharedQuadState());
scoped_ptr<TileDrawQuad> test_blending_draw_quad = TileDrawQuad::Create();
test_blending_draw_quad->SetNew(shared_quad_state,
quad_rect_,
opaque_rect,
resource_id_,
gfx::RectF(0.f, 0.f, 1.f, 1.f),
gfx::Size(1, 1),
false);
test_blending_draw_quad->visible_rect = quad_visible_rect_;
EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending());
EXPECT_EQ(has_render_surface_, !!render_surface());
quad_sink->Append(test_blending_draw_quad.PassAs<DrawQuad>(),
append_quads_data);
}
void SetExpectation(bool blend, bool has_render_surface) {
blend_ = blend;
has_render_surface_ = has_render_surface;
quads_appended_ = false;
}
bool quads_appended() const { return quads_appended_; }
void SetQuadRect(gfx::Rect rect) { quad_rect_ = rect; }
void SetQuadVisibleRect(gfx::Rect rect) { quad_visible_rect_ = rect; }
void SetOpaqueContentRect(gfx::Rect rect) { opaque_content_rect_ = rect; }
private:
BlendStateCheckLayer(LayerTreeImpl* tree_impl,
int id,
ResourceProvider* resource_provider)
: LayerImpl(tree_impl, id),
blend_(false),
has_render_surface_(false),
quads_appended_(false),
quad_rect_(5, 5, 5, 5),
quad_visible_rect_(5, 5, 5, 5),
resource_id_(resource_provider->CreateResource(
gfx::Size(1, 1),
GL_RGBA,
ResourceProvider::TextureUsageAny)) {
resource_provider->AllocateForTesting(resource_id_);
SetAnchorPoint(gfx::PointF());
SetBounds(gfx::Size(10, 10));
SetContentBounds(gfx::Size(10, 10));
SetDrawsContent(true);
}
bool blend_;
bool has_render_surface_;
bool quads_appended_;
gfx::Rect quad_rect_;
gfx::Rect opaque_content_rect_;
gfx::Rect quad_visible_rect_;
ResourceProvider::ResourceId resource_id_;
};
TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
{
scoped_ptr<LayerImpl> root =
LayerImpl::Create(host_impl_->active_tree(), 1);
root->SetAnchorPoint(gfx::PointF());
root->SetBounds(gfx::Size(10, 10));
root->SetContentBounds(root->bounds());
root->SetDrawsContent(false);
host_impl_->active_tree()->SetRootLayer(root.Pass());
}
LayerImpl* root = host_impl_->active_tree()->root_layer();
root->AddChild(
BlendStateCheckLayer::Create(host_impl_->active_tree(),
2,
host_impl_->resource_provider()));
BlendStateCheckLayer* layer1 =
static_cast<BlendStateCheckLayer*>(root->children()[0]);
layer1->SetPosition(gfx::PointF(2.f, 2.f));
LayerTreeHostImpl::FrameData frame;
// Opaque layer, drawn without blending.
layer1->SetContentsOpaque(true);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with translucent content and painting, so drawn with blending.
layer1->SetContentsOpaque(false);
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with translucent opacity, drawn with blending.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(0.5f);
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with translucent opacity and painting, drawn with blending.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(0.5f);
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
layer1->AddChild(
BlendStateCheckLayer::Create(host_impl_->active_tree(),
3,
host_impl_->resource_provider()));
BlendStateCheckLayer* layer2 =
static_cast<BlendStateCheckLayer*>(layer1->children()[0]);
layer2->SetPosition(gfx::PointF(4.f, 4.f));
// 2 opaque layers, drawn without blending.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(1.f);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetContentsOpaque(true);
layer2->SetOpacity(1.f);
layer2->SetExpectation(false, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Parent layer with translucent content, drawn with blending.
// Child layer with opaque content, drawn without blending.
layer1->SetContentsOpaque(false);
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetExpectation(false, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Parent layer with translucent content but opaque painting, drawn without
// blending.
// Child layer with opaque content, drawn without blending.
layer1->SetContentsOpaque(true);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetExpectation(false, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Parent layer with translucent opacity and opaque content. Since it has a
// drawing child, it's drawn to a render surface which carries the opacity,
// so it's itself drawn without blending.
// Child layer with opaque content, drawn without blending (parent surface
// carries the inherited opacity).
layer1->SetContentsOpaque(true);
layer1->SetOpacity(0.5f);
layer1->SetExpectation(false, true);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetExpectation(false, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Draw again, but with child non-opaque, to make sure
// layer1 not culled.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(1.f);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetContentsOpaque(true);
layer2->SetOpacity(0.5f);
layer2->SetExpectation(true, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// A second way of making the child non-opaque.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(1.f);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetContentsOpaque(false);
layer2->SetOpacity(1.f);
layer2->SetExpectation(true, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// And when the layer says its not opaque but is painted opaque, it is not
// blended.
layer1->SetContentsOpaque(true);
layer1->SetOpacity(1.f);
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
layer2->SetContentsOpaque(true);
layer2->SetOpacity(1.f);
layer2->SetExpectation(false, false);
layer2->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
EXPECT_TRUE(layer2->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with partially opaque contents, drawn with blending.
layer1->SetContentsOpaque(false);
layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5));
layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 5, 5));
layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5));
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with partially opaque contents partially culled, drawn with blending.
layer1->SetContentsOpaque(false);
layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5));
layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 5, 2));
layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5));
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with partially opaque contents culled, drawn with blending.
layer1->SetContentsOpaque(false);
layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5));
layer1->SetQuadVisibleRect(gfx::Rect(7, 5, 3, 5));
layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5));
layer1->SetExpectation(true, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
// Layer with partially opaque contents and translucent contents culled, drawn
// without blending.
layer1->SetContentsOpaque(false);
layer1->SetQuadRect(gfx::Rect(5, 5, 5, 5));
layer1->SetQuadVisibleRect(gfx::Rect(5, 5, 2, 5));
layer1->SetOpaqueContentRect(gfx::Rect(5, 5, 2, 5));
layer1->SetExpectation(false, false);
layer1->set_update_rect(gfx::RectF(layer1->content_bounds()));
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
EXPECT_TRUE(layer1->quads_appended());
host_impl_->DidDrawAllLayers(frame);
}
TEST_F(LayerTreeHostImplTest, ViewportCovered) {
host_impl_->InitializeRenderer(CreateOutputSurface());
host_impl_->active_tree()->set_background_color(SK_ColorGRAY);
gfx::Size viewport_size(1000, 1000);
host_impl_->SetViewportSize(viewport_size);
host_impl_->active_tree()->SetRootLayer(
LayerImpl::Create(host_impl_->active_tree(), 1