Index: content/browser/renderer_host/input/touch_handle_unittest.cc |
diff --git a/content/browser/renderer_host/input/touch_handle_unittest.cc b/content/browser/renderer_host/input/touch_handle_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f7ff11137754785740db3f2702b95be3f484a9f2 |
--- /dev/null |
+++ b/content/browser/renderer_host/input/touch_handle_unittest.cc |
@@ -0,0 +1,433 @@ |
+// 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 "content/browser/renderer_host/input/touch_handle.h" |
+ |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/events/test/mock_motion_event.h" |
+#include "ui/gfx/geometry/rect_f.h" |
+ |
+using ui::test::MockMotionEvent; |
+ |
+namespace content { |
+namespace { |
+ |
+const float kDefaultDrawableSize = 10.f; |
+ |
+struct MockDrawableData { |
+ MockDrawableData() |
+ : orientation(TOUCH_HANDLE_ORIENTATION_UNDEFINED), |
+ alpha(0.f), |
+ enabled(false), |
+ visible(false), |
+ rect(0, 0, kDefaultDrawableSize, kDefaultDrawableSize) {} |
+ TouchHandleOrientation orientation; |
+ float alpha; |
+ bool enabled; |
+ bool visible; |
+ gfx::RectF rect; |
+}; |
+ |
+class MockTouchHandleDrawable : public TouchHandleDrawable { |
+ public: |
+ explicit MockTouchHandleDrawable(MockDrawableData* data) : data_(data) {} |
+ virtual ~MockTouchHandleDrawable() {} |
+ |
+ virtual void SetEnabled(bool enabled) OVERRIDE { data_->enabled = enabled; } |
+ |
+ virtual void SetOrientation(TouchHandleOrientation orientation) OVERRIDE { |
+ data_->orientation = orientation; |
+ } |
+ |
+ virtual void SetAlpha(float alpha) OVERRIDE { data_->alpha = alpha; } |
+ |
+ virtual void SetFocus(const gfx::PointF& position) OVERRIDE { |
+ // Anchor focus to the top left of the rect (regardless of orientation). |
+ data_->rect.set_origin(position); |
+ } |
+ |
+ virtual void SetVisible(bool visible) OVERRIDE { data_->visible = visible; } |
+ |
+ virtual bool ContainsPoint(const gfx::PointF& point) const OVERRIDE { |
+ return data_->rect.Contains(point); |
+ } |
+ |
+ private: |
+ MockDrawableData* data_; |
+}; |
+ |
+} // namespace |
+ |
+class TouchHandleTest : public testing::Test, public TouchHandleClient { |
+ public: |
+ TouchHandleTest() |
+ : dragging_(false), |
+ dragged_(false), |
+ tapped_(false), |
+ needs_animate_(false) {} |
+ |
+ virtual ~TouchHandleTest() {} |
+ |
+ // TouchHandleClient implementation. |
+ virtual void OnHandleDragBegin(const TouchHandle& handle) OVERRIDE { |
+ dragging_ = true; |
+ } |
+ |
+ virtual void OnHandleDragUpdate(const TouchHandle& handle, |
+ const gfx::PointF& new_position) OVERRIDE { |
+ dragged_ = true; |
+ drag_position_ = new_position; |
+ } |
+ |
+ virtual void OnHandleDragEnd(const TouchHandle& handle) OVERRIDE { |
+ dragging_ = false; |
+ } |
+ |
+ virtual void OnHandleTapped(const TouchHandle& handle) OVERRIDE { |
+ tapped_ = true; |
+ } |
+ |
+ virtual void SetNeedsAnimate() OVERRIDE { needs_animate_ = true; } |
+ |
+ virtual scoped_ptr<TouchHandleDrawable> CreateDrawable() OVERRIDE { |
+ return scoped_ptr<TouchHandleDrawable>( |
+ new MockTouchHandleDrawable(&drawable_data_)); |
+ } |
+ |
+ void Animate(TouchHandle& handle) { |
+ needs_animate_ = false; |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ while (handle.Animate(now)) |
+ now += base::TimeDelta::FromMilliseconds(16); |
+ } |
+ |
+ bool GetAndResetHandleDragged() { |
+ bool dragged = dragged_; |
+ dragged_ = false; |
+ return dragged; |
+ } |
+ |
+ bool GetAndResetHandleTapped() { |
+ bool tapped = tapped_; |
+ tapped_ = false; |
+ return tapped; |
+ } |
+ |
+ bool GetAndResetNeedsAnimate() { |
+ bool needs_animate = needs_animate_; |
+ needs_animate_ = false; |
+ return needs_animate; |
+ } |
+ |
+ bool IsDragging() const { return dragging_; } |
+ const gfx::PointF& DragPosition() const { return drag_position_; } |
+ bool NeedsAnimate() const { return needs_animate_; } |
+ |
+ const MockDrawableData& drawable() { return drawable_data_; } |
+ |
+ private: |
+ gfx::PointF drag_position_; |
+ bool dragging_; |
+ bool dragged_; |
+ bool tapped_; |
+ bool needs_animate_; |
+ |
+ MockDrawableData drawable_data_; |
+}; |
+ |
+TEST_F(TouchHandleTest, Visibility) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ EXPECT_FALSE(drawable().visible); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ |
+ handle.SetVisible(false, TouchHandle::ANIMATION_NONE); |
+ EXPECT_FALSE(drawable().visible); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+} |
+ |
+TEST_F(TouchHandleTest, VisibilityAnimation) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ ASSERT_FALSE(NeedsAnimate()); |
+ ASSERT_FALSE(drawable().visible); |
+ ASSERT_EQ(0.f, drawable().alpha); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_TRUE(NeedsAnimate()); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(0.f, drawable().alpha); |
+ |
+ Animate(handle); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ |
+ ASSERT_FALSE(NeedsAnimate()); |
+ handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_TRUE(NeedsAnimate()); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ |
+ Animate(handle); |
+ EXPECT_FALSE(drawable().visible); |
+ EXPECT_EQ(0.f, drawable().alpha); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ EXPECT_FALSE(GetAndResetNeedsAnimate()); |
+ handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ EXPECT_TRUE(GetAndResetNeedsAnimate()); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ EXPECT_FALSE(GetAndResetNeedsAnimate()); |
+} |
+ |
+TEST_F(TouchHandleTest, Orientation) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ EXPECT_EQ(TOUCH_HANDLE_CENTER, drawable().orientation); |
+ |
+ handle.SetOrientation(TOUCH_HANDLE_LEFT); |
+ EXPECT_EQ(TOUCH_HANDLE_LEFT, drawable().orientation); |
+ |
+ handle.SetOrientation(TOUCH_HANDLE_RIGHT); |
+ EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation); |
+ |
+ handle.SetOrientation(TOUCH_HANDLE_CENTER); |
+ EXPECT_EQ(TOUCH_HANDLE_CENTER, drawable().orientation); |
+} |
+ |
+TEST_F(TouchHandleTest, Position) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ |
+ gfx::PointF position; |
+ EXPECT_EQ(gfx::PointF(), drawable().rect.origin()); |
+ |
+ position = gfx::PointF(7.3f, -3.7f); |
+ handle.SetPosition(position); |
+ EXPECT_EQ(position, drawable().rect.origin()); |
+ |
+ position = gfx::PointF(-7.3f, 3.7f); |
+ handle.SetPosition(position); |
+ EXPECT_EQ(position, drawable().rect.origin()); |
+} |
+ |
+TEST_F(TouchHandleTest, PositionNotUpdatedWhileFadingOrInvisible) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ ASSERT_TRUE(drawable().visible); |
+ ASSERT_FALSE(NeedsAnimate()); |
+ |
+ gfx::PointF old_position(7.3f, -3.7f); |
+ handle.SetPosition(old_position); |
+ ASSERT_EQ(old_position, drawable().rect.origin()); |
+ |
+ handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH); |
+ ASSERT_TRUE(NeedsAnimate()); |
+ |
+ gfx::PointF new_position(3.7f, -3.7f); |
+ handle.SetPosition(new_position); |
+ EXPECT_EQ(old_position, drawable().rect.origin()); |
+ EXPECT_TRUE(NeedsAnimate()); |
+ |
+ // While the handle is fading, the new position should not take affect. |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ while (handle.Animate(now)) { |
+ EXPECT_EQ(old_position, drawable().rect.origin()); |
+ now += base::TimeDelta::FromMilliseconds(16); |
+ } |
+ |
+ // Even after the animation terminates, the new position will not be pushed. |
+ EXPECT_EQ(old_position, drawable().rect.origin()); |
+ |
+ // As soon as the handle becomes visible, the new position will be pushed. |
+ handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_EQ(new_position, drawable().rect.origin()); |
+} |
+ |
+TEST_F(TouchHandleTest, Enabled) { |
+ // A newly created handle defaults to enabled. |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ EXPECT_TRUE(drawable().enabled); |
+ |
+ handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_TRUE(GetAndResetNeedsAnimate()); |
+ EXPECT_EQ(0.f, drawable().alpha); |
+ handle.SetEnabled(false); |
+ EXPECT_FALSE(drawable().enabled); |
+ |
+ // Dragging should not be allowed while the handle is disabled. |
+ base::TimeTicks event_time = base::TimeTicks::Now(); |
+ const float kOffset = kDefaultDrawableSize / 2.f; |
+ MockMotionEvent event( |
+ MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+ |
+ // Disabling mid-animation should cancel the animation. |
+ handle.SetEnabled(true); |
+ handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_TRUE(GetAndResetNeedsAnimate()); |
+ handle.SetEnabled(false); |
+ EXPECT_FALSE(drawable().enabled); |
+ EXPECT_FALSE(drawable().visible); |
+ EXPECT_FALSE(handle.Animate(base::TimeTicks::Now())); |
+ |
+ // Disabling mid-drag should cancel the drag. |
+ handle.SetEnabled(true); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(IsDragging()); |
+ handle.SetEnabled(false); |
+ EXPECT_FALSE(IsDragging()); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+} |
+ |
+TEST_F(TouchHandleTest, Drag) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ |
+ base::TimeTicks event_time = base::TimeTicks::Now(); |
+ const float kOffset = kDefaultDrawableSize / 2.f; |
+ |
+ // The handle must be visible to trigger drag. |
+ MockMotionEvent event( |
+ MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(IsDragging()); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ |
+ // ACTION_DOWN must fall within the drawable region to trigger drag. |
+ event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 50); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(IsDragging()); |
+ |
+ // Only ACTION_DOWN will trigger drag. |
+ event = MockMotionEvent( |
+ MockMotionEvent::ACTION_MOVE, event_time, kOffset, kOffset); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(IsDragging()); |
+ |
+ // Start the drag. |
+ event = MockMotionEvent( |
+ MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(IsDragging()); |
+ |
+ event = MockMotionEvent( |
+ MockMotionEvent::ACTION_MOVE, event_time, kOffset + 10, kOffset + 15); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(GetAndResetHandleDragged()); |
+ EXPECT_TRUE(IsDragging()); |
+ EXPECT_EQ(gfx::PointF(10, 15), DragPosition()); |
+ |
+ event = MockMotionEvent( |
+ MockMotionEvent::ACTION_MOVE, event_time, kOffset - 10, kOffset - 15); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(GetAndResetHandleDragged()); |
+ EXPECT_TRUE(IsDragging()); |
+ EXPECT_EQ(gfx::PointF(-10, -15), DragPosition()); |
+ |
+ event = MockMotionEvent(MockMotionEvent::ACTION_UP); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(GetAndResetHandleDragged()); |
+ EXPECT_FALSE(IsDragging()); |
+ |
+ // Non-ACTION_DOWN events after the drag has terminated should not be handled. |
+ event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL); |
+ EXPECT_FALSE(handle.WillHandleTouchEvent(event)); |
+} |
+ |
+TEST_F(TouchHandleTest, DragDefersOrientationChange) { |
+ TouchHandle handle(this, TOUCH_HANDLE_RIGHT); |
+ ASSERT_EQ(drawable().orientation, TOUCH_HANDLE_RIGHT); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ |
+ MockMotionEvent event(MockMotionEvent::ACTION_DOWN); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(IsDragging()); |
+ |
+ // Orientation changes will be deferred until the drag ends. |
+ handle.SetOrientation(TOUCH_HANDLE_LEFT); |
+ EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation); |
+ |
+ event = MockMotionEvent(MockMotionEvent::ACTION_MOVE); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(GetAndResetHandleDragged()); |
+ EXPECT_TRUE(IsDragging()); |
+ EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation); |
+ |
+ event = MockMotionEvent(MockMotionEvent::ACTION_UP); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(GetAndResetHandleDragged()); |
+ EXPECT_FALSE(IsDragging()); |
+ EXPECT_EQ(TOUCH_HANDLE_LEFT, drawable().orientation); |
+} |
+ |
+TEST_F(TouchHandleTest, DragDefersFade) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ |
+ MockMotionEvent event(MockMotionEvent::ACTION_DOWN); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(IsDragging()); |
+ |
+ // Fade will be deferred until the drag ends. |
+ handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH); |
+ EXPECT_FALSE(NeedsAnimate()); |
+ EXPECT_TRUE(drawable().visible); |
+ EXPECT_EQ(1.f, drawable().alpha); |
+ |
+ event = MockMotionEvent(MockMotionEvent::ACTION_MOVE); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(NeedsAnimate()); |
+ EXPECT_TRUE(drawable().visible); |
+ |
+ event = MockMotionEvent(MockMotionEvent::ACTION_UP); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(IsDragging()); |
+ EXPECT_TRUE(NeedsAnimate()); |
+ |
+ Animate(handle); |
+ EXPECT_FALSE(drawable().visible); |
+ EXPECT_EQ(0.f, drawable().alpha); |
+} |
+ |
+TEST_F(TouchHandleTest, Tap) { |
+ TouchHandle handle(this, TOUCH_HANDLE_CENTER); |
+ handle.SetVisible(true, TouchHandle::ANIMATION_NONE); |
+ |
+ base::TimeTicks event_time = base::TimeTicks::Now(); |
+ |
+ // ACTION_CANCEL shouldn't trigger a tap. |
+ MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ event_time += base::TimeDelta::FromMilliseconds(50); |
+ event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(GetAndResetHandleTapped()); |
+ |
+ // Long press shouldn't trigger a tap. |
+ event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ event_time += base::TimeDelta::FromMilliseconds(500); |
+ event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_FALSE(GetAndResetHandleTapped()); |
+ |
+ // Only a brief tap should trigger a tap. |
+ event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ event_time += base::TimeDelta::FromMilliseconds(50); |
+ event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); |
+ EXPECT_TRUE(handle.WillHandleTouchEvent(event)); |
+ EXPECT_TRUE(GetAndResetHandleTapped()); |
+} |
+ |
+} // namespace content |