// Copyright 2013 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 "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "content/browser/renderer_host/input/synthetic_gesture.h" #include "content/browser/renderer_host/input/synthetic_gesture_controller.h" #include "content/browser/renderer_host/input/synthetic_gesture_target.h" #include "content/browser/renderer_host/input/synthetic_pinch_gesture.h" #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h" #include "content/browser/renderer_host/input/synthetic_tap_gesture.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/common/input/synthetic_pinch_gesture_params.h" #include "content/common/input/synthetic_smooth_scroll_gesture_params.h" #include "content/common/input/synthetic_tap_gesture_params.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "content/test/test_render_view_host.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/gfx/point.h" #include "ui/gfx/point_f.h" #include "ui/gfx/vector2d.h" #include "ui/gfx/vector2d_f.h" using blink::WebInputEvent; using blink::WebMouseEvent; using blink::WebMouseWheelEvent; using blink::WebTouchEvent; namespace content { namespace { const int kFlushInputRateInMs = 16; const int kPointerAssumedStoppedTimeMs = 43; const float kTouchSlopInDips = 7.0f; const float kMinScalingSpanInDips = 27.5f; class MockSyntheticGesture : public SyntheticGesture { public: MockSyntheticGesture(bool* finished, int num_steps) : finished_(finished), num_steps_(num_steps), step_count_(0) { *finished_ = false; } ~MockSyntheticGesture() override {} Result ForwardInputEvents(const base::TimeTicks& timestamp, SyntheticGestureTarget* target) override { step_count_++; if (step_count_ == num_steps_) { *finished_ = true; return SyntheticGesture::GESTURE_FINISHED; } else if (step_count_ > num_steps_) { *finished_ = true; // Return arbitrary failure. return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; } return SyntheticGesture::GESTURE_RUNNING; } protected: bool* finished_; int num_steps_; int step_count_; }; class MockSyntheticGestureTarget : public SyntheticGestureTarget { public: MockSyntheticGestureTarget() : flush_requested_(false), pointer_assumed_stopped_time_ms_(kPointerAssumedStoppedTimeMs) {} ~MockSyntheticGestureTarget() override {} // SyntheticGestureTarget: void DispatchInputEventToPlatform(const WebInputEvent& event) override {} void SetNeedsFlush() override { flush_requested_ = true; } SyntheticGestureParams::GestureSourceType GetDefaultSyntheticGestureSourceType() const override { return SyntheticGestureParams::TOUCH_INPUT; } base::TimeDelta PointerAssumedStoppedTime() const override { return base::TimeDelta::FromMilliseconds(pointer_assumed_stopped_time_ms_); } void set_pointer_assumed_stopped_time_ms(int time_ms) { pointer_assumed_stopped_time_ms_ = time_ms; } float GetTouchSlopInDips() const override { return kTouchSlopInDips; } float GetMinScalingSpanInDips() const override { return kMinScalingSpanInDips; } bool flush_requested() const { return flush_requested_; } void ClearFlushRequest() { flush_requested_ = false; } private: bool flush_requested_; int pointer_assumed_stopped_time_ms_; }; class MockScrollGestureTarget : public MockSyntheticGestureTarget { public: MockScrollGestureTarget() : total_abs_scroll_distance_length_(0) {} ~MockScrollGestureTarget() override {} gfx::Vector2dF start_to_end_distance() const { return start_to_end_distance_; } float total_abs_scroll_distance_length() const { return total_abs_scroll_distance_length_; } protected: gfx::Vector2dF start_to_end_distance_; float total_abs_scroll_distance_length_; }; class MockScrollMouseTarget : public MockScrollGestureTarget { public: MockScrollMouseTarget() {} ~MockScrollMouseTarget() override {} void DispatchInputEventToPlatform(const WebInputEvent& event) override { ASSERT_EQ(event.type, WebInputEvent::MouseWheel); const WebMouseWheelEvent& mouse_wheel_event = static_cast(event); gfx::Vector2dF delta(mouse_wheel_event.deltaX, mouse_wheel_event.deltaY); start_to_end_distance_ += delta; total_abs_scroll_distance_length_ += delta.Length(); } }; class MockScrollTouchTarget : public MockScrollGestureTarget { public: MockScrollTouchTarget() : started_(false) {} ~MockScrollTouchTarget() override {} void DispatchInputEventToPlatform(const WebInputEvent& event) override { ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type)); const WebTouchEvent& touch_event = static_cast(event); ASSERT_EQ(touch_event.touchesLength, 1U); if (!started_) { ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart); start_.SetPoint(touch_event.touches[0].position.x, touch_event.touches[0].position.y); last_touch_point_ = start_; started_ = true; } else { ASSERT_NE(touch_event.type, WebInputEvent::TouchStart); ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel); gfx::PointF touch_point(touch_event.touches[0].position.x, touch_event.touches[0].position.y); gfx::Vector2dF delta = touch_point - last_touch_point_; total_abs_scroll_distance_length_ += delta.Length(); if (touch_event.type == WebInputEvent::TouchEnd) start_to_end_distance_ = touch_point - start_; last_touch_point_ = touch_point; } } protected: gfx::Point start_; gfx::PointF last_touch_point_; bool started_; }; class MockSyntheticPinchTouchTarget : public MockSyntheticGestureTarget { public: enum ZoomDirection { ZOOM_DIRECTION_UNKNOWN, ZOOM_IN, ZOOM_OUT }; MockSyntheticPinchTouchTarget() : initial_pointer_distance_(0), last_pointer_distance_(0), zoom_direction_(ZOOM_DIRECTION_UNKNOWN), started_(false) {} ~MockSyntheticPinchTouchTarget() override {} void DispatchInputEventToPlatform(const WebInputEvent& event) override { ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type)); const WebTouchEvent& touch_event = static_cast(event); ASSERT_EQ(touch_event.touchesLength, 2U); if (!started_) { ASSERT_EQ(touch_event.type, WebInputEvent::TouchStart); start_0_ = gfx::PointF(touch_event.touches[0].position); start_1_ = gfx::PointF(touch_event.touches[1].position); last_pointer_distance_ = (start_0_ - start_1_).Length(); initial_pointer_distance_ = last_pointer_distance_; EXPECT_GE(initial_pointer_distance_, GetMinScalingSpanInDips()); started_ = true; } else { ASSERT_NE(touch_event.type, WebInputEvent::TouchStart); ASSERT_NE(touch_event.type, WebInputEvent::TouchCancel); gfx::PointF current_0 = gfx::PointF(touch_event.touches[0].position); gfx::PointF current_1 = gfx::PointF(touch_event.touches[1].position); float pointer_distance = (current_0 - current_1).Length(); if (last_pointer_distance_ != pointer_distance) { if (zoom_direction_ == ZOOM_DIRECTION_UNKNOWN) zoom_direction_ = ComputeZoomDirection(last_pointer_distance_, pointer_distance); else EXPECT_EQ( zoom_direction_, ComputeZoomDirection(last_pointer_distance_, pointer_distance)); } last_pointer_distance_ = pointer_distance; } } ZoomDirection zoom_direction() const { return zoom_direction_; } float ComputeScaleFactor() const { switch (zoom_direction_) { case ZOOM_IN: return last_pointer_distance_ / (initial_pointer_distance_ + 2 * GetTouchSlopInDips()); case ZOOM_OUT: return last_pointer_distance_ / (initial_pointer_distance_ - 2 * GetTouchSlopInDips()); case ZOOM_DIRECTION_UNKNOWN: return 1.0f; default: NOTREACHED(); return 0.0f; } } private: ZoomDirection ComputeZoomDirection(float last_pointer_distance, float current_pointer_distance) { DCHECK_NE(last_pointer_distance, current_pointer_distance); return last_pointer_distance < current_pointer_distance ? ZOOM_IN : ZOOM_OUT; } float initial_pointer_distance_; float last_pointer_distance_; ZoomDirection zoom_direction_; gfx::PointF start_0_; gfx::PointF start_1_; bool started_; }; class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget { public: MockSyntheticTapGestureTarget() : state_(NOT_STARTED) {} ~MockSyntheticTapGestureTarget() override {} bool GestureFinished() const { return state_ == FINISHED; } gfx::PointF position() const { return position_; } base::TimeDelta GetDuration() const { return stop_time_ - start_time_; } protected: enum GestureState { NOT_STARTED, STARTED, FINISHED }; gfx::PointF position_; base::TimeDelta start_time_; base::TimeDelta stop_time_; GestureState state_; }; class MockSyntheticTapTouchTarget : public MockSyntheticTapGestureTarget { public: MockSyntheticTapTouchTarget() {} ~MockSyntheticTapTouchTarget() override {} void DispatchInputEventToPlatform(const WebInputEvent& event) override { ASSERT_TRUE(WebInputEvent::isTouchEventType(event.type)); const WebTouchEvent& touch_event = static_cast(event); ASSERT_EQ(touch_event.touchesLength, 1U); switch (state_) { case NOT_STARTED: EXPECT_EQ(touch_event.type, WebInputEvent::TouchStart); position_ = gfx::PointF(touch_event.touches[0].position); start_time_ = base::TimeDelta::FromMilliseconds( static_cast(touch_event.timeStampSeconds * 1000)); state_ = STARTED; break; case STARTED: EXPECT_EQ(touch_event.type, WebInputEvent::TouchEnd); EXPECT_EQ(position_, gfx::PointF(touch_event.touches[0].position)); stop_time_ = base::TimeDelta::FromMilliseconds( static_cast(touch_event.timeStampSeconds * 1000)); state_ = FINISHED; break; case FINISHED: EXPECT_FALSE(true); break; } } }; class MockSyntheticTapMouseTarget : public MockSyntheticTapGestureTarget { public: MockSyntheticTapMouseTarget() {} ~MockSyntheticTapMouseTarget() override {} void DispatchInputEventToPlatform(const WebInputEvent& event) override { ASSERT_TRUE(WebInputEvent::isMouseEventType(event.type)); const WebMouseEvent& mouse_event = static_cast(event); switch (state_) { case NOT_STARTED: EXPECT_EQ(mouse_event.type, WebInputEvent::MouseDown); EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft); EXPECT_EQ(mouse_event.clickCount, 1); position_ = gfx::PointF(mouse_event.x, mouse_event.y); start_time_ = base::TimeDelta::FromMilliseconds( static_cast(mouse_event.timeStampSeconds * 1000)); state_ = STARTED; break; case STARTED: EXPECT_EQ(mouse_event.type, WebInputEvent::MouseUp); EXPECT_EQ(mouse_event.button, WebMouseEvent::ButtonLeft); EXPECT_EQ(mouse_event.clickCount, 1); EXPECT_EQ(position_, gfx::PointF(mouse_event.x, mouse_event.y)); stop_time_ = base::TimeDelta::FromMilliseconds( static_cast(mouse_event.timeStampSeconds * 1000)); state_ = FINISHED; break; case FINISHED: EXPECT_FALSE(true); break; } } }; class SyntheticGestureControllerTest : public testing::Test { public: SyntheticGestureControllerTest() {} ~SyntheticGestureControllerTest() override {} protected: template void CreateControllerAndTarget() { target_ = new MockGestureTarget(); controller_.reset(new SyntheticGestureController( scoped_ptr(target_))); } void SetUp() override { start_time_ = base::TimeTicks::Now(); time_ = start_time_; num_success_ = 0; num_failure_ = 0; } void TearDown() override { controller_.reset(); target_ = NULL; time_ = base::TimeTicks(); } void QueueSyntheticGesture(scoped_ptr gesture) { controller_->QueueSyntheticGesture(gesture.Pass(), base::Bind(&SyntheticGestureControllerTest::OnSyntheticGestureCompleted, base::Unretained(this))); } void FlushInputUntilComplete() { while (target_->flush_requested()) { while (target_->flush_requested()) { target_->ClearFlushRequest(); time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs); controller_->Flush(time_); } controller_->OnDidFlushInput(); } } void OnSyntheticGestureCompleted(SyntheticGesture::Result result) { DCHECK_NE(result, SyntheticGesture::GESTURE_RUNNING); if (result == SyntheticGesture::GESTURE_FINISHED) num_success_++; else num_failure_++; } base::TimeDelta GetTotalTime() const { return time_ - start_time_; } MockSyntheticGestureTarget* target_; scoped_ptr controller_; base::TimeTicks start_time_; base::TimeTicks time_; int num_success_; int num_failure_; }; TEST_F(SyntheticGestureControllerTest, SingleGesture) { CreateControllerAndTarget(); bool finished = false; scoped_ptr gesture( new MockSyntheticGesture(&finished, 3)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); EXPECT_TRUE(finished); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); } TEST_F(SyntheticGestureControllerTest, GestureFailed) { CreateControllerAndTarget(); bool finished = false; scoped_ptr gesture( new MockSyntheticGesture(&finished, 0)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); EXPECT_TRUE(finished); EXPECT_EQ(1, num_failure_); EXPECT_EQ(0, num_success_); } TEST_F(SyntheticGestureControllerTest, SuccessiveGestures) { CreateControllerAndTarget(); bool finished_1 = false; scoped_ptr gesture_1( new MockSyntheticGesture(&finished_1, 2)); bool finished_2 = false; scoped_ptr gesture_2( new MockSyntheticGesture(&finished_2, 4)); // Queue first gesture and wait for it to finish QueueSyntheticGesture(gesture_1.Pass()); FlushInputUntilComplete(); EXPECT_TRUE(finished_1); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); // Queue second gesture. QueueSyntheticGesture(gesture_2.Pass()); FlushInputUntilComplete(); EXPECT_TRUE(finished_2); EXPECT_EQ(2, num_success_); EXPECT_EQ(0, num_failure_); } TEST_F(SyntheticGestureControllerTest, TwoGesturesInFlight) { CreateControllerAndTarget(); bool finished_1 = false; scoped_ptr gesture_1( new MockSyntheticGesture(&finished_1, 2)); bool finished_2 = false; scoped_ptr gesture_2( new MockSyntheticGesture(&finished_2, 4)); QueueSyntheticGesture(gesture_1.Pass()); QueueSyntheticGesture(gesture_2.Pass()); FlushInputUntilComplete(); EXPECT_TRUE(finished_1); EXPECT_TRUE(finished_2); EXPECT_EQ(2, num_success_); EXPECT_EQ(0, num_failure_); } TEST_F(SyntheticGestureControllerTest, GestureCompletedOnDidFlushInput) { CreateControllerAndTarget(); bool finished_1, finished_2; scoped_ptr gesture_1( new MockSyntheticGesture(&finished_1, 2)); scoped_ptr gesture_2( new MockSyntheticGesture(&finished_2, 4)); QueueSyntheticGesture(gesture_1.Pass()); QueueSyntheticGesture(gesture_2.Pass()); while (target_->flush_requested()) { target_->ClearFlushRequest(); time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs); controller_->Flush(time_); } EXPECT_EQ(0, num_success_); controller_->OnDidFlushInput(); EXPECT_EQ(1, num_success_); while (target_->flush_requested()) { target_->ClearFlushRequest(); time_ += base::TimeDelta::FromMilliseconds(kFlushInputRateInMs); controller_->Flush(time_); } EXPECT_EQ(1, num_success_); controller_->OnDidFlushInput(); EXPECT_EQ(2, num_success_); } gfx::Vector2d AddTouchSlopToVector(const gfx::Vector2d& vector, SyntheticGestureTarget* target) { const int kTouchSlop = target->GetTouchSlopInDips(); int x = vector.x(); if (x > 0) x += kTouchSlop; else if (x < 0) x -= kTouchSlop; int y = vector.y(); if (y > 0) y += kTouchSlop; else if (y < 0) y -= kTouchSlop; return gfx::Vector2d(x, y); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchVertical) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(89, 32); params.distances.push_back(gfx::Vector2d(0, 123)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_), scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchHorizontal) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(12, -23); params.distances.push_back(gfx::Vector2d(-234, 0)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(AddTouchSlopToVector(params.distances[0], target_), scroll_target->start_to_end_distance()); } void CheckIsWithinRangeSingle(float scroll_distance, int target_distance, SyntheticGestureTarget* target) { if (target_distance > 0) { EXPECT_LE(target_distance, scroll_distance); EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips()); } else { EXPECT_GE(target_distance, scroll_distance); EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips()); } } void CheckSingleScrollDistanceIsWithinRange( const gfx::Vector2dF& scroll_distance, const gfx::Vector2d& target_distance, SyntheticGestureTarget* target) { CheckIsWithinRangeSingle(scroll_distance.x(), target_distance.x(), target); CheckIsWithinRangeSingle(scroll_distance.y(), target_distance.y(), target); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchDiagonal) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(0, 7); params.distances.push_back(gfx::Vector2d(413, -83)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); CheckSingleScrollDistanceIsWithinRange( scroll_target->start_to_end_distance(), params.distances[0], target_); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchLongStop) { CreateControllerAndTarget(); // Create a smooth scroll with a short distance and set the pointer assumed // stopped time high, so that the stopping should dominate the time the // gesture is active. SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(-98, -23); params.distances.push_back(gfx::Vector2d(21, -12)); params.prevent_fling = true; target_->set_pointer_assumed_stopped_time_ms(543); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); CheckSingleScrollDistanceIsWithinRange( scroll_target->start_to_end_distance(), params.distances[0], target_); EXPECT_GE(GetTotalTime(), target_->PointerAssumedStoppedTime()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchFling) { CreateControllerAndTarget(); // Create a smooth scroll with a short distance and set the pointer assumed // stopped time high. Disable 'prevent_fling' and check that the gesture // finishes without waiting before it stops. SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(-89, 78); params.distances.push_back(gfx::Vector2d(-43, 19)); params.prevent_fling = false; target_->set_pointer_assumed_stopped_time_ms(543); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); CheckSingleScrollDistanceIsWithinRange( scroll_target->start_to_end_distance(), params.distances[0], target_); EXPECT_LE(GetTotalTime(), target_->PointerAssumedStoppedTime()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureTouchZeroDistance) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(-32, 43); params.distances.push_back(gfx::Vector2d(0, 0)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseVertical) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.anchor.SetPoint(432, 89); params.distances.push_back(gfx::Vector2d(0, -234)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseHorizontal) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.anchor.SetPoint(90, 12); params.distances.push_back(gfx::Vector2d(345, 0)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, SingleScrollGestureMouseDiagonal) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.anchor.SetPoint(90, 12); params.distances.push_back(gfx::Vector2d(-194, 303)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(params.distances[0], scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouse) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.anchor.SetPoint(90, 12); params.distances.push_back(gfx::Vector2d(-129, 212)); params.distances.push_back(gfx::Vector2d(8, -9)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(params.distances[0] + params.distances[1], scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, MultiScrollGestureMouseHorizontal) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.anchor.SetPoint(90, 12); params.distances.push_back(gfx::Vector2d(-129, 0)); params.distances.push_back(gfx::Vector2d(79, 0)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); // This check only works for horizontal or vertical scrolls because of // floating point precision issues with diagonal scrolls. EXPECT_FLOAT_EQ(params.distances[0].Length() + params.distances[1].Length(), scroll_target->total_abs_scroll_distance_length()); EXPECT_EQ(params.distances[0] + params.distances[1], scroll_target->start_to_end_distance()); } void CheckIsWithinRangeMulti(float scroll_distance, int target_distance, SyntheticGestureTarget* target) { if (target_distance > 0) { EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips()); EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips()); } else { EXPECT_LE(scroll_distance, target_distance + target->GetTouchSlopInDips()); EXPECT_GE(scroll_distance, target_distance - target->GetTouchSlopInDips()); } } void CheckMultiScrollDistanceIsWithinRange( const gfx::Vector2dF& scroll_distance, const gfx::Vector2d& target_distance, SyntheticGestureTarget* target) { CheckIsWithinRangeMulti(scroll_distance.x(), target_distance.x(), target); CheckIsWithinRangeMulti(scroll_distance.y(), target_distance.y(), target); } TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouch) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(8, -13); params.distances.push_back(gfx::Vector2d(234, 133)); params.distances.push_back(gfx::Vector2d(-9, 78)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); CheckMultiScrollDistanceIsWithinRange( scroll_target->start_to_end_distance(), params.distances[0] + params.distances[1], target_); } TEST_F(SyntheticGestureControllerTest, MultiScrollGestureTouchVertical) { CreateControllerAndTarget(); SyntheticSmoothScrollGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.anchor.SetPoint(234, -13); params.distances.push_back(gfx::Vector2d(0, 133)); params.distances.push_back(gfx::Vector2d(0, 78)); scoped_ptr gesture( new SyntheticSmoothScrollGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockScrollGestureTarget* scroll_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_FLOAT_EQ( params.distances[0].Length() + params.distances[1].Length() + target_->GetTouchSlopInDips(), scroll_target->total_abs_scroll_distance_length()); EXPECT_EQ(AddTouchSlopToVector(params.distances[0] + params.distances[1], target_), scroll_target->start_to_end_distance()); } TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomIn) { CreateControllerAndTarget(); SyntheticPinchGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.scale_factor = 2.3f; params.anchor.SetPoint(54, 89); scoped_ptr gesture(new SyntheticPinchGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockSyntheticPinchTouchTarget* pinch_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(pinch_target->zoom_direction(), MockSyntheticPinchTouchTarget::ZOOM_IN); EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor()); } TEST_F(SyntheticGestureControllerTest, PinchGestureTouchZoomOut) { CreateControllerAndTarget(); SyntheticPinchGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.scale_factor = 0.4f; params.anchor.SetPoint(-12, 93); scoped_ptr gesture(new SyntheticPinchGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockSyntheticPinchTouchTarget* pinch_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(pinch_target->zoom_direction(), MockSyntheticPinchTouchTarget::ZOOM_OUT); EXPECT_FLOAT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor()); } TEST_F(SyntheticGestureControllerTest, PinchGestureTouchNoScaling) { CreateControllerAndTarget(); SyntheticPinchGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.scale_factor = 1.0f; scoped_ptr gesture(new SyntheticPinchGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockSyntheticPinchTouchTarget* pinch_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_EQ(pinch_target->zoom_direction(), MockSyntheticPinchTouchTarget::ZOOM_DIRECTION_UNKNOWN); EXPECT_EQ(params.scale_factor, pinch_target->ComputeScaleFactor()); } TEST_F(SyntheticGestureControllerTest, TapGestureTouch) { CreateControllerAndTarget(); SyntheticTapGestureParams params; params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT; params.duration_ms = 123; params.position.SetPoint(87, -124); scoped_ptr gesture(new SyntheticTapGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockSyntheticTapTouchTarget* tap_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_TRUE(tap_target->GestureFinished()); EXPECT_EQ(tap_target->position(), params.position); EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms); EXPECT_GE(GetTotalTime(), base::TimeDelta::FromMilliseconds(params.duration_ms)); } TEST_F(SyntheticGestureControllerTest, TapGestureMouse) { CreateControllerAndTarget(); SyntheticTapGestureParams params; params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT; params.duration_ms = 79; params.position.SetPoint(98, 123); scoped_ptr gesture(new SyntheticTapGesture(params)); QueueSyntheticGesture(gesture.Pass()); FlushInputUntilComplete(); MockSyntheticTapMouseTarget* tap_target = static_cast(target_); EXPECT_EQ(1, num_success_); EXPECT_EQ(0, num_failure_); EXPECT_TRUE(tap_target->GestureFinished()); EXPECT_EQ(tap_target->position(), params.position); EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms); EXPECT_GE(GetTotalTime(), base::TimeDelta::FromMilliseconds(params.duration_ms)); } } // namespace } // namespace content