| // Copyright 2014 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 "ui/android/overscroll_refresh.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "cc/input/overscroll_behavior.h" |
| #include "cc/layers/layer.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/android/overscroll_refresh_handler.h" |
| |
| namespace ui { |
| |
| const float kDipScale = 1.f; |
| const gfx::PointF kStartPos(2.f, 2.f); |
| const float kDefaultEdgeWidth = |
| OverscrollRefresh::kDefaultNavigationEdgeWidth * kDipScale; |
| |
| class OverscrollRefreshTest : public OverscrollRefreshHandler, |
| public testing::Test { |
| public: |
| OverscrollRefreshTest() : OverscrollRefreshHandler(nullptr) {} |
| |
| // OverscrollRefreshHandler implementation. |
| bool PullStart(OverscrollAction type, |
| float startx, |
| float starty, |
| bool navigateForward) override { |
| started_ = true; |
| return true; |
| } |
| |
| void PullUpdate(float x_delta, float y_delta) override { delta_ += y_delta; } |
| |
| void PullRelease(bool allow_refresh) override { |
| released_ = true; |
| refresh_allowed_ = allow_refresh; |
| } |
| |
| void PullReset() override { reset_ = true; } |
| |
| bool GetAndResetPullStarted() { |
| bool result = started_; |
| started_ = false; |
| return result; |
| } |
| |
| float GetAndResetPullDelta() { |
| float result = delta_; |
| delta_ = 0; |
| return result; |
| } |
| |
| bool GetAndResetPullReleased() { |
| bool result = released_; |
| released_ = false; |
| return result; |
| } |
| |
| bool GetAndResetRefreshAllowed() { |
| bool result = refresh_allowed_; |
| refresh_allowed_ = false; |
| return result; |
| } |
| |
| bool GetAndResetPullReset() { |
| bool result = reset_; |
| reset_ = false; |
| return result; |
| } |
| |
| void TestOverscrollBehavior(const cc::OverscrollBehavior& ob, |
| const gfx::Vector2dF& scroll_delta, |
| bool started) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_delta)); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(ob); |
| EXPECT_EQ(started, GetAndResetPullStarted()); |
| EXPECT_EQ(!started, GetAndResetPullReset()); |
| } |
| |
| private: |
| float delta_ = 0; |
| bool started_ = false; |
| bool released_ = false; |
| bool reset_ = false; |
| bool refresh_allowed_ = false; |
| }; |
| |
| TEST_F(OverscrollRefreshTest, Basic) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| |
| effect.OnScrollBegin(kStartPos); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| |
| // The initial scroll should not be consumed, as it should first be offered |
| // to content. |
| gfx::Vector2dF scroll_up(0, 10); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_up)); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| |
| // The unconsumed, overscrolling scroll will trigger the effect. |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_TRUE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_TRUE(GetAndResetPullStarted()); |
| |
| // Further scrolls will be consumed. |
| EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 50))); |
| EXPECT_EQ(50.f, GetAndResetPullDelta()); |
| EXPECT_TRUE(effect.IsActive()); |
| |
| // Even scrolls in the down direction should be consumed. |
| EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -50))); |
| EXPECT_EQ(-50.f, GetAndResetPullDelta()); |
| EXPECT_TRUE(effect.IsActive()); |
| |
| // Ending the scroll while beyond the threshold should trigger a refresh. |
| gfx::Vector2dF zero_velocity; |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| effect.OnScrollEnd(zero_velocity); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_TRUE(GetAndResetPullReleased()); |
| EXPECT_TRUE(GetAndResetRefreshAllowed()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| |
| // A positive y scroll offset at the start of scroll will prevent activation, |
| // even if the subsequent scroll overscrolls upward. |
| gfx::Vector2dF nonzero_offset(0, 10); |
| gfx::SizeF viewport(100, 100); |
| bool overflow_y_hidden = false; |
| effect.OnFrameUpdated(viewport, nonzero_offset, overflow_y_hidden); |
| effect.OnScrollBegin(kStartPos); |
| |
| effect.OnFrameUpdated(viewport, gfx::Vector2dF(), overflow_y_hidden); |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_FALSE(GetAndResetPullStarted()); |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| |
| // overflow-y:hidden at the start of scroll will prevent activation. |
| gfx::Vector2dF zero_offset; |
| bool overflow_y_hidden = true; |
| gfx::SizeF viewport(100, 100); |
| effect.OnFrameUpdated(viewport, zero_offset, overflow_y_hidden); |
| effect.OnScrollBegin(kStartPos); |
| |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_FALSE(GetAndResetPullStarted()); |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| |
| // A downward initial scroll will prevent activation, even if the subsequent |
| // scroll overscrolls upward. |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -10))); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| |
| // Consumption of the initial touchmove or scroll should prevent future |
| // activation. |
| effect.Reset(); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| EXPECT_FALSE(effect.IsActive()); |
| EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); |
| EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_FALSE(GetAndResetPullStarted()); |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| ASSERT_TRUE(effect.IsActive()); |
| EXPECT_TRUE(GetAndResetPullStarted()); |
| |
| // Terminating the pull with a down-directed fling should prevent triggering. |
| effect.OnScrollEnd(gfx::Vector2dF(0, -1000)); |
| EXPECT_TRUE(GetAndResetPullReleased()); |
| EXPECT_FALSE(GetAndResetRefreshAllowed()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| ASSERT_TRUE(effect.IsActive()); |
| EXPECT_TRUE(GetAndResetPullStarted()); |
| |
| // An early release should prevent the refresh action from firing. |
| effect.ReleaseWithoutActivation(); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_TRUE(GetAndResetPullReleased()); |
| EXPECT_FALSE(GetAndResetRefreshAllowed()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, NotTriggeredIfReset) { |
| OverscrollRefresh effect(this, kDefaultEdgeWidth); |
| effect.OnScrollBegin(kStartPos); |
| ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); |
| ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); |
| effect.OnOverscrolled(cc::OverscrollBehavior()); |
| ASSERT_TRUE(effect.IsActive()); |
| EXPECT_TRUE(GetAndResetPullStarted()); |
| |
| // An early reset should prevent the refresh action from firing. |
| effect.Reset(); |
| EXPECT_TRUE(GetAndResetPullReset()); |
| effect.OnScrollEnd(gfx::Vector2dF()); |
| EXPECT_FALSE(GetAndResetPullReleased()); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorYAutoTriggersStart) { |
| TestOverscrollBehavior(cc::OverscrollBehavior(), gfx::Vector2dF(0, 10), true); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorYContainPreventsTriggerStart) { |
| auto ob = cc::OverscrollBehavior(); |
| ob.y = cc::OverscrollBehavior::Type::kContain; |
| TestOverscrollBehavior(ob, gfx::Vector2dF(0, 10), false); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorYNonePreventsTriggerStart) { |
| auto ob = cc::OverscrollBehavior(); |
| ob.y = cc::OverscrollBehavior::Type::kNone; |
| TestOverscrollBehavior(ob, gfx::Vector2dF(0, 10), false); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorXAutoTriggersStart) { |
| TestOverscrollBehavior(cc::OverscrollBehavior(), gfx::Vector2dF(10, 0), true); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorXContainPreventsTriggerStart) { |
| auto ob = cc::OverscrollBehavior(); |
| ob.x = cc::OverscrollBehavior::Type::kContain; |
| TestOverscrollBehavior(ob, gfx::Vector2dF(10, 0), false); |
| } |
| |
| TEST_F(OverscrollRefreshTest, OverscrollBehaviorXNonePreventsTriggerStart) { |
| auto ob = cc::OverscrollBehavior(); |
| ob.x = cc::OverscrollBehavior::Type::kNone; |
| TestOverscrollBehavior(ob, gfx::Vector2dF(10, 0), false); |
| } |
| |
| } // namespace ui |