// Copyright 2012 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.h" #include "base/memory/weak_ptr.h" #include "cc/layers/content_layer.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" #include "cc/trees/layer_tree_impl.h" #include "ui/gfx/point_conversions.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/vector2d_conversions.h" namespace cc { namespace { class LayerTreeHostScrollTest : public LayerTreeTest {}; class LayerTreeHostScrollTestScrollSimple : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestScrollSimple() : initial_scroll_(10, 20), second_scroll_(40, 5), scroll_amount_(2, -1), num_scrolls_(0) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->root_layer()->SetScrollable(true); layer_tree_host()->root_layer() ->SetMaxScrollOffset(gfx::Vector2d(100, 100)); layer_tree_host()->root_layer()->SetScrollOffset(initial_scroll_); PostSetNeedsCommitToMainThread(); } virtual void Layout() OVERRIDE { Layer* root = layer_tree_host()->root_layer(); if (!layer_tree_host()->source_frame_number()) { EXPECT_VECTOR_EQ(initial_scroll_, root->scroll_offset()); } else { EXPECT_VECTOR_EQ(initial_scroll_ + scroll_amount_, root->scroll_offset()); // Pretend like Javascript updated the scroll position itself. root->SetScrollOffset(second_scroll_); } } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root = impl->active_tree()->root_layer(); EXPECT_VECTOR_EQ(gfx::Vector2d(), root->ScrollDelta()); root->SetScrollable(true); root->SetMaxScrollOffset(gfx::Vector2d(100, 100)); root->ScrollBy(scroll_amount_); switch (impl->active_tree()->source_frame_number()) { case 0: EXPECT_VECTOR_EQ(initial_scroll_, root->scroll_offset()); EXPECT_VECTOR_EQ(scroll_amount_, root->ScrollDelta()); PostSetNeedsCommitToMainThread(); break; case 1: EXPECT_VECTOR_EQ(root->scroll_offset(), second_scroll_); EXPECT_VECTOR_EQ(root->ScrollDelta(), scroll_amount_); EndTest(); break; } } virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) OVERRIDE { num_scrolls_++; } virtual void AfterTest() OVERRIDE { EXPECT_EQ(1, num_scrolls_); } private: gfx::Vector2d initial_scroll_; gfx::Vector2d second_scroll_; gfx::Vector2d scroll_amount_; int num_scrolls_; }; MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollSimple); class LayerTreeHostScrollTestScrollMultipleRedraw : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestScrollMultipleRedraw() : initial_scroll_(40, 10), scroll_amount_(-3, 17), num_scrolls_(0) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->root_layer()->SetScrollable(true); layer_tree_host()->root_layer()->SetScrollOffset(initial_scroll_); layer_tree_host()->root_layer()->SetBounds(gfx::Size(200, 200)); layer_tree_host()->root_layer() ->SetMaxScrollOffset(gfx::Vector2d(100, 100)); PostSetNeedsCommitToMainThread(); } virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE { Layer* root = layer_tree_host()->root_layer(); switch (layer_tree_host()->source_frame_number()) { case 0: EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); break; case 1: EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_ + scroll_amount_ + scroll_amount_); case 2: EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_ + scroll_amount_ + scroll_amount_); break; } } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root = impl->active_tree()->root_layer(); if (impl->active_tree()->source_frame_number() == 0 && impl->SourceAnimationFrameNumber() == 1) { // First draw after first commit. EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d()); root->ScrollBy(scroll_amount_); EXPECT_VECTOR_EQ(root->ScrollDelta(), scroll_amount_); EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); PostSetNeedsRedrawToMainThread(); } else if (impl->active_tree()->source_frame_number() == 0 && impl->SourceAnimationFrameNumber() == 2) { // Second draw after first commit. EXPECT_EQ(root->ScrollDelta(), scroll_amount_); root->ScrollBy(scroll_amount_); EXPECT_VECTOR_EQ(root->ScrollDelta(), scroll_amount_ + scroll_amount_); EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); PostSetNeedsCommitToMainThread(); } else if (impl->active_tree()->source_frame_number() == 1) { // Third or later draw after second commit. EXPECT_GE(impl->SourceAnimationFrameNumber(), 3); EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d()); EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_ + scroll_amount_ + scroll_amount_); EndTest(); } } virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) OVERRIDE { num_scrolls_++; } virtual void AfterTest() OVERRIDE { EXPECT_EQ(1, num_scrolls_); } private: gfx::Vector2d initial_scroll_; gfx::Vector2d scroll_amount_; int num_scrolls_; }; MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollMultipleRedraw); class LayerTreeHostScrollTestScrollAbortedCommit : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestScrollAbortedCommit() : initial_scroll_(50, 60), impl_scroll_(-3, 2), second_main_scroll_(14, -3), impl_scale_(2.f), num_will_begin_frames_(0), num_did_begin_frames_(0), num_will_commits_(0), num_did_commits_(0), num_impl_commits_(0), num_impl_scrolls_(0) {} virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void SetupTree() OVERRIDE { LayerTreeHostScrollTest::SetupTree(); scoped_refptr root_scroll_layer = Layer::Create(); root_scroll_layer->SetScrollable(true); root_scroll_layer->SetScrollOffset(initial_scroll_); root_scroll_layer->SetBounds(gfx::Size(200, 200)); root_scroll_layer->SetMaxScrollOffset(gfx::Vector2d(100, 100)); root_scroll_layer->SetIsDrawable(true); layer_tree_host()->root_layer()->AddChild(root_scroll_layer); layer_tree_host()->SetPageScaleFactorAndLimits(1.f, 0.01f, 100.f); } virtual void WillBeginFrame() OVERRIDE { num_will_begin_frames_++; Layer* root_scroll_layer = layer_tree_host()->root_layer()->children()[0]; switch (num_will_begin_frames_) { case 1: // This will not be aborted because of the initial prop changes. EXPECT_EQ(0, num_impl_scrolls_); EXPECT_EQ(0, layer_tree_host()->source_frame_number()); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_); EXPECT_EQ(1.f, layer_tree_host()->page_scale_factor()); break; case 2: // This commit will be aborted, and another commit will be // initiated from the redraw. EXPECT_EQ(1, num_impl_scrolls_); EXPECT_EQ(1, layer_tree_host()->source_frame_number()); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_); EXPECT_EQ(impl_scale_, layer_tree_host()->page_scale_factor()); PostSetNeedsRedrawToMainThread(); break; case 3: // This commit will not be aborted because of the scroll change. EXPECT_EQ(2, num_impl_scrolls_); EXPECT_EQ(1, layer_tree_host()->source_frame_number()); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_ + impl_scroll_); EXPECT_EQ(impl_scale_ * impl_scale_, layer_tree_host()->page_scale_factor()); root_scroll_layer->SetScrollOffset(root_scroll_layer->scroll_offset() + second_main_scroll_); break; case 4: // This commit will also be aborted. EXPECT_EQ(3, num_impl_scrolls_); EXPECT_EQ(2, layer_tree_host()->source_frame_number()); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_); // End the test by drawing to verify this commit is also aborted. PostSetNeedsRedrawToMainThread(); break; } } virtual void DidBeginFrame() OVERRIDE { num_did_begin_frames_++; } virtual void WillCommit() OVERRIDE { num_will_commits_++; } virtual void DidCommit() OVERRIDE { num_did_commits_++; } virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE { num_impl_commits_++; } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root_scroll_layer = impl->active_tree()->root_layer()->children()[0]; if (impl->active_tree()->source_frame_number() == 0 && impl->SourceAnimationFrameNumber() == 1) { // First draw EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); root_scroll_layer->ScrollBy(impl_scroll_); EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_); EXPECT_EQ(1.f, impl->active_tree()->page_scale_delta()); EXPECT_EQ(1.f, impl->active_tree()->total_page_scale_factor()); impl->active_tree()->SetPageScaleDelta(impl_scale_); EXPECT_EQ(impl_scale_, impl->active_tree()->page_scale_delta()); EXPECT_EQ(impl_scale_, impl->active_tree()->total_page_scale_factor()); // To simplify the testing flow, don't redraw here, just commit. impl->SetNeedsCommit(); } else if (impl->active_tree()->source_frame_number() == 0 && impl->SourceAnimationFrameNumber() == 2) { // Test a second draw after an aborted commit. // The scroll/scale values should be baked into the offset/scale factor // since the main thread consumed but aborted the begin frame. EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); root_scroll_layer->ScrollBy(impl_scroll_); EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_); EXPECT_EQ(1.f, impl->active_tree()->page_scale_delta()); EXPECT_EQ(impl_scale_, impl->active_tree()->total_page_scale_factor()); impl->active_tree()->SetPageScaleDelta(impl_scale_); EXPECT_EQ(impl_scale_, impl->active_tree()->page_scale_delta()); EXPECT_EQ(impl_scale_ * impl_scale_, impl->active_tree()->total_page_scale_factor()); impl->SetNeedsCommit(); } else if (impl->active_tree()->source_frame_number() == 1 && impl->SourceAnimationFrameNumber() == 3) { // Third draw after the second full commit. EXPECT_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); root_scroll_layer->ScrollBy(impl_scroll_); impl->SetNeedsCommit(); EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), impl_scroll_); EXPECT_VECTOR_EQ( root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_); } else if (impl->active_tree()->source_frame_number() == 1 && impl->SourceAnimationFrameNumber() == 4) { // Final draw after the second aborted commit. EXPECT_VECTOR_EQ(root_scroll_layer->ScrollDelta(), gfx::Vector2d()); EXPECT_VECTOR_EQ(root_scroll_layer->scroll_offset(), initial_scroll_ + impl_scroll_ + impl_scroll_ + impl_scroll_ + second_main_scroll_); EndTest(); } } virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) OVERRIDE { num_impl_scrolls_++; } virtual void AfterTest() OVERRIDE { EXPECT_EQ(3, num_impl_scrolls_); // Verify that the embedder sees aborted commits as real commits. EXPECT_EQ(4, num_will_begin_frames_); EXPECT_EQ(4, num_did_begin_frames_); EXPECT_EQ(4, num_will_commits_); EXPECT_EQ(4, num_did_commits_); // ...but the compositor thread only sees two real ones. EXPECT_EQ(2, num_impl_commits_); } private: gfx::Vector2d initial_scroll_; gfx::Vector2d impl_scroll_; gfx::Vector2d second_main_scroll_; float impl_scale_; int num_will_begin_frames_; int num_did_begin_frames_; int num_will_commits_; int num_did_commits_; int num_impl_commits_; int num_impl_scrolls_; }; MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollAbortedCommit); class LayerTreeHostScrollTestFractionalScroll : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestFractionalScroll() : scroll_amount_(1.75, 0) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->root_layer()->SetScrollable(true); layer_tree_host()->root_layer() ->SetMaxScrollOffset(gfx::Vector2d(100, 100)); PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root = impl->active_tree()->root_layer(); // Check that a fractional scroll delta is correctly accumulated over // multiple commits. switch (impl->active_tree()->source_frame_number()) { case 0: EXPECT_VECTOR_EQ(root->scroll_offset(), gfx::Vector2d(0, 0)); EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d(0, 0)); PostSetNeedsCommitToMainThread(); break; case 1: EXPECT_VECTOR_EQ(root->scroll_offset(), gfx::ToFlooredVector2d(scroll_amount_)); EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2dF(fmod(scroll_amount_.x(), 1.0f), 0.0f)); PostSetNeedsCommitToMainThread(); break; case 2: EXPECT_VECTOR_EQ( root->scroll_offset(), gfx::ToFlooredVector2d(scroll_amount_ + scroll_amount_)); EXPECT_VECTOR_EQ( root->ScrollDelta(), gfx::Vector2dF(fmod(2.0f * scroll_amount_.x(), 1.0f), 0.0f)); EndTest(); break; } root->ScrollBy(scroll_amount_); } virtual void AfterTest() OVERRIDE {} private: gfx::Vector2dF scroll_amount_; }; MULTI_THREAD_TEST_F(LayerTreeHostScrollTestFractionalScroll); class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestCaseWithChild() : initial_offset_(10, 20), javascript_scroll_(40, 5), scroll_amount_(2, -1), num_scrolls_(0) {} virtual void SetupTree() OVERRIDE { layer_tree_host()->SetDeviceScaleFactor(device_scale_factor_); scoped_refptr root_layer = Layer::Create(); root_layer->SetBounds(gfx::Size(10, 10)); root_scroll_layer_ = ContentLayer::Create(&fake_content_layer_client_); root_scroll_layer_->SetBounds(gfx::Size(110, 110)); root_scroll_layer_->SetPosition(gfx::Point()); root_scroll_layer_->SetAnchorPoint(gfx::PointF()); root_scroll_layer_->SetIsDrawable(true); root_scroll_layer_->SetScrollable(true); root_scroll_layer_->SetMaxScrollOffset(gfx::Vector2d(100, 100)); root_layer->AddChild(root_scroll_layer_); child_layer_ = ContentLayer::Create(&fake_content_layer_client_); child_layer_->set_did_scroll_callback( base::Bind(&LayerTreeHostScrollTestCaseWithChild::DidScroll, base::Unretained(this))); child_layer_->SetBounds(gfx::Size(110, 110)); if (scroll_child_layer_) { // Scrolls on the child layer will happen at 5, 5. If they are treated // like device pixels, and device scale factor is 2, then they will // be considered at 2.5, 2.5 in logical pixels, and will miss this layer. child_layer_->SetPosition(gfx::Point(5, 5)); } else { // Adjust the child layer horizontally so that scrolls will never hit it. child_layer_->SetPosition(gfx::Point(60, 5)); } child_layer_->SetAnchorPoint(gfx::PointF()); child_layer_->SetIsDrawable(true); child_layer_->SetScrollable(true); child_layer_->SetMaxScrollOffset(gfx::Vector2d(100, 100)); root_scroll_layer_->AddChild(child_layer_); if (scroll_child_layer_) { expected_scroll_layer_ = child_layer_; expected_no_scroll_layer_ = root_scroll_layer_; } else { expected_scroll_layer_ = root_scroll_layer_; expected_no_scroll_layer_ = child_layer_; } expected_scroll_layer_->SetScrollOffset(initial_offset_); layer_tree_host()->SetRootLayer(root_layer); LayerTreeHostScrollTest::SetupTree(); } virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void WillCommit() OVERRIDE { // Keep the test committing (otherwise the early out for no update // will stall the test). if (layer_tree_host()->source_frame_number() < 2) { layer_tree_host()->SetNeedsCommit(); } } void DidScroll() { final_scroll_offset_ = expected_scroll_layer_->scroll_offset(); } virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) OVERRIDE { num_scrolls_++; } virtual void Layout() OVERRIDE { EXPECT_VECTOR_EQ(gfx::Vector2d(), expected_no_scroll_layer_->scroll_offset()); switch (layer_tree_host()->source_frame_number()) { case 0: EXPECT_VECTOR_EQ(initial_offset_, expected_scroll_layer_->scroll_offset()); break; case 1: EXPECT_VECTOR_EQ(initial_offset_ + scroll_amount_, expected_scroll_layer_->scroll_offset()); // Pretend like Javascript updated the scroll position itself. expected_scroll_layer_->SetScrollOffset(javascript_scroll_); break; case 2: EXPECT_VECTOR_EQ(javascript_scroll_ + scroll_amount_, expected_scroll_layer_->scroll_offset()); break; } } virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root_impl = impl->active_tree()->root_layer(); LayerImpl* root_scroll_layer_impl = root_impl->children()[0]; LayerImpl* child_layer_impl = root_scroll_layer_impl->children()[0]; LayerImpl* expected_scroll_layer_impl = NULL; LayerImpl* expected_no_scroll_layer_impl = NULL; if (scroll_child_layer_) { expected_scroll_layer_impl = child_layer_impl; expected_no_scroll_layer_impl = root_scroll_layer_impl; } else { expected_scroll_layer_impl = root_scroll_layer_impl; expected_no_scroll_layer_impl = child_layer_impl; } EXPECT_VECTOR_EQ(gfx::Vector2d(), root_impl->ScrollDelta()); EXPECT_VECTOR_EQ(gfx::Vector2d(), expected_no_scroll_layer_impl->ScrollDelta()); // Ensure device scale factor is affecting the layers. gfx::Size expected_content_bounds = gfx::ToCeiledSize( gfx::ScaleSize(root_scroll_layer_impl->bounds(), device_scale_factor_)); EXPECT_SIZE_EQ(expected_content_bounds, root_scroll_layer_->content_bounds()); expected_content_bounds = gfx::ToCeiledSize( gfx::ScaleSize(child_layer_impl->bounds(), device_scale_factor_)); EXPECT_SIZE_EQ(expected_content_bounds, child_layer_->content_bounds()); switch (impl->active_tree()->source_frame_number()) { case 0: { // Gesture scroll on impl thread. InputHandler::ScrollStatus status = impl->ScrollBegin( gfx::ToCeiledPoint(expected_scroll_layer_impl->position() - gfx::Vector2dF(0.5f, 0.5f)), InputHandler::Gesture); EXPECT_EQ(InputHandler::ScrollStarted, status); impl->ScrollBy(gfx::Point(), scroll_amount_); impl->ScrollEnd(); // Check the scroll is applied as a delta. EXPECT_VECTOR_EQ(initial_offset_, expected_scroll_layer_impl->scroll_offset()); EXPECT_VECTOR_EQ(scroll_amount_, expected_scroll_layer_impl->ScrollDelta()); break; } case 1: { // Wheel scroll on impl thread. InputHandler::ScrollStatus status = impl->ScrollBegin( gfx::ToCeiledPoint(expected_scroll_layer_impl->position() + gfx::Vector2dF(0.5f, 0.5f)), InputHandler::Wheel); EXPECT_EQ(InputHandler::ScrollStarted, status); impl->ScrollBy(gfx::Point(), scroll_amount_); impl->ScrollEnd(); // Check the scroll is applied as a delta. EXPECT_VECTOR_EQ(javascript_scroll_, expected_scroll_layer_impl->scroll_offset()); EXPECT_VECTOR_EQ(scroll_amount_, expected_scroll_layer_impl->ScrollDelta()); break; } case 2: EXPECT_VECTOR_EQ(javascript_scroll_ + scroll_amount_, expected_scroll_layer_impl->scroll_offset()); EXPECT_VECTOR_EQ(gfx::Vector2d(), expected_scroll_layer_impl->ScrollDelta()); EndTest(); break; } } virtual void AfterTest() OVERRIDE { if (scroll_child_layer_) { EXPECT_EQ(0, num_scrolls_); EXPECT_VECTOR_EQ(javascript_scroll_ + scroll_amount_, final_scroll_offset_); } else { EXPECT_EQ(2, num_scrolls_); EXPECT_VECTOR_EQ(gfx::Vector2d(), final_scroll_offset_); } } protected: float device_scale_factor_; bool scroll_child_layer_; gfx::Vector2d initial_offset_; gfx::Vector2d javascript_scroll_; gfx::Vector2d scroll_amount_; int num_scrolls_; gfx::Vector2d final_scroll_offset_; FakeContentLayerClient fake_content_layer_client_; scoped_refptr root_scroll_layer_; scoped_refptr child_layer_; scoped_refptr expected_scroll_layer_; scoped_refptr expected_no_scroll_layer_; }; TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollChild_DirectRenderer_MainThreadPaint) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, false, false); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollChild_DirectRenderer_ImplSidePaint) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollChild_DelegatingRenderer_MainThreadPaint) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, true, false); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollChild_DelegatingRenderer_ImplSidePaint) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, true, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor15_ScrollChild_DirectRenderer) { device_scale_factor_ = 1.5f; scroll_child_layer_ = true; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor15_ScrollChild_DelegatingRenderer) { device_scale_factor_ = 1.5f; scroll_child_layer_ = true; RunTest(true, true, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor2_ScrollChild_DirectRenderer) { device_scale_factor_ = 2.f; scroll_child_layer_ = true; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor2_ScrollChild_DelegatingRenderer) { device_scale_factor_ = 2.f; scroll_child_layer_ = true; RunTest(true, true, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollRootScrollLayer_DirectRenderer) { device_scale_factor_ = 1.f; scroll_child_layer_ = false; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor1_ScrollRootScrollLayer_DelegatingRenderer) { device_scale_factor_ = 1.f; scroll_child_layer_ = false; RunTest(true, true, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor15_ScrollRootScrollLayer_DirectRenderer) { device_scale_factor_ = 1.5f; scroll_child_layer_ = false; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor15_ScrollRootScrollLayer_DelegatingRenderer) { device_scale_factor_ = 1.5f; scroll_child_layer_ = false; RunTest(true, true, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor2_ScrollRootScrollLayer_DirectRenderer_MainSidePaint) { device_scale_factor_ = 2.f; scroll_child_layer_ = false; RunTest(true, false, false); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor2_ScrollRootScrollLayer_DirectRenderer_ImplSidePaint) { device_scale_factor_ = 2.f; scroll_child_layer_ = false; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, DeviceScaleFactor2_ScrollRootScrollLayer_DelegatingRenderer) { device_scale_factor_ = 2.f; scroll_child_layer_ = false; RunTest(true, true, true); } class ImplSidePaintingScrollTest : public LayerTreeHostScrollTest { public: virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { settings->impl_side_painting = true; } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { if (impl->pending_tree()) impl->SetNeedsRedraw(); } }; class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest { public: ImplSidePaintingScrollTestSimple() : initial_scroll_(10, 20), main_thread_scroll_(40, 5), impl_thread_scroll1_(2, -1), impl_thread_scroll2_(-3, 10), num_scrolls_(0), can_activate_(true) {} virtual void BeginTest() OVERRIDE { layer_tree_host()->root_layer()->SetScrollable(true); layer_tree_host()->root_layer() ->SetMaxScrollOffset(gfx::Vector2d(100, 100)); layer_tree_host()->root_layer()->SetScrollOffset(initial_scroll_); PostSetNeedsCommitToMainThread(); } virtual void Layout() OVERRIDE { Layer* root = layer_tree_host()->root_layer(); if (!layer_tree_host()->source_frame_number()) { EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); } else { EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_ + impl_thread_scroll1_); // Pretend like Javascript updated the scroll position itself with a // change of main_thread_scroll. root->SetScrollOffset(initial_scroll_ + main_thread_scroll_ + impl_thread_scroll1_); } } virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE { return can_activate_; } virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl) OVERRIDE { return can_activate_; } virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { // We force a second draw here of the first commit before activating // the second commit. if (impl->active_tree()->source_frame_number() == 0) impl->SetNeedsRedraw(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { ImplSidePaintingScrollTest::DrawLayersOnThread(impl); LayerImpl* root = impl->active_tree()->root_layer(); LayerImpl* pending_root = impl->active_tree()->FindPendingTreeLayerById(root->id()); switch (impl->active_tree()->source_frame_number()) { case 0: if (!impl->pending_tree()) { can_activate_ = false; EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d()); root->ScrollBy(impl_thread_scroll1_); EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); EXPECT_VECTOR_EQ(root->ScrollDelta(), impl_thread_scroll1_); EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d()); PostSetNeedsCommitToMainThread(); // CommitCompleteOnThread will trigger this function again // and cause us to take the else clause. } else { can_activate_ = true; ASSERT_TRUE(pending_root); EXPECT_EQ(impl->pending_tree()->source_frame_number(), 1); root->ScrollBy(impl_thread_scroll2_); EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_); EXPECT_VECTOR_EQ(root->ScrollDelta(), impl_thread_scroll1_ + impl_thread_scroll2_); EXPECT_VECTOR_EQ(root->sent_scroll_delta(), impl_thread_scroll1_); EXPECT_VECTOR_EQ( pending_root->scroll_offset(), initial_scroll_ + main_thread_scroll_ + impl_thread_scroll1_); EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), impl_thread_scroll2_); EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d()); } break; case 1: EXPECT_FALSE(impl->pending_tree()); EXPECT_VECTOR_EQ( root->scroll_offset(), initial_scroll_ + main_thread_scroll_ + impl_thread_scroll1_); EXPECT_VECTOR_EQ(root->ScrollDelta(), impl_thread_scroll2_); EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d()); EndTest(); break; } } virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta, float scale) OVERRIDE { num_scrolls_++; } virtual void AfterTest() OVERRIDE { EXPECT_EQ(1, num_scrolls_); } private: gfx::Vector2d initial_scroll_; gfx::Vector2d main_thread_scroll_; gfx::Vector2d impl_thread_scroll1_; gfx::Vector2d impl_thread_scroll2_; int num_scrolls_; bool can_activate_; }; MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestSimple); class LayerTreeHostScrollTestScrollZeroMaxScrollOffset : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestScrollZeroMaxScrollOffset() {} virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root = impl->active_tree()->root_layer(); root->SetScrollable(true); root->SetMaxScrollOffset(gfx::Vector2d(100, 100)); EXPECT_EQ(InputHandler::ScrollStarted, root->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture)); root->SetMaxScrollOffset(gfx::Vector2d(0, 0)); EXPECT_EQ(InputHandler::ScrollIgnored, root->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture)); root->SetMaxScrollOffset(gfx::Vector2d(-100, -100)); EXPECT_EQ(InputHandler::ScrollIgnored, root->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture)); EndTest(); } virtual void AfterTest() OVERRIDE {} }; SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostScrollTestScrollZeroMaxScrollOffset); class ThreadCheckingInputHandlerClient : public InputHandlerClient { public: ThreadCheckingInputHandlerClient(base::SingleThreadTaskRunner* runner, bool* received_stop_flinging) : task_runner_(runner), received_stop_flinging_(received_stop_flinging) {} virtual void WillShutdown() OVERRIDE { if (!received_stop_flinging_) ADD_FAILURE() << "WillShutdown() called before fling stopped"; } virtual void Animate(base::TimeTicks time) OVERRIDE { if (!task_runner_->BelongsToCurrentThread()) ADD_FAILURE() << "Animate called on wrong thread"; } virtual void MainThreadHasStoppedFlinging() OVERRIDE { if (!task_runner_->BelongsToCurrentThread()) ADD_FAILURE() << "MainThreadHasStoppedFlinging called on wrong thread"; *received_stop_flinging_ = true; } virtual void DidOverscroll(const DidOverscrollParams& params) OVERRIDE { if (!task_runner_->BelongsToCurrentThread()) ADD_FAILURE() << "DidOverscroll called on wrong thread"; } private: base::SingleThreadTaskRunner* task_runner_; bool* received_stop_flinging_; }; void BindInputHandlerOnCompositorThread( const base::WeakPtr& input_handler, ThreadCheckingInputHandlerClient* client) { input_handler->BindToClient(client); } TEST(LayerTreeHostFlingTest, DidStopFlingingThread) { base::Thread impl_thread("cc"); ASSERT_TRUE(impl_thread.Start()); bool received_stop_flinging = false; LayerTreeSettings settings; ThreadCheckingInputHandlerClient input_handler_client( impl_thread.message_loop_proxy().get(), &received_stop_flinging); FakeLayerTreeHostClient client(FakeLayerTreeHostClient::DIRECT_3D); ASSERT_TRUE(impl_thread.message_loop_proxy().get()); scoped_ptr layer_tree_host = LayerTreeHost::Create( &client, settings, impl_thread.message_loop_proxy()); impl_thread.message_loop_proxy() ->PostTask(FROM_HERE, base::Bind(&BindInputHandlerOnCompositorThread, layer_tree_host->GetInputHandler(), base::Unretained(&input_handler_client))); layer_tree_host->DidStopFlinging(); layer_tree_host.reset(); impl_thread.Stop(); EXPECT_TRUE(received_stop_flinging); } class LayerTreeHostScrollTestLayerStructureChange : public LayerTreeHostScrollTest { public: LayerTreeHostScrollTestLayerStructureChange() : scroll_destroy_whole_tree_(false) {} virtual void SetupTree() OVERRIDE { scoped_refptr root_layer = Layer::Create(); root_layer->SetBounds(gfx::Size(10, 10)); Layer* root_scroll_layer = CreateScrollLayer(root_layer.get(), &root_scroll_layer_client_); CreateScrollLayer(root_layer.get(), &sibling_scroll_layer_client_); CreateScrollLayer(root_scroll_layer, &child_scroll_layer_client_); layer_tree_host()->SetRootLayer(root_layer); LayerTreeHostScrollTest::SetupTree(); } virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root = impl->active_tree()->root_layer(); switch (impl->active_tree()->source_frame_number()) { case 0: root->child_at(0)->SetScrollDelta(gfx::Vector2dF(5, 5)); root->child_at(0)->child_at(0)->SetScrollDelta(gfx::Vector2dF(5, 5)); root->child_at(1)->SetScrollDelta(gfx::Vector2dF(5, 5)); PostSetNeedsCommitToMainThread(); break; case 1: EndTest(); break; } } virtual void AfterTest() OVERRIDE {} virtual void DidScroll(Layer* layer) { if (scroll_destroy_whole_tree_) { layer_tree_host()->SetRootLayer(NULL); EndTest(); return; } layer->RemoveFromParent(); } protected: class FakeLayerScrollClient { public: void DidScroll() { owner_->DidScroll(layer_); } LayerTreeHostScrollTestLayerStructureChange* owner_; Layer* layer_; }; Layer* CreateScrollLayer(Layer* parent, FakeLayerScrollClient* client) { scoped_refptr scroll_layer = ContentLayer::Create(&fake_content_layer_client_); scroll_layer->SetBounds(gfx::Size(110, 110)); scroll_layer->SetPosition(gfx::Point(0, 0)); scroll_layer->SetAnchorPoint(gfx::PointF()); scroll_layer->SetIsDrawable(true); scroll_layer->SetScrollable(true); scroll_layer->SetMaxScrollOffset(gfx::Vector2d(100, 100)); scroll_layer->set_did_scroll_callback(base::Bind( &FakeLayerScrollClient::DidScroll, base::Unretained(client))); client->owner_ = this; client->layer_ = scroll_layer.get(); parent->AddChild(scroll_layer); return scroll_layer.get(); } FakeLayerScrollClient root_scroll_layer_client_; FakeLayerScrollClient sibling_scroll_layer_client_; FakeLayerScrollClient child_scroll_layer_client_; FakeContentLayerClient fake_content_layer_client_; bool scroll_destroy_whole_tree_; }; TEST_F(LayerTreeHostScrollTestLayerStructureChange, ScrollDestroyLayer) { RunTest(true, false, false); } TEST_F(LayerTreeHostScrollTestLayerStructureChange, ScrollDestroyWholeTree) { scroll_destroy_whole_tree_ = true; RunTest(true, false, false); } } // namespace } // namespace cc