1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/android/overscroll_refresh.h"
6 #include "base/android/scoped_java_ref.h"
7 #include "cc/input/overscroll_behavior.h"
8 #include "cc/layers/layer.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/android/overscroll_refresh_handler.h"
11 
12 namespace ui {
13 
14 const float kDipScale = 1.f;
15 const gfx::PointF kStartPos(2.f, 2.f);
16 
17 class OverscrollRefreshTest : public OverscrollRefreshHandler,
18                               public testing::Test {
19  public:
OverscrollRefreshTest()20   OverscrollRefreshTest() : OverscrollRefreshHandler(nullptr) {}
21 
22   // OverscrollRefreshHandler implementation.
PullStart(OverscrollAction type,float startx,float starty,bool navigateForward)23   bool PullStart(OverscrollAction type,
24                  float startx,
25                  float starty,
26                  bool navigateForward) override {
27     started_ = true;
28     return true;
29   }
30 
PullUpdate(float x_delta,float y_delta)31   void PullUpdate(float x_delta, float y_delta) override { delta_ += y_delta; }
32 
PullRelease(bool allow_refresh)33   void PullRelease(bool allow_refresh) override {
34     released_ = true;
35     refresh_allowed_ = allow_refresh;
36   }
37 
PullReset()38   void PullReset() override { reset_ = true; }
39 
GetAndResetPullStarted()40   bool GetAndResetPullStarted() {
41     bool result = started_;
42     started_ = false;
43     return result;
44   }
45 
GetAndResetPullDelta()46   float GetAndResetPullDelta() {
47     float result = delta_;
48     delta_ = 0;
49     return result;
50   }
51 
GetAndResetPullReleased()52   bool GetAndResetPullReleased() {
53     bool result = released_;
54     released_ = false;
55     return result;
56   }
57 
GetAndResetRefreshAllowed()58   bool GetAndResetRefreshAllowed() {
59     bool result = refresh_allowed_;
60     refresh_allowed_ = false;
61     return result;
62   }
63 
GetAndResetPullReset()64   bool GetAndResetPullReset() {
65     bool result = reset_;
66     reset_ = false;
67     return result;
68   }
69 
TestOverscrollBehavior(const cc::OverscrollBehavior & ob,const gfx::Vector2dF & scroll_delta,bool started)70   void TestOverscrollBehavior(const cc::OverscrollBehavior& ob,
71                               const gfx::Vector2dF& scroll_delta,
72                               bool started) {
73     OverscrollRefresh effect(this, 1.f);
74     effect.OnScrollBegin(kStartPos);
75     EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_delta));
76     EXPECT_FALSE(effect.IsActive());
77     EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck());
78     effect.OnOverscrolled(ob);
79     EXPECT_EQ(started, GetAndResetPullStarted());
80     EXPECT_EQ(!started, GetAndResetPullReset());
81   }
82 
83  private:
84   float delta_ = 0;
85   bool started_ = false;
86   bool released_ = false;
87   bool reset_ = false;
88   bool refresh_allowed_ = false;
89 };
90 
TEST_F(OverscrollRefreshTest,Basic)91 TEST_F(OverscrollRefreshTest, Basic) {
92   OverscrollRefresh effect(this, kDipScale);
93 
94   EXPECT_FALSE(effect.IsActive());
95   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
96 
97   effect.OnScrollBegin(kStartPos);
98   EXPECT_FALSE(effect.IsActive());
99   EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck());
100 
101   // The initial scroll should not be consumed, as it should first be offered
102   // to content.
103   gfx::Vector2dF scroll_up(0, 10);
104   EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_up));
105   EXPECT_FALSE(effect.IsActive());
106   EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck());
107 
108   // The unconsumed, overscrolling scroll will trigger the effect.
109   effect.OnOverscrolled(cc::OverscrollBehavior());
110   EXPECT_TRUE(effect.IsActive());
111   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
112   EXPECT_TRUE(GetAndResetPullStarted());
113 
114   // Further scrolls will be consumed.
115   EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 50)));
116   EXPECT_EQ(50.f, GetAndResetPullDelta());
117   EXPECT_TRUE(effect.IsActive());
118 
119   // Even scrolls in the down direction should be consumed.
120   EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -50)));
121   EXPECT_EQ(-50.f, GetAndResetPullDelta());
122   EXPECT_TRUE(effect.IsActive());
123 
124   // Ending the scroll while beyond the threshold should trigger a refresh.
125   gfx::Vector2dF zero_velocity;
126   EXPECT_FALSE(GetAndResetPullReleased());
127   effect.OnScrollEnd(zero_velocity);
128   EXPECT_FALSE(effect.IsActive());
129   EXPECT_TRUE(GetAndResetPullReleased());
130   EXPECT_TRUE(GetAndResetRefreshAllowed());
131 }
132 
TEST_F(OverscrollRefreshTest,NotTriggeredIfInitialYOffsetIsNotZero)133 TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) {
134   OverscrollRefresh effect(this, kDipScale);
135 
136   // A positive y scroll offset at the start of scroll will prevent activation,
137   // even if the subsequent scroll overscrolls upward.
138   gfx::Vector2dF nonzero_offset(0, 10);
139   gfx::SizeF viewport(100, 100);
140   bool overflow_y_hidden = false;
141   effect.OnFrameUpdated(viewport, nonzero_offset, overflow_y_hidden);
142   effect.OnScrollBegin(kStartPos);
143 
144   effect.OnFrameUpdated(viewport, gfx::Vector2dF(), overflow_y_hidden);
145   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
146   EXPECT_FALSE(effect.IsActive());
147   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
148   effect.OnOverscrolled(cc::OverscrollBehavior());
149   EXPECT_FALSE(effect.IsActive());
150   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
151   EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500)));
152   effect.OnScrollEnd(gfx::Vector2dF());
153   EXPECT_FALSE(GetAndResetPullStarted());
154   EXPECT_FALSE(GetAndResetPullReleased());
155 }
156 
TEST_F(OverscrollRefreshTest,NotTriggeredIfOverflowYHidden)157 TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) {
158   OverscrollRefresh effect(this, kDipScale);
159 
160   // overflow-y:hidden at the start of scroll will prevent activation.
161   gfx::Vector2dF zero_offset;
162   bool overflow_y_hidden = true;
163   gfx::SizeF viewport(100, 100);
164   effect.OnFrameUpdated(viewport, zero_offset, overflow_y_hidden);
165   effect.OnScrollBegin(kStartPos);
166 
167   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
168   EXPECT_FALSE(effect.IsActive());
169   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
170   effect.OnOverscrolled(cc::OverscrollBehavior());
171   EXPECT_FALSE(effect.IsActive());
172   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
173   EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500)));
174   effect.OnScrollEnd(gfx::Vector2dF());
175   EXPECT_FALSE(GetAndResetPullStarted());
176   EXPECT_FALSE(GetAndResetPullReleased());
177 }
178 
TEST_F(OverscrollRefreshTest,NotTriggeredIfInitialScrollDownward)179 TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) {
180   OverscrollRefresh effect(this, kDipScale);
181   effect.OnScrollBegin(kStartPos);
182 
183   // A downward initial scroll will prevent activation, even if the subsequent
184   // scroll overscrolls upward.
185   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -10)));
186   EXPECT_FALSE(effect.IsActive());
187   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
188 
189   effect.OnOverscrolled(cc::OverscrollBehavior());
190   EXPECT_FALSE(effect.IsActive());
191   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
192   EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500)));
193   effect.OnScrollEnd(gfx::Vector2dF());
194   EXPECT_FALSE(GetAndResetPullReleased());
195 }
196 
TEST_F(OverscrollRefreshTest,NotTriggeredIfInitialScrollOrTouchConsumed)197 TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) {
198   OverscrollRefresh effect(this, kDipScale);
199   effect.OnScrollBegin(kStartPos);
200   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
201   ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
202 
203   // Consumption of the initial touchmove or scroll should prevent future
204   // activation.
205   effect.Reset();
206   effect.OnOverscrolled(cc::OverscrollBehavior());
207   EXPECT_FALSE(effect.IsActive());
208   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
209   EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500)));
210   effect.OnOverscrolled(cc::OverscrollBehavior());
211   EXPECT_FALSE(effect.IsActive());
212   EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck());
213   EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500)));
214   effect.OnScrollEnd(gfx::Vector2dF());
215   EXPECT_FALSE(GetAndResetPullStarted());
216   EXPECT_FALSE(GetAndResetPullReleased());
217 }
218 
TEST_F(OverscrollRefreshTest,NotTriggeredIfFlungDownward)219 TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) {
220   OverscrollRefresh effect(this, kDipScale);
221   effect.OnScrollBegin(kStartPos);
222   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
223   ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
224   effect.OnOverscrolled(cc::OverscrollBehavior());
225   ASSERT_TRUE(effect.IsActive());
226   EXPECT_TRUE(GetAndResetPullStarted());
227 
228   // Terminating the pull with a down-directed fling should prevent triggering.
229   effect.OnScrollEnd(gfx::Vector2dF(0, -1000));
230   EXPECT_TRUE(GetAndResetPullReleased());
231   EXPECT_FALSE(GetAndResetRefreshAllowed());
232 }
233 
TEST_F(OverscrollRefreshTest,NotTriggeredIfReleasedWithoutActivation)234 TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) {
235   OverscrollRefresh effect(this, kDipScale);
236   effect.OnScrollBegin(kStartPos);
237   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
238   ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
239   effect.OnOverscrolled(cc::OverscrollBehavior());
240   ASSERT_TRUE(effect.IsActive());
241   EXPECT_TRUE(GetAndResetPullStarted());
242 
243   // An early release should prevent the refresh action from firing.
244   effect.ReleaseWithoutActivation();
245   effect.OnScrollEnd(gfx::Vector2dF());
246   EXPECT_TRUE(GetAndResetPullReleased());
247   EXPECT_FALSE(GetAndResetRefreshAllowed());
248 }
249 
TEST_F(OverscrollRefreshTest,NotTriggeredIfReset)250 TEST_F(OverscrollRefreshTest, NotTriggeredIfReset) {
251   OverscrollRefresh effect(this, kDipScale);
252   effect.OnScrollBegin(kStartPos);
253   ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10)));
254   ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck());
255   effect.OnOverscrolled(cc::OverscrollBehavior());
256   ASSERT_TRUE(effect.IsActive());
257   EXPECT_TRUE(GetAndResetPullStarted());
258 
259   // An early reset should prevent the refresh action from firing.
260   effect.Reset();
261   EXPECT_TRUE(GetAndResetPullReset());
262   effect.OnScrollEnd(gfx::Vector2dF());
263   EXPECT_FALSE(GetAndResetPullReleased());
264 }
265 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorYAutoTriggersStart)266 TEST_F(OverscrollRefreshTest, OverscrollBehaviorYAutoTriggersStart) {
267   TestOverscrollBehavior(cc::OverscrollBehavior(), gfx::Vector2dF(0, 10), true);
268 }
269 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorYContainPreventsTriggerStart)270 TEST_F(OverscrollRefreshTest, OverscrollBehaviorYContainPreventsTriggerStart) {
271   auto ob = cc::OverscrollBehavior();
272   ob.y = cc::OverscrollBehavior::OverscrollBehaviorType::
273       kOverscrollBehaviorTypeContain;
274   TestOverscrollBehavior(ob, gfx::Vector2dF(0, 10), false);
275 }
276 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorYNonePreventsTriggerStart)277 TEST_F(OverscrollRefreshTest, OverscrollBehaviorYNonePreventsTriggerStart) {
278   auto ob = cc::OverscrollBehavior();
279   ob.y = cc::OverscrollBehavior::OverscrollBehaviorType::
280       kOverscrollBehaviorTypeNone;
281   TestOverscrollBehavior(ob, gfx::Vector2dF(0, 10), false);
282 }
283 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorXAutoTriggersStart)284 TEST_F(OverscrollRefreshTest, OverscrollBehaviorXAutoTriggersStart) {
285   TestOverscrollBehavior(cc::OverscrollBehavior(), gfx::Vector2dF(10, 0), true);
286 }
287 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorXContainPreventsTriggerStart)288 TEST_F(OverscrollRefreshTest, OverscrollBehaviorXContainPreventsTriggerStart) {
289   auto ob = cc::OverscrollBehavior();
290   ob.x = cc::OverscrollBehavior::OverscrollBehaviorType::
291       kOverscrollBehaviorTypeContain;
292   TestOverscrollBehavior(ob, gfx::Vector2dF(10, 0), false);
293 }
294 
TEST_F(OverscrollRefreshTest,OverscrollBehaviorXNonePreventsTriggerStart)295 TEST_F(OverscrollRefreshTest, OverscrollBehaviorXNonePreventsTriggerStart) {
296   auto ob = cc::OverscrollBehavior();
297   ob.x = cc::OverscrollBehavior::OverscrollBehaviorType::
298       kOverscrollBehaviorTypeNone;
299   TestOverscrollBehavior(ob, gfx::Vector2dF(10, 0), false);
300 }
301 
302 }  // namespace ui
303