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.
5 #include "content/browser/renderer_host/input/touch_handle.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/events/test/mock_motion_event.h"
9 #include "ui/gfx/geometry/rect_f.h"
11 using ui::test::MockMotionEvent
;
16 const float kDefaultDrawableSize
= 10.f
;
18 struct MockDrawableData
{
20 : orientation(TOUCH_HANDLE_ORIENTATION_UNDEFINED
),
24 rect(0, 0, kDefaultDrawableSize
, kDefaultDrawableSize
) {}
25 TouchHandleOrientation orientation
;
32 class MockTouchHandleDrawable
: public TouchHandleDrawable
{
34 explicit MockTouchHandleDrawable(MockDrawableData
* data
) : data_(data
) {}
35 virtual ~MockTouchHandleDrawable() {}
37 virtual void SetEnabled(bool enabled
) OVERRIDE
{ data_
->enabled
= enabled
; }
39 virtual void SetOrientation(TouchHandleOrientation orientation
) OVERRIDE
{
40 data_
->orientation
= orientation
;
43 virtual void SetAlpha(float alpha
) OVERRIDE
{ data_
->alpha
= alpha
; }
45 virtual void SetFocus(const gfx::PointF
& position
) OVERRIDE
{
46 // Anchor focus to the top left of the rect (regardless of orientation).
47 data_
->rect
.set_origin(position
);
50 virtual void SetVisible(bool visible
) OVERRIDE
{ data_
->visible
= visible
; }
52 virtual bool IntersectsWith(const gfx::RectF
& rect
) const OVERRIDE
{
53 return data_
->rect
.Intersects(rect
);
57 MockDrawableData
* data_
;
62 class TouchHandleTest
: public testing::Test
, public TouchHandleClient
{
68 needs_animate_(false) {}
70 virtual ~TouchHandleTest() {}
72 // TouchHandleClient implementation.
73 virtual void OnHandleDragBegin(const TouchHandle
& handle
) OVERRIDE
{
77 virtual void OnHandleDragUpdate(const TouchHandle
& handle
,
78 const gfx::PointF
& new_position
) OVERRIDE
{
80 drag_position_
= new_position
;
83 virtual void OnHandleDragEnd(const TouchHandle
& handle
) OVERRIDE
{
87 virtual void OnHandleTapped(const TouchHandle
& handle
) OVERRIDE
{
91 virtual void SetNeedsAnimate() OVERRIDE
{ needs_animate_
= true; }
93 virtual scoped_ptr
<TouchHandleDrawable
> CreateDrawable() OVERRIDE
{
94 return scoped_ptr
<TouchHandleDrawable
>(
95 new MockTouchHandleDrawable(&drawable_data_
));
98 void Animate(TouchHandle
& handle
) {
99 needs_animate_
= false;
100 base::TimeTicks now
= base::TimeTicks::Now();
101 while (handle
.Animate(now
))
102 now
+= base::TimeDelta::FromMilliseconds(16);
105 bool GetAndResetHandleDragged() {
106 bool dragged
= dragged_
;
111 bool GetAndResetHandleTapped() {
112 bool tapped
= tapped_
;
117 bool GetAndResetNeedsAnimate() {
118 bool needs_animate
= needs_animate_
;
119 needs_animate_
= false;
120 return needs_animate
;
123 bool IsDragging() const { return dragging_
; }
124 const gfx::PointF
& DragPosition() const { return drag_position_
; }
125 bool NeedsAnimate() const { return needs_animate_
; }
127 const MockDrawableData
& drawable() { return drawable_data_
; }
130 gfx::PointF drag_position_
;
136 MockDrawableData drawable_data_
;
139 TEST_F(TouchHandleTest
, Visibility
) {
140 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
141 EXPECT_FALSE(drawable().visible
);
143 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
144 EXPECT_TRUE(drawable().visible
);
145 EXPECT_EQ(1.f
, drawable().alpha
);
147 handle
.SetVisible(false, TouchHandle::ANIMATION_NONE
);
148 EXPECT_FALSE(drawable().visible
);
150 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
151 EXPECT_TRUE(drawable().visible
);
152 EXPECT_EQ(1.f
, drawable().alpha
);
155 TEST_F(TouchHandleTest
, VisibilityAnimation
) {
156 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
157 ASSERT_FALSE(NeedsAnimate());
158 ASSERT_FALSE(drawable().visible
);
159 ASSERT_EQ(0.f
, drawable().alpha
);
161 handle
.SetVisible(true, TouchHandle::ANIMATION_SMOOTH
);
162 EXPECT_TRUE(NeedsAnimate());
163 EXPECT_TRUE(drawable().visible
);
164 EXPECT_EQ(0.f
, drawable().alpha
);
167 EXPECT_TRUE(drawable().visible
);
168 EXPECT_EQ(1.f
, drawable().alpha
);
170 ASSERT_FALSE(NeedsAnimate());
171 handle
.SetVisible(false, TouchHandle::ANIMATION_SMOOTH
);
172 EXPECT_TRUE(NeedsAnimate());
173 EXPECT_TRUE(drawable().visible
);
174 EXPECT_EQ(1.f
, drawable().alpha
);
177 EXPECT_FALSE(drawable().visible
);
178 EXPECT_EQ(0.f
, drawable().alpha
);
180 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
181 EXPECT_EQ(1.f
, drawable().alpha
);
182 EXPECT_FALSE(GetAndResetNeedsAnimate());
183 handle
.SetVisible(false, TouchHandle::ANIMATION_SMOOTH
);
184 EXPECT_EQ(1.f
, drawable().alpha
);
185 EXPECT_TRUE(GetAndResetNeedsAnimate());
186 handle
.SetVisible(true, TouchHandle::ANIMATION_SMOOTH
);
187 EXPECT_EQ(1.f
, drawable().alpha
);
188 EXPECT_FALSE(GetAndResetNeedsAnimate());
191 TEST_F(TouchHandleTest
, Orientation
) {
192 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
193 EXPECT_EQ(TOUCH_HANDLE_CENTER
, drawable().orientation
);
195 handle
.SetOrientation(TOUCH_HANDLE_LEFT
);
196 EXPECT_EQ(TOUCH_HANDLE_LEFT
, drawable().orientation
);
198 handle
.SetOrientation(TOUCH_HANDLE_RIGHT
);
199 EXPECT_EQ(TOUCH_HANDLE_RIGHT
, drawable().orientation
);
201 handle
.SetOrientation(TOUCH_HANDLE_CENTER
);
202 EXPECT_EQ(TOUCH_HANDLE_CENTER
, drawable().orientation
);
205 TEST_F(TouchHandleTest
, Position
) {
206 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
207 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
209 gfx::PointF position
;
210 EXPECT_EQ(gfx::PointF(), drawable().rect
.origin());
212 position
= gfx::PointF(7.3f
, -3.7f
);
213 handle
.SetPosition(position
);
214 EXPECT_EQ(position
, drawable().rect
.origin());
216 position
= gfx::PointF(-7.3f
, 3.7f
);
217 handle
.SetPosition(position
);
218 EXPECT_EQ(position
, drawable().rect
.origin());
221 TEST_F(TouchHandleTest
, PositionNotUpdatedWhileFadingOrInvisible
) {
222 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
224 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
225 ASSERT_TRUE(drawable().visible
);
226 ASSERT_FALSE(NeedsAnimate());
228 gfx::PointF
old_position(7.3f
, -3.7f
);
229 handle
.SetPosition(old_position
);
230 ASSERT_EQ(old_position
, drawable().rect
.origin());
232 handle
.SetVisible(false, TouchHandle::ANIMATION_SMOOTH
);
233 ASSERT_TRUE(NeedsAnimate());
235 gfx::PointF
new_position(3.7f
, -3.7f
);
236 handle
.SetPosition(new_position
);
237 EXPECT_EQ(old_position
, drawable().rect
.origin());
238 EXPECT_TRUE(NeedsAnimate());
240 // While the handle is fading, the new position should not take affect.
241 base::TimeTicks now
= base::TimeTicks::Now();
242 while (handle
.Animate(now
)) {
243 EXPECT_EQ(old_position
, drawable().rect
.origin());
244 now
+= base::TimeDelta::FromMilliseconds(16);
247 // Even after the animation terminates, the new position will not be pushed.
248 EXPECT_EQ(old_position
, drawable().rect
.origin());
250 // As soon as the handle becomes visible, the new position will be pushed.
251 handle
.SetVisible(true, TouchHandle::ANIMATION_SMOOTH
);
252 EXPECT_EQ(new_position
, drawable().rect
.origin());
255 TEST_F(TouchHandleTest
, Enabled
) {
256 // A newly created handle defaults to enabled.
257 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
258 EXPECT_TRUE(drawable().enabled
);
260 handle
.SetVisible(true, TouchHandle::ANIMATION_SMOOTH
);
261 EXPECT_TRUE(GetAndResetNeedsAnimate());
262 EXPECT_EQ(0.f
, drawable().alpha
);
263 handle
.SetEnabled(false);
264 EXPECT_FALSE(drawable().enabled
);
266 // Dragging should not be allowed while the handle is disabled.
267 base::TimeTicks event_time
= base::TimeTicks::Now();
268 const float kOffset
= kDefaultDrawableSize
/ 2.f
;
269 MockMotionEvent
event(
270 MockMotionEvent::ACTION_DOWN
, event_time
, kOffset
, kOffset
);
271 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
273 // Disabling mid-animation should cancel the animation.
274 handle
.SetEnabled(true);
275 handle
.SetVisible(false, TouchHandle::ANIMATION_SMOOTH
);
276 EXPECT_TRUE(drawable().visible
);
277 EXPECT_TRUE(GetAndResetNeedsAnimate());
278 handle
.SetEnabled(false);
279 EXPECT_FALSE(drawable().enabled
);
280 EXPECT_FALSE(drawable().visible
);
281 EXPECT_FALSE(handle
.Animate(base::TimeTicks::Now()));
283 // Disabling mid-drag should cancel the drag.
284 handle
.SetEnabled(true);
285 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
286 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
287 EXPECT_TRUE(IsDragging());
288 handle
.SetEnabled(false);
289 EXPECT_FALSE(IsDragging());
290 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
293 TEST_F(TouchHandleTest
, Drag
) {
294 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
296 base::TimeTicks event_time
= base::TimeTicks::Now();
297 const float kOffset
= kDefaultDrawableSize
/ 2.f
;
299 // The handle must be visible to trigger drag.
300 MockMotionEvent
event(
301 MockMotionEvent::ACTION_DOWN
, event_time
, kOffset
, kOffset
);
302 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
303 EXPECT_FALSE(IsDragging());
304 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
306 // ACTION_DOWN must fall within the drawable region to trigger drag.
307 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 50, 50);
308 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
309 EXPECT_FALSE(IsDragging());
311 // Only ACTION_DOWN will trigger drag.
312 event
= MockMotionEvent(
313 MockMotionEvent::ACTION_MOVE
, event_time
, kOffset
, kOffset
);
314 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
315 EXPECT_FALSE(IsDragging());
318 event
= MockMotionEvent(
319 MockMotionEvent::ACTION_DOWN
, event_time
, kOffset
, kOffset
);
320 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
321 EXPECT_TRUE(IsDragging());
323 event
= MockMotionEvent(
324 MockMotionEvent::ACTION_MOVE
, event_time
, kOffset
+ 10, kOffset
+ 15);
325 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
326 EXPECT_TRUE(GetAndResetHandleDragged());
327 EXPECT_TRUE(IsDragging());
328 EXPECT_EQ(gfx::PointF(10, 15), DragPosition());
330 event
= MockMotionEvent(
331 MockMotionEvent::ACTION_MOVE
, event_time
, kOffset
- 10, kOffset
- 15);
332 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
333 EXPECT_TRUE(GetAndResetHandleDragged());
334 EXPECT_TRUE(IsDragging());
335 EXPECT_EQ(gfx::PointF(-10, -15), DragPosition());
337 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
);
338 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
339 EXPECT_FALSE(GetAndResetHandleDragged());
340 EXPECT_FALSE(IsDragging());
342 // Non-ACTION_DOWN events after the drag has terminated should not be handled.
343 event
= MockMotionEvent(MockMotionEvent::ACTION_CANCEL
);
344 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
347 TEST_F(TouchHandleTest
, DragDefersOrientationChange
) {
348 TouchHandle
handle(this, TOUCH_HANDLE_RIGHT
);
349 ASSERT_EQ(drawable().orientation
, TOUCH_HANDLE_RIGHT
);
350 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
352 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
);
353 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
354 EXPECT_TRUE(IsDragging());
356 // Orientation changes will be deferred until the drag ends.
357 handle
.SetOrientation(TOUCH_HANDLE_LEFT
);
358 EXPECT_EQ(TOUCH_HANDLE_RIGHT
, drawable().orientation
);
360 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
);
361 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
362 EXPECT_TRUE(GetAndResetHandleDragged());
363 EXPECT_TRUE(IsDragging());
364 EXPECT_EQ(TOUCH_HANDLE_RIGHT
, drawable().orientation
);
366 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
);
367 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
368 EXPECT_FALSE(GetAndResetHandleDragged());
369 EXPECT_FALSE(IsDragging());
370 EXPECT_EQ(TOUCH_HANDLE_LEFT
, drawable().orientation
);
373 TEST_F(TouchHandleTest
, DragDefersFade
) {
374 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
375 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
377 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
);
378 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
379 EXPECT_TRUE(IsDragging());
381 // Fade will be deferred until the drag ends.
382 handle
.SetVisible(false, TouchHandle::ANIMATION_SMOOTH
);
383 EXPECT_FALSE(NeedsAnimate());
384 EXPECT_TRUE(drawable().visible
);
385 EXPECT_EQ(1.f
, drawable().alpha
);
387 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
);
388 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
389 EXPECT_FALSE(NeedsAnimate());
390 EXPECT_TRUE(drawable().visible
);
392 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
);
393 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
394 EXPECT_FALSE(IsDragging());
395 EXPECT_TRUE(NeedsAnimate());
398 EXPECT_FALSE(drawable().visible
);
399 EXPECT_EQ(0.f
, drawable().alpha
);
402 TEST_F(TouchHandleTest
, DragTargettingUsesTouchSize
) {
403 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
404 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
406 base::TimeTicks event_time
= base::TimeTicks::Now();
407 const float kTouchSize
= 24.f
;
408 const float kOffset
= kDefaultDrawableSize
+ kTouchSize
/ 2.001f
;
410 MockMotionEvent
event(
411 MockMotionEvent::ACTION_DOWN
, event_time
, kOffset
, kOffset
);
412 event
.SetTouchMajor(0.f
);
413 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
414 EXPECT_FALSE(IsDragging());
416 event
.SetTouchMajor(kTouchSize
/ 2.f
);
417 EXPECT_FALSE(handle
.WillHandleTouchEvent(event
));
418 EXPECT_FALSE(IsDragging());
420 event
.SetTouchMajor(kTouchSize
);
421 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
422 EXPECT_TRUE(IsDragging());
424 event
.SetTouchMajor(kTouchSize
* 2.f
);
425 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
426 EXPECT_TRUE(IsDragging());
428 // Ensure a touch size of 0 can still register a hit.
429 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
,
431 kDefaultDrawableSize
/ 2.f
,
432 kDefaultDrawableSize
/ 2.f
);
433 event
.SetTouchMajor(0);
434 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
435 EXPECT_TRUE(IsDragging());
438 TEST_F(TouchHandleTest
, Tap
) {
439 TouchHandle
handle(this, TOUCH_HANDLE_CENTER
);
440 handle
.SetVisible(true, TouchHandle::ANIMATION_NONE
);
442 base::TimeTicks event_time
= base::TimeTicks::Now();
444 // ACTION_CANCEL shouldn't trigger a tap.
445 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
446 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
447 event_time
+= base::TimeDelta::FromMilliseconds(50);
448 event
= MockMotionEvent(MockMotionEvent::ACTION_CANCEL
, event_time
, 0, 0);
449 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
450 EXPECT_FALSE(GetAndResetHandleTapped());
452 // Long press shouldn't trigger a tap.
453 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
454 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
455 event_time
+= base::TimeDelta::FromMilliseconds(500);
456 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
457 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
458 EXPECT_FALSE(GetAndResetHandleTapped());
460 // Only a brief tap should trigger a tap.
461 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
462 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
463 event_time
+= base::TimeDelta::FromMilliseconds(50);
464 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
465 EXPECT_TRUE(handle
.WillHandleTouchEvent(event
));
466 EXPECT_TRUE(GetAndResetHandleTapped());
469 } // namespace content