// Copyright (c) 2012 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 <stdint.h> #include <list> #include "base/command_line.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/timer/timer.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/env.h" #include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/base/hit_test.h" #include "ui/base/ui_base_switches.h" #include "ui/events/event.h" #include "ui/events/event_switches.h" #include "ui/events/event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/gestures/gesture_recognizer_impl.h" #include "ui/events/gestures/gesture_types.h" #include "ui/events/test/event_generator.h" #include "ui/events/test/events_test_utils.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" namespace aura { namespace test { namespace { std::string WindowIDAsString(ui::GestureConsumer* consumer) { return consumer ? base::IntToString(static_cast<Window*>(consumer)->id()) : "?"; } #define EXPECT_0_EVENTS(events) \ EXPECT_EQ(0u, events.size()) #define EXPECT_1_EVENT(events, e0) \ EXPECT_EQ(1u, events.size()); \ EXPECT_EQ(e0, events[0]) #define EXPECT_2_EVENTS(events, e0, e1) \ EXPECT_EQ(2u, events.size()); \ EXPECT_EQ(e0, events[0]); \ EXPECT_EQ(e1, events[1]) #define EXPECT_3_EVENTS(events, e0, e1, e2) \ EXPECT_EQ(3u, events.size()); \ EXPECT_EQ(e0, events[0]); \ EXPECT_EQ(e1, events[1]); \ EXPECT_EQ(e2, events[2]) #define EXPECT_4_EVENTS(events, e0, e1, e2, e3) \ EXPECT_EQ(4u, events.size()); \ EXPECT_EQ(e0, events[0]); \ EXPECT_EQ(e1, events[1]); \ EXPECT_EQ(e2, events[2]); \ EXPECT_EQ(e3, events[3]) // A delegate that keeps track of gesture events. class GestureEventConsumeDelegate : public TestWindowDelegate { public: GestureEventConsumeDelegate() : tap_(false), tap_down_(false), tap_cancel_(false), begin_(false), end_(false), scroll_begin_(false), scroll_update_(false), scroll_end_(false), pinch_begin_(false), pinch_update_(false), pinch_end_(false), long_press_(false), fling_(false), two_finger_tap_(false), show_press_(false), swipe_left_(false), swipe_right_(false), swipe_up_(false), swipe_down_(false), scroll_x_(0), scroll_y_(0), scroll_velocity_x_(0), scroll_velocity_y_(0), velocity_x_(0), velocity_y_(0), scroll_x_hint_(0), scroll_y_hint_(0), tap_count_(0), flags_(0), wait_until_event_(ui::ET_UNKNOWN) {} ~GestureEventConsumeDelegate() override {} void Reset() { events_.clear(); tap_ = false; tap_down_ = false; tap_cancel_ = false; begin_ = false; end_ = false; scroll_begin_ = false; scroll_update_ = false; scroll_end_ = false; pinch_begin_ = false; pinch_update_ = false; pinch_end_ = false; long_press_ = false; fling_ = false; two_finger_tap_ = false; show_press_ = false; swipe_left_ = false; swipe_right_ = false; swipe_up_ = false; swipe_down_ = false; scroll_begin_position_.SetPoint(0, 0); tap_location_.SetPoint(0, 0); gesture_end_location_.SetPoint(0, 0); scroll_x_ = 0; scroll_y_ = 0; scroll_velocity_x_ = 0; scroll_velocity_y_ = 0; velocity_x_ = 0; velocity_y_ = 0; scroll_x_hint_ = 0; scroll_y_hint_ = 0; tap_count_ = 0; scale_ = 0; flags_ = 0; } const std::vector<ui::EventType>& events() const { return events_; }; bool tap() const { return tap_; } bool tap_down() const { return tap_down_; } bool tap_cancel() const { return tap_cancel_; } bool begin() const { return begin_; } bool end() const { return end_; } bool scroll_begin() const { return scroll_begin_; } bool scroll_update() const { return scroll_update_; } bool scroll_end() const { return scroll_end_; } bool pinch_begin() const { return pinch_begin_; } bool pinch_update() const { return pinch_update_; } bool pinch_end() const { return pinch_end_; } bool long_press() const { return long_press_; } bool long_tap() const { return long_tap_; } bool fling() const { return fling_; } bool two_finger_tap() const { return two_finger_tap_; } bool show_press() const { return show_press_; } bool swipe_left() const { return swipe_left_; } bool swipe_right() const { return swipe_right_; } bool swipe_up() const { return swipe_up_; } bool swipe_down() const { return swipe_down_; } const gfx::Point& scroll_begin_position() const { return scroll_begin_position_; } const gfx::Point& tap_location() const { return tap_location_; } const gfx::Point& gesture_end_location() const { return gesture_end_location_; } float scroll_x() const { return scroll_x_; } float scroll_y() const { return scroll_y_; } float scroll_velocity_x() const { return scroll_velocity_x_; } float scroll_velocity_y() const { return scroll_velocity_y_; } float velocity_x() const { return velocity_x_; } float velocity_y() const { return velocity_y_; } float scroll_x_hint() const { return scroll_x_hint_; } float scroll_y_hint() const { return scroll_y_hint_; } float scale() const { return scale_; } const gfx::Rect& bounding_box() const { return bounding_box_; } int tap_count() const { return tap_count_; } int flags() const { return flags_; } void WaitUntilReceivedGesture(ui::EventType type) { wait_until_event_ = type; run_loop_.reset(new base::RunLoop()); run_loop_->Run(); } void OnGestureEvent(ui::GestureEvent* gesture) override { events_.push_back(gesture->type()); bounding_box_ = gesture->details().bounding_box(); flags_ = gesture->flags(); switch (gesture->type()) { case ui::ET_GESTURE_TAP: tap_location_ = gesture->location(); tap_count_ = gesture->details().tap_count(); tap_ = true; break; case ui::ET_GESTURE_TAP_DOWN: tap_down_ = true; break; case ui::ET_GESTURE_TAP_CANCEL: tap_cancel_ = true; break; case ui::ET_GESTURE_BEGIN: begin_ = true; break; case ui::ET_GESTURE_END: end_ = true; gesture_end_location_ = gesture->location(); break; case ui::ET_GESTURE_SCROLL_BEGIN: scroll_begin_ = true; scroll_begin_position_ = gesture->location(); scroll_x_hint_ = gesture->details().scroll_x_hint(); scroll_y_hint_ = gesture->details().scroll_y_hint(); break; case ui::ET_GESTURE_SCROLL_UPDATE: scroll_update_ = true; scroll_x_ += gesture->details().scroll_x(); scroll_y_ += gesture->details().scroll_y(); break; case ui::ET_GESTURE_SCROLL_END: EXPECT_TRUE(velocity_x_ == 0 && velocity_y_ == 0); scroll_end_ = true; break; case ui::ET_GESTURE_PINCH_BEGIN: pinch_begin_ = true; break; case ui::ET_GESTURE_PINCH_UPDATE: pinch_update_ = true; scale_ = gesture->details().scale(); break; case ui::ET_GESTURE_PINCH_END: pinch_end_ = true; break; case ui::ET_GESTURE_LONG_PRESS: long_press_ = true; break; case ui::ET_GESTURE_LONG_TAP: long_tap_ = true; break; case ui::ET_SCROLL_FLING_START: EXPECT_TRUE(gesture->details().velocity_x() != 0 || gesture->details().velocity_y() != 0); EXPECT_FALSE(scroll_end_); fling_ = true; velocity_x_ = gesture->details().velocity_x(); velocity_y_ = gesture->details().velocity_y(); break; case ui::ET_GESTURE_TWO_FINGER_TAP: two_finger_tap_ = true; break; case ui::ET_GESTURE_SHOW_PRESS: show_press_ = true; break; case ui::ET_GESTURE_SWIPE: swipe_left_ = gesture->details().swipe_left(); swipe_right_ = gesture->details().swipe_right(); swipe_up_ = gesture->details().swipe_up(); swipe_down_ = gesture->details().swipe_down(); break; case ui::ET_SCROLL_FLING_CANCEL: // Only used in unified gesture detection. break; default: NOTREACHED(); } if (wait_until_event_ == gesture->type() && run_loop_) { run_loop_->Quit(); wait_until_event_ = ui::ET_UNKNOWN; } gesture->StopPropagation(); } private: scoped_ptr<base::RunLoop> run_loop_; std::vector<ui::EventType> events_; bool tap_; bool tap_down_; bool tap_cancel_; bool begin_; bool end_; bool scroll_begin_; bool scroll_update_; bool scroll_end_; bool pinch_begin_; bool pinch_update_; bool pinch_end_; bool long_press_; bool long_tap_; bool fling_; bool two_finger_tap_; bool show_press_; bool swipe_left_; bool swipe_right_; bool swipe_up_; bool swipe_down_; gfx::Point scroll_begin_position_; gfx::Point tap_location_; gfx::Point gesture_end_location_; float scroll_x_; float scroll_y_; float scroll_velocity_x_; float scroll_velocity_y_; float velocity_x_; float velocity_y_; float scroll_x_hint_; float scroll_y_hint_; float scale_; gfx::Rect bounding_box_; int tap_count_; int flags_; ui::EventType wait_until_event_; DISALLOW_COPY_AND_ASSIGN(GestureEventConsumeDelegate); }; class QueueTouchEventDelegate : public GestureEventConsumeDelegate { public: explicit QueueTouchEventDelegate(WindowEventDispatcher* dispatcher) : window_(NULL), dispatcher_(dispatcher), synchronous_ack_for_next_event_(AckState::PENDING) {} ~QueueTouchEventDelegate() override {} void OnTouchEvent(ui::TouchEvent* event) override { event->DisableSynchronousHandling(); if (synchronous_ack_for_next_event_ != AckState::PENDING) { ui::GestureRecognizer::Get()->AckTouchEvent( event->unique_event_id(), synchronous_ack_for_next_event_ == AckState::CONSUMED ? ui::ER_CONSUMED : ui::ER_UNHANDLED, window_); synchronous_ack_for_next_event_ = AckState::PENDING; } else { sent_events_ids_.push_back(event->unique_event_id()); } } void ReceivedAck() { ReceivedAckImpl(false); } void ReceivedAckPreventDefaulted() { ReceivedAckImpl(true); } void set_window(Window* w) { window_ = w; } void set_synchronous_ack_for_next_event(bool consumed) { DCHECK(synchronous_ack_for_next_event_ == AckState::PENDING); synchronous_ack_for_next_event_ = consumed ? AckState::CONSUMED : AckState::UNCONSUMED; } private: enum class AckState { PENDING, CONSUMED, UNCONSUMED, }; void ReceivedAckImpl(bool prevent_defaulted) { DCHECK(!sent_events_ids_.empty()); if (sent_events_ids_.empty()) return; uint32_t sent_event_id = sent_events_ids_.front(); sent_events_ids_.pop_front(); dispatcher_->ProcessedTouchEvent( sent_event_id, window_, prevent_defaulted ? ui::ER_HANDLED : ui::ER_UNHANDLED); } Window* window_; WindowEventDispatcher* dispatcher_; AckState synchronous_ack_for_next_event_; std::list<uint32_t> sent_events_ids_; DISALLOW_COPY_AND_ASSIGN(QueueTouchEventDelegate); }; // A delegate that ignores gesture events but keeps track of [synthetic] mouse // events. class GestureEventSynthDelegate : public TestWindowDelegate { public: GestureEventSynthDelegate() : mouse_enter_(false), mouse_exit_(false), mouse_press_(false), mouse_release_(false), mouse_move_(false), double_click_(false) { } void Reset() { mouse_enter_ = false; mouse_exit_ = false; mouse_press_ = false; mouse_release_ = false; mouse_move_ = false; double_click_ = false; } bool mouse_enter() const { return mouse_enter_; } bool mouse_exit() const { return mouse_exit_; } bool mouse_press() const { return mouse_press_; } bool mouse_move() const { return mouse_move_; } bool mouse_release() const { return mouse_release_; } bool double_click() const { return double_click_; } void OnMouseEvent(ui::MouseEvent* event) override { switch (event->type()) { case ui::ET_MOUSE_PRESSED: double_click_ = event->flags() & ui::EF_IS_DOUBLE_CLICK; mouse_press_ = true; break; case ui::ET_MOUSE_RELEASED: mouse_release_ = true; break; case ui::ET_MOUSE_MOVED: mouse_move_ = true; break; case ui::ET_MOUSE_ENTERED: mouse_enter_ = true; break; case ui::ET_MOUSE_EXITED: mouse_exit_ = true; break; default: NOTREACHED(); } event->SetHandled(); } private: bool mouse_enter_; bool mouse_exit_; bool mouse_press_; bool mouse_release_; bool mouse_move_; bool double_click_; DISALLOW_COPY_AND_ASSIGN(GestureEventSynthDelegate); }; class ScopedGestureRecognizerSetter { public: // Takes ownership of |new_gr|. explicit ScopedGestureRecognizerSetter(ui::GestureRecognizer* new_gr) : new_gr_(new_gr) { original_gr_ = ui::GestureRecognizer::Get(); ui::SetGestureRecognizerForTesting(new_gr_.get()); } virtual ~ScopedGestureRecognizerSetter() { ui::SetGestureRecognizerForTesting(original_gr_); } private: ui::GestureRecognizer* original_gr_; scoped_ptr<ui::GestureRecognizer> new_gr_; DISALLOW_COPY_AND_ASSIGN(ScopedGestureRecognizerSetter); }; class TimedEvents { private: int simulated_now_; public: // Use a non-zero start time to pass DCHECKs which ensure events have had a // time assigned. TimedEvents() : simulated_now_(1) { } base::TimeDelta Now() { base::TimeDelta t = base::TimeDelta::FromMilliseconds(simulated_now_); simulated_now_++; return t; } base::TimeDelta LeapForward(int time_in_millis) { simulated_now_ += time_in_millis; return base::TimeDelta::FromMilliseconds(simulated_now_); } base::TimeDelta InFuture(int time_in_millis) { return base::TimeDelta::FromMilliseconds(simulated_now_ + time_in_millis); } void SendScrollEvents(ui::EventProcessor* dispatcher, int x_start, int y_start, int dx, int dy, int touch_id, int time_step, int num_steps, GestureEventConsumeDelegate* delegate) { float x = x_start; float y = y_start; for (int i = 0; i < num_steps; i++) { x += dx; y += dy; ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(x, y), touch_id, base::TimeDelta::FromMilliseconds(simulated_now_)); ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move); ASSERT_FALSE(details.dispatcher_destroyed); simulated_now_ += time_step; } } void SendScrollEvent(ui::EventProcessor* dispatcher, float x, float y, int touch_id, GestureEventConsumeDelegate* delegate) { delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(), touch_id, base::TimeDelta::FromMilliseconds(simulated_now_)); move.set_location_f(gfx::PointF(x, y)); move.set_root_location_f(gfx::PointF(x, y)); ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move); ASSERT_FALSE(details.dispatcher_destroyed); simulated_now_++; } }; // An event handler to keep track of events. class TestEventHandler : public ui::EventHandler { public: TestEventHandler() : touch_released_count_(0), touch_pressed_count_(0), touch_moved_count_(0) {} ~TestEventHandler() override {} void OnTouchEvent(ui::TouchEvent* event) override { switch (event->type()) { case ui::ET_TOUCH_RELEASED: touch_released_count_++; break; case ui::ET_TOUCH_PRESSED: touch_pressed_count_++; break; case ui::ET_TOUCH_MOVED: touch_moved_count_++; break; case ui::ET_TOUCH_CANCELLED: cancelled_touch_points_.push_back(event->location_f()); break; default: break; } } void Reset() { touch_released_count_ = 0; touch_pressed_count_ = 0; touch_moved_count_ = 0; cancelled_touch_points_.clear(); } int touch_released_count() const { return touch_released_count_; } int touch_pressed_count() const { return touch_pressed_count_; } int touch_moved_count() const { return touch_moved_count_; } int touch_cancelled_count() const { return static_cast<int>(cancelled_touch_points_.size()); } const std::vector<gfx::PointF>& cancelled_touch_points() const { return cancelled_touch_points_; } private: int touch_released_count_; int touch_pressed_count_; int touch_moved_count_; std::vector<gfx::PointF> cancelled_touch_points_; DISALLOW_COPY_AND_ASSIGN(TestEventHandler); }; // Removes the target window from its parent when it receives a touch-cancel // event. class RemoveOnTouchCancelHandler : public TestEventHandler { public: RemoveOnTouchCancelHandler() {} ~RemoveOnTouchCancelHandler() override {} private: // ui::EventHandler: void OnTouchEvent(ui::TouchEvent* event) override { TestEventHandler::OnTouchEvent(event); if (event->type() == ui::ET_TOUCH_CANCELLED) { Window* target = static_cast<Window*>(event->target()); target->parent()->RemoveChild(target); } } DISALLOW_COPY_AND_ASSIGN(RemoveOnTouchCancelHandler); }; void DelayByLongPressTimeout() { ui::GestureProvider::Config config; base::RunLoop run_loop; base::MessageLoop::current()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), config.gesture_detector_config.longpress_timeout * 2); run_loop.Run(); } void DelayByShowPressTimeout() { ui::GestureProvider::Config config; base::RunLoop run_loop; base::MessageLoop::current()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), config.gesture_detector_config.showpress_timeout * 2); run_loop.Run(); } void SetTouchRadius(ui::TouchEvent* event, float radius_x, float radius_y) { // Using ctor (over direct struct access) due to it's special behavior with // radii. ui::PointerDetails details(ui::EventPointerType::POINTER_TYPE_TOUCH, radius_x, radius_y, event->pointer_details().force, event->pointer_details().tilt_x, event->pointer_details().tilt_y); event->set_pointer_details(details); } } // namespace class GestureRecognizerTest : public AuraTestBase, public ::testing::WithParamInterface<bool> { public: GestureRecognizerTest() {} void SetUp() override { AuraTestBase::SetUp(); ui::GestureConfiguration::GetInstance()->set_show_press_delay_in_ms(2); ui::GestureConfiguration::GetInstance()->set_long_press_time_in_ms(3); } private: DISALLOW_COPY_AND_ASSIGN(GestureRecognizerTest); }; class GestureRecognizerWithSwitchTest : public GestureRecognizerTest { public: GestureRecognizerWithSwitchTest() {} void SetUp() override { GestureRecognizerTest::SetUp(); base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kCompensateForUnstablePinchZoom); ui::GestureConfiguration::GetInstance()->set_min_pinch_update_span_delta(5); } private: DISALLOW_COPY_AND_ASSIGN(GestureRecognizerWithSwitchTest); }; // Check that appropriate touch events generate tap gesture events. TEST_F(GestureRecognizerTest, GestureEventTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->show_press()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_SHOW_PRESS); EXPECT_TRUE(delegate->show_press()); EXPECT_FALSE(delegate->tap_down()); // Make sure there is enough delay before the touch is released so that it is // recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); } // Check that appropriate touch events generate tap gesture events // when information about the touch radii are provided. TEST_F(GestureRecognizerTest, GestureEventTapRegion) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 800; const int kWindowHeight = 600; const int kTouchId = 2; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); // Test with no ET_TOUCH_MOVED events. { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); SetTouchRadius(&press, 5, 12); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); SetTouchRadius(&release, 5, 12); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); gfx::Point actual_point(delegate->tap_location()); EXPECT_EQ(24, delegate->bounding_box().width()); EXPECT_EQ(24, delegate->bounding_box().height()); EXPECT_EQ(101, actual_point.x()); EXPECT_EQ(201, actual_point.y()); } // Test with no ET_TOUCH_MOVED events but different touch points and radii. { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(365, 290), kTouchId, tes.Now()); SetTouchRadius(&press, 8, 14); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(367, 291), kTouchId, tes.LeapForward(50)); SetTouchRadius(&release, 20, 13); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); gfx::Point actual_point(delegate->tap_location()); EXPECT_EQ(40, delegate->bounding_box().width()); EXPECT_EQ(40, delegate->bounding_box().height()); EXPECT_EQ(367, actual_point.x()); EXPECT_EQ(291, actual_point.y()); } // Test with a single ET_TOUCH_MOVED event. { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(46, 205), kTouchId, tes.Now()); SetTouchRadius(&press, 6, 10); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(49, 204), kTouchId, tes.LeapForward(50)); SetTouchRadius(&move, 8, 12); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(49, 204), kTouchId, tes.LeapForward(50)); SetTouchRadius(&release, 4, 8); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); gfx::Point actual_point(delegate->tap_location()); EXPECT_EQ(16, delegate->bounding_box().width()); EXPECT_EQ(16, delegate->bounding_box().height()); EXPECT_EQ(49, actual_point.x()); EXPECT_EQ(204, actual_point.y()); } // Test with a few ET_TOUCH_MOVED events. { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(400, 150), kTouchId, tes.Now()); SetTouchRadius(&press, 7, 10); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(397, 151), kTouchId, tes.LeapForward(50)); SetTouchRadius(&move, 13, 12); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(397, 149), kTouchId, tes.LeapForward(50)); SetTouchRadius(&move1, 16, 16); DispatchEventUsingWindowDispatcher(&move1); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(400, 150), kTouchId, tes.LeapForward(50)); SetTouchRadius(&move2, 14, 10); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(401, 149), kTouchId, tes.LeapForward(50)); SetTouchRadius(&release, 8, 9); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); gfx::Point actual_point(delegate->tap_location()); EXPECT_EQ(18, delegate->bounding_box().width()); EXPECT_EQ(18, delegate->bounding_box().height()); EXPECT_EQ(401, actual_point.x()); EXPECT_EQ(149, actual_point.y()); } } // Check that appropriate touch events generate scroll gesture events. TEST_F(GestureRecognizerTest, GestureEventScroll) { // We'll start by moving the touch point by (10.5, 10.5). We want 5 dips of // that distance to be consumed by the slop, so we set the slop radius to // sqrt(5 * 5 + 5 * 5). ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(sqrt(5.f * 5 + 5 * 5)); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 5; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); // Move the touch-point enough so that it is considered as a scroll. This // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures. // The first movement is diagonal, to ensure that we have a free scroll, // and not a rail scroll. tes.SendScrollEvent(event_processor(), 111.5, 211.5, kTouchId, delegate.get()); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); // The slop consumed 5 dips EXPECT_FLOAT_EQ(5.5, delegate->scroll_x()); EXPECT_FLOAT_EQ(5.5, delegate->scroll_y()); EXPECT_EQ(gfx::Point(1, 1).ToString(), delegate->scroll_begin_position().ToString()); // When scrolling with a single finger, the bounding box of the gesture should // be empty, since it's a single point and the radius for testing is zero. EXPECT_TRUE(delegate->bounding_box().IsEmpty()); // Move some more to generate a few more scroll updates. Make sure that we get // out of the snap channel for the unified GR. tes.SendScrollEvent(event_processor(), 20, 120, kTouchId, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); EXPECT_FLOAT_EQ(-91.5, delegate->scroll_x()); EXPECT_FLOAT_EQ(-91.5, delegate->scroll_y()); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); tes.SendScrollEvent(event_processor(), 50, 124, kTouchId, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); EXPECT_EQ(30, delegate->scroll_x()); EXPECT_EQ(4, delegate->scroll_y()); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); // Release the touch. This should end the scroll. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_2_EVENTS(delegate->events(), ui::ET_SCROLL_FLING_START, ui::ET_GESTURE_END); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); } // Check that predicted scroll update positions are correct. TEST_F(GestureRecognizerTest, GestureEventScrollPrediction) { // We'll start by moving the touch point by (5, 5). We want all of that // distance to be consumed by the slop, so we set the slop radius to // sqrt(5 * 5 + 5 * 5). ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(sqrt(5.f * 5 + 5 * 5)); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 5; gfx::Rect bounds(95, 195, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); // Tracks the total scroll since we want to verify that the correct position // will be scrolled to throughout the prediction. gfx::Vector2dF total_scroll; ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(96, 196), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); delegate->Reset(); // Get rid of touch slop. ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(111, 211), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); total_scroll.set_x(total_scroll.x() + delegate->scroll_x()); total_scroll.set_y(total_scroll.y() + delegate->scroll_y()); // Move the touch-point enough so that it is considered as a scroll. This // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures. // The first movement is diagonal, to ensure that we have a free scroll, // and not a rail scroll. tes.LeapForward(30); tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); total_scroll.set_x(total_scroll.x() + delegate->scroll_x()); total_scroll.set_y(total_scroll.y() + delegate->scroll_y()); // Move some more to generate a few more scroll updates. tes.LeapForward(30); tes.SendScrollEvent(event_processor(), 110, 211, kTouchId, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); total_scroll.set_x(total_scroll.x() + delegate->scroll_x()); total_scroll.set_y(total_scroll.y() + delegate->scroll_y()); tes.LeapForward(30); tes.SendScrollEvent(event_processor(), 140, 215, kTouchId, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); total_scroll.set_x(total_scroll.x() + delegate->scroll_x()); total_scroll.set_y(total_scroll.y() + delegate->scroll_y()); // Release the touch. This should end the scroll. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); } // Check that the bounding box during a scroll event is correct. TEST_F(GestureRecognizerTest, GestureEventScrollBoundingBox) { TimedEvents tes; for (int radius = 1; radius <= 10; ++radius) { ui::GestureConfiguration::GetInstance()->set_default_radius(radius); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 5; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); const int kPositionX = 101; const int kPositionY = 201; delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(kPositionX, kPositionY), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_EQ(gfx::Rect(kPositionX - radius, kPositionY - radius, radius * 2, radius * 2), delegate->bounding_box()); const int kScrollAmount = 50; tes.SendScrollEvents(event_processor(), kPositionX, kPositionY, 1, 1, kTouchId, 1, kScrollAmount, delegate.get()); EXPECT_EQ(gfx::Point(1, 1).ToString(), delegate->scroll_begin_position().ToString()); EXPECT_EQ( gfx::Rect(kPositionX + kScrollAmount - radius, kPositionY + kScrollAmount - radius, radius * 2, radius * 2), delegate->bounding_box()); // Release the touch. This should end the scroll. delegate->Reset(); ui::TouchEvent release( ui::ET_TOUCH_RELEASED, gfx::Point(kPositionX + kScrollAmount, kPositionY + kScrollAmount), kTouchId, press.time_stamp() + base::TimeDelta::FromMilliseconds(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_EQ( gfx::Rect(kPositionX + kScrollAmount - radius, kPositionY + kScrollAmount - radius, radius * 2, radius * 2), delegate->bounding_box()); } ui::GestureConfiguration::GetInstance()->set_default_radius(0); } // Check Scroll End Events report correct velocities // if the user was on a horizontal rail TEST_F(GestureRecognizerTest, GestureEventHorizontalRailFling) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Get rid of touch slop. ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(10, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->Reset(); // Move the touch-point horizontally enough that it is considered a // horizontal scroll. tes.SendScrollEvent(event_processor(), 30, 1, kTouchId, delegate.get()); EXPECT_FLOAT_EQ(0, delegate->scroll_y()); EXPECT_FLOAT_EQ(20, delegate->scroll_x()); // Get a high x velocity, while still staying on the rail const int kScrollAmount = 8; tes.SendScrollEvents(event_processor(), 1, 1, 100, 10, kTouchId, 1, kScrollAmount, delegate.get()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->fling()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_GT(delegate->velocity_x(), 0); EXPECT_EQ(0, delegate->velocity_y()); } // Check Scroll End Events report correct velocities // if the user was on a vertical rail TEST_F(GestureRecognizerTest, GestureEventVerticalRailFling) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Get rid of touch slop. ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(0, 10), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->Reset(); // Move the touch-point vertically enough that it is considered a // vertical scroll. tes.SendScrollEvent(event_processor(), 1, 30, kTouchId, delegate.get()); EXPECT_EQ(20, delegate->scroll_y()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_velocity_x()); // Get a high y velocity, while still staying on the rail const int kScrollAmount = 8; tes.SendScrollEvents(event_processor(), 1, 6, 10, 100, kTouchId, 1, kScrollAmount, delegate.get()); EXPECT_EQ(0, delegate->scroll_velocity_x()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 206), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->fling()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(0, delegate->velocity_x()); EXPECT_GT(delegate->velocity_y(), 0); } // Check Scroll End Events report non-zero velocities if the user is not on a // rail TEST_F(GestureRecognizerTest, GestureEventNonRailFling) { ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(0); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Move the touch-point such that a non-rail scroll begins, and we're outside // the snap channel for the unified GR. tes.SendScrollEvent(event_processor(), 50, 50, kTouchId, delegate.get()); EXPECT_EQ(50, delegate->scroll_y()); EXPECT_EQ(50, delegate->scroll_x()); const int kScrollAmount = 8; tes.SendScrollEvents(event_processor(), 1, 1, 10, 100, kTouchId, 1, kScrollAmount, delegate.get()); delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->fling()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_GT(delegate->velocity_x(), 0); EXPECT_GT(delegate->velocity_y(), 0); } // Check that appropriate touch events generate long press events TEST_F(GestureRecognizerTest, GestureEventLongPress) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, ui::EventTimeForNow()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->tap_cancel()); // We haven't pressed long enough for a long press to occur EXPECT_FALSE(delegate->long_press()); // Wait until the timer runs out delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS); EXPECT_TRUE(delegate->long_press()); EXPECT_FALSE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, ui::EventTimeForNow()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->long_press()); // Note the tap cancel isn't dispatched until the release EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->tap()); } // Check that scrolling prevents a long press. TEST_F(GestureRecognizerTest, GestureEventLongPressCancelledByScroll) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 6; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); // We haven't pressed long enough for a long press to occur EXPECT_FALSE(delegate->long_press()); EXPECT_FALSE(delegate->tap_cancel()); // Scroll around, to cancel the long press tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); // Wait until a long press event would have fired, if it hadn't been // cancelled. DelayByLongPressTimeout(); EXPECT_FALSE(delegate->long_press()); EXPECT_TRUE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(10)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->long_press()); EXPECT_FALSE(delegate->tap_cancel()); } // Check that appropriate touch events generate long tap events TEST_F(GestureRecognizerTest, GestureEventLongTap) { ui::GestureConfiguration::GetInstance() ->set_max_touch_down_duration_for_click_in_ms(3); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, ui::EventTimeForNow()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->tap_cancel()); // We haven't pressed long enough for a long press to occur EXPECT_FALSE(delegate->long_press()); // Wait until the timer runs out delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS); EXPECT_TRUE(delegate->long_press()); EXPECT_FALSE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, ui::EventTimeForNow()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->long_press()); EXPECT_TRUE(delegate->long_tap()); // Note the tap cancel isn't dispatched until the release EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->tap()); } // Check that second tap cancels a long press TEST_F(GestureRecognizerTest, GestureEventLongPressCancelledBySecondTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 8; const int kTouchId2 = 2; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_TRUE(delegate->tap_down()); EXPECT_TRUE(delegate->begin()); // We haven't pressed long enough for a long press to occur EXPECT_FALSE(delegate->long_press()); // Second tap, to cancel the long press delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->tap_down()); // no touch down for second tap. EXPECT_TRUE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); // Wait until the timer runs out DelayByLongPressTimeout(); // No long press occurred EXPECT_FALSE(delegate->long_press()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->long_press()); EXPECT_TRUE(delegate->two_finger_tap()); EXPECT_FALSE(delegate->tap_cancel()); } // Check that horizontal scroll gestures cause scrolls on horizontal rails. // Also tests that horizontal rails can be broken. TEST_F(GestureRecognizerTest, GestureEventHorizontalRailScroll) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Get rid of touch slop. ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(5, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->Reset(); // Move the touch-point horizontally enough that it is considered a // horizontal scroll. tes.SendScrollEvent(event_processor(), 25, 0, kTouchId, delegate.get()); EXPECT_EQ(0, delegate->scroll_y()); EXPECT_EQ(20, delegate->scroll_x()); tes.SendScrollEvent(event_processor(), 30, 6, kTouchId, delegate.get()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(5, delegate->scroll_x()); // y shouldn't change, as we're on a horizontal rail. EXPECT_EQ(0, delegate->scroll_y()); // Send enough information that a velocity can be calculated for the gesture, // and we can break the rail const int kScrollAmount = 8; tes.SendScrollEvents(event_processor(), 1, 1, 6, 100, kTouchId, 1, kScrollAmount, delegate.get()); tes.SendScrollEvent(event_processor(), 5, 0, kTouchId, delegate.get()); tes.SendScrollEvent(event_processor(), 10, 5, kTouchId, delegate.get()); // The rail should be broken EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(5, delegate->scroll_x()); EXPECT_EQ(5, delegate->scroll_y()); } // Check that vertical scroll gestures cause scrolls on vertical rails. // Also tests that vertical rails can be broken. TEST_F(GestureRecognizerTest, GestureEventVerticalRailScroll) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Get rid of touch slop. ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(0, 5), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->Reset(); // Move the touch-point vertically enough that it is considered a // vertical scroll. tes.SendScrollEvent(event_processor(), 0, 25, kTouchId, delegate.get()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(20, delegate->scroll_y()); tes.SendScrollEvent(event_processor(), 6, 30, kTouchId, delegate.get()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(5, delegate->scroll_y()); // x shouldn't change, as we're on a vertical rail. EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_velocity_x()); // Send enough information that a velocity can be calculated for the gesture, // and we can break the rail const int kScrollAmount = 8; tes.SendScrollEvents(event_processor(), 1, 6, 100, 1, kTouchId, 1, kScrollAmount, delegate.get()); tes.SendScrollEvent(event_processor(), 0, 5, kTouchId, delegate.get()); tes.SendScrollEvent(event_processor(), 5, 10, kTouchId, delegate.get()); // The rail should be broken EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(5, delegate->scroll_x()); EXPECT_EQ(5, delegate->scroll_y()); } TEST_F(GestureRecognizerTest, GestureTapFollowedByScroll) { // We'll start by moving the touch point by (5, 5). We want all of that // distance to be consumed by the slop, so we set the slop radius to // sqrt(5 * 5 + 5 * 5). ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(sqrt(5.f * 5 + 5 * 5)); // First, tap. Then, do a scroll using the same touch-id. scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 3; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Make sure there is enough delay before the touch is released so that it is // recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Now, do a scroll gesture. Delay it sufficiently so that it doesn't trigger // a double-tap. delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.LeapForward(1000)); DispatchEventUsingWindowDispatcher(&press1); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Get rid of touch slop. ui::TouchEvent move_remove_slop(ui::ET_TOUCH_MOVED, gfx::Point(116, 216), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move_remove_slop); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(15, delegate->scroll_x_hint()); EXPECT_EQ(15, delegate->scroll_y_hint()); delegate->Reset(); // Move the touch-point enough so that it is considered as a scroll. This // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures. // The first movement is diagonal, to ensure that we have a free scroll, // and not a rail scroll. delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(135, 235), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(19, delegate->scroll_x()); EXPECT_EQ(19, delegate->scroll_y()); // Move some more to generate a few more scroll updates. delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(115, 216), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(-20, delegate->scroll_x()); EXPECT_EQ(-19, delegate->scroll_y()); EXPECT_EQ(0, delegate->scroll_x_hint()); EXPECT_EQ(0, delegate->scroll_y_hint()); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(145, 220), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(30, delegate->scroll_x()); EXPECT_EQ(4, delegate->scroll_y()); // Release the touch. This should end the scroll. delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_TRUE(delegate->fling()); } TEST_F(GestureRecognizerTest, AsynchronousGestureRecognition) { scoped_ptr<QueueTouchEventDelegate> queued_delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 6; const int kTouchId2 = 4; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> queue(CreateTestWindowWithDelegate( queued_delegate.get(), -1234, bounds, root_window())); queued_delegate->set_window(queue.get()); // Touch down on the window. This should not generate any gesture event. queued_delegate->Reset(); ui::TouchEvent press( ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Introduce some delay before the touch is released so that it is recognized // as a tap. However, this still should not create any gesture events. queued_delegate->Reset(); ui::TouchEvent release( ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, press.time_stamp() + base::TimeDelta::FromMilliseconds(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Create another window, and place a touch-down on it. This should create a // tap-down gesture. scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -2345, gfx::Rect(0, 0, 50, 50), root_window())); delegate->Reset(); ui::TouchEvent press2( ui::ET_TOUCH_PRESSED, gfx::Point(10, 20), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); ui::TouchEvent release2( ui::ET_TOUCH_RELEASED, gfx::Point(10, 20), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&release2); // Process the first queued event. queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_FALSE(queued_delegate->tap()); EXPECT_TRUE(queued_delegate->tap_down()); EXPECT_TRUE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Now, process the second queued event. queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_TRUE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_TRUE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Start all over. Press on the first window, then press again on the second // window. The second press should still go to the first window. queued_delegate->Reset(); ui::TouchEvent press3( ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press3); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); queued_delegate->Reset(); delegate->Reset(); ui::TouchEvent press4( ui::ET_TOUCH_PRESSED, gfx::Point(103, 203), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press4); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Move the second touch-point enough so that it is considered a pinch. This // should generate both SCROLL_BEGIN and PINCH_BEGIN gestures. queued_delegate->Reset(); delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(203 + ui::GestureConfiguration::GetInstance() ->max_touch_move_in_pixels_for_click(), 303), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_FALSE(queued_delegate->tap()); EXPECT_TRUE(queued_delegate->tap_down()); EXPECT_TRUE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); // no touch down for second tap. EXPECT_TRUE(queued_delegate->tap_cancel()); EXPECT_TRUE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); EXPECT_FALSE(queued_delegate->pinch_begin()); EXPECT_FALSE(queued_delegate->pinch_update()); EXPECT_FALSE(queued_delegate->pinch_end()); queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->end()); EXPECT_TRUE(queued_delegate->scroll_begin()); EXPECT_TRUE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); EXPECT_TRUE(queued_delegate->pinch_begin()); EXPECT_FALSE(queued_delegate->pinch_update()); EXPECT_FALSE(queued_delegate->pinch_end()); } // Check that appropriate touch events generate pinch gesture events. TEST_F(GestureRecognizerTest, GestureEventPinchFromScroll) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 5; const int kTouchId2 = 3; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); // Move the touch-point enough so that it is considered as a scroll. This // should generate both SCROLL_BEGIN and SCROLL_UPDATE gestures. delegate->Reset(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(130, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); // Press the second finger. It should cause pinch-begin. Note that we will not // transition to two finger tap here because the touch points are far enough. delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_BEGIN); EXPECT_EQ(gfx::Rect(10, 10, 120, 291).ToString(), delegate->bounding_box().ToString()); // Move the first finger. delegate->Reset(); ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(95, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); EXPECT_EQ(gfx::Rect(10, 10, 85, 191).ToString(), delegate->bounding_box().ToString()); // Now move the second finger. delegate->Reset(); ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(55, 15), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move4); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_UPDATE); EXPECT_EQ(gfx::Rect(55, 15, 40, 186).ToString(), delegate->bounding_box().ToString()); // Release the first finger. This should end pinch. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&release); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_PINCH_END, ui::ET_GESTURE_END); EXPECT_EQ(gfx::Rect(55, 15, 46, 186).ToString(), delegate->bounding_box().ToString()); // Move the second finger. This should still generate a scroll. delegate->Reset(); ui::TouchEvent move5(ui::ET_TOUCH_MOVED, gfx::Point(25, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move5); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); } TEST_F(GestureRecognizerTest, GestureEventPinchFromScrollFromPinch) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 5; const int kTouchId2 = 3; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->pinch_begin()); // Touch move triggers pinch begin. tes.SendScrollEvent(event_processor(), 130, 230, kTouchId1, delegate.get()); EXPECT_TRUE(delegate->pinch_begin()); EXPECT_FALSE(delegate->pinch_update()); // Touch move triggers pinch update. tes.SendScrollEvent(event_processor(), 160, 200, kTouchId1, delegate.get()); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_TRUE(delegate->pinch_update()); // Pinch has started, now release the second finger delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&release); EXPECT_TRUE(delegate->pinch_end()); tes.SendScrollEvent(event_processor(), 130, 230, kTouchId2, delegate.get()); EXPECT_TRUE(delegate->scroll_update()); // Pinch again delegate->Reset(); ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press3); // Now the touch points are close. So we will go into two finger tap. // Move the touch-point enough to break two-finger-tap and enter pinch. ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(101, 50), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_TRUE(delegate->pinch_begin()); tes.SendScrollEvent(event_processor(), 350, 350, kTouchId1, delegate.get()); EXPECT_TRUE(delegate->pinch_update()); } TEST_F(GestureRecognizerTest, GestureEventPinchFromTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 3; const int kTouchId2 = 5; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); // Press the second finger far enough to break two finger tap. delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_BEGIN); EXPECT_EQ(gfx::Rect(10, 10, 91, 291).ToString(), delegate->bounding_box().ToString()); // Move the first finger. delegate->Reset(); ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(65, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); EXPECT_EQ(gfx::Rect(10, 10, 55, 191).ToString(), delegate->bounding_box().ToString()); // Now move the second finger. delegate->Reset(); ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(55, 15), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move4); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_UPDATE); EXPECT_EQ(gfx::Rect(55, 15, 10, 186).ToString(), delegate->bounding_box().ToString()); // Release the first finger. This should end pinch. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.LeapForward(10)); DispatchEventUsingWindowDispatcher(&release); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_PINCH_END, ui::ET_GESTURE_END); EXPECT_EQ(gfx::Rect(55, 15, 46, 186).ToString(), delegate->bounding_box().ToString()); // Move the second finger. This should still generate a scroll. delegate->Reset(); ui::TouchEvent move5(ui::ET_TOUCH_MOVED, gfx::Point(25, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move5); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); } TEST_F(GestureRecognizerTest, GestureEventIgnoresDisconnectedEvents) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), 6, tes.Now()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); } // Check that a touch is locked to the window of the closest current touch // within max_separation_for_gesture_touches_in_pixels TEST_F(GestureRecognizerTest, GestureEventTouchLockSelectsCorrectWindow) { ui::GestureRecognizer* gesture_recognizer = new ui::GestureRecognizerImpl(); TimedEvents tes; ScopedGestureRecognizerSetter gr_setter(gesture_recognizer); ui::GestureConsumer* target; const int kNumWindows = 4; scoped_ptr<GestureEventConsumeDelegate*[]> delegates( new GestureEventConsumeDelegate*[kNumWindows]); ui::GestureConfiguration::GetInstance() ->set_max_separation_for_gesture_touches_in_pixels(499); scoped_ptr<gfx::Rect[]> window_bounds(new gfx::Rect[kNumWindows]); window_bounds[0] = gfx::Rect(0, 0, 1, 1); window_bounds[1] = gfx::Rect(500, 0, 1, 1); window_bounds[2] = gfx::Rect(0, 500, 1, 1); window_bounds[3] = gfx::Rect(500, 500, 1, 1); scoped_ptr<aura::Window*[]> windows(new aura::Window*[kNumWindows]); // Instantiate windows with |window_bounds| and touch each window at // its origin. for (int i = 0; i < kNumWindows; ++i) { delegates[i] = new GestureEventConsumeDelegate(); windows[i] = CreateTestWindowWithDelegate( delegates[i], i, window_bounds[i], root_window()); windows[i]->set_id(i); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, window_bounds[i].origin(), i, tes.Now()); DispatchEventUsingWindowDispatcher(&press); } // Touches should now be associated with the closest touch within // ui::GestureConfiguration::max_separation_for_gesture_touches_in_pixels target = gesture_recognizer->GetTargetForLocation(gfx::PointF(11.f, 11.f), -1); EXPECT_EQ("0", WindowIDAsString(target)); target = gesture_recognizer->GetTargetForLocation(gfx::PointF(511.f, 11.f), -1); EXPECT_EQ("1", WindowIDAsString(target)); target = gesture_recognizer->GetTargetForLocation(gfx::PointF(11.f, 511.f), -1); EXPECT_EQ("2", WindowIDAsString(target)); target = gesture_recognizer->GetTargetForLocation(gfx::PointF(511.f, 511.f), -1); EXPECT_EQ("3", WindowIDAsString(target)); // Add a touch in the middle associated with windows[2] ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 500), kNumWindows, tes.Now()); DispatchEventUsingWindowDispatcher(&press); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(250, 250), kNumWindows, tes.Now()); DispatchEventUsingWindowDispatcher(&move); target = gesture_recognizer->GetTargetForLocation(gfx::PointF(250.f, 250.f), -1); EXPECT_EQ("2", WindowIDAsString(target)); // Make sure that ties are broken by distance to a current touch // Closer to the point in the bottom right. target = gesture_recognizer->GetTargetForLocation(gfx::PointF(380.f, 380.f), -1); EXPECT_EQ("3", WindowIDAsString(target)); // This touch is closer to the point in the middle target = gesture_recognizer->GetTargetForLocation(gfx::PointF(300.f, 300.f), -1); EXPECT_EQ("2", WindowIDAsString(target)); // A touch too far from other touches won't be locked to anything target = gesture_recognizer->GetTargetForLocation(gfx::PointF(1000.f, 1000.f), -1); EXPECT_TRUE(target == NULL); // Move a touch associated with windows[2] to 1000, 1000 ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(1000, 1000), kNumWindows, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); target = gesture_recognizer->GetTargetForLocation(gfx::PointF(1000.f, 1000.f), -1); EXPECT_EQ("2", WindowIDAsString(target)); for (int i = 0; i < kNumWindows; ++i) { // Delete windows before deleting delegates. delete windows[i]; delete delegates[i]; } } // Check that a touch's target will not be effected by a touch on a different // screen. TEST_F(GestureRecognizerTest, GestureEventTouchLockIgnoresOtherScreens) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); gfx::Rect bounds(0, 0, 10, 10); scoped_ptr<aura::Window> window( CreateTestWindowWithDelegate(delegate.get(), 0, bounds, root_window())); const int kTouchId1 = 8; const int kTouchId2 = 2; TimedEvents tes; ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(5, 5), kTouchId1, tes.Now()); ui::EventTestApi test_press1(&press1); test_press1.set_source_device_id(1); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(20, 20), kTouchId2, tes.Now()); ui::EventTestApi test_press2(&press2); test_press2.set_source_device_id(2); DispatchEventUsingWindowDispatcher(&press2); // The second press should not have been locked to the same target as the // first, as they occured on different displays. EXPECT_NE( ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1), ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2)); } // Check that touch events outside the root window are still handled // by the root window's gesture sequence. TEST_F(GestureRecognizerTest, GestureEventOutsideRootWindowTap) { TimedEvents tes; scoped_ptr<aura::Window> window(CreateTestWindowWithBounds( gfx::Rect(-100, -100, 2000, 2000), root_window())); gfx::Point pos1(-10, -10); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, pos1, 0, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); gfx::Point pos2(1000, 1000); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, pos2, 1, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); // As these presses were outside the root window, they should be // associated with the root window. EXPECT_EQ(root_window(), static_cast<aura::Window*>( ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1))); EXPECT_EQ(root_window(), static_cast<aura::Window*>( ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2))); } TEST_F(GestureRecognizerTest, NoTapWithPreventDefaultedRelease) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kTouchId = 2; gfx::Rect bounds(100, 200, 100, 100); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); delegate->Reset(); delegate->ReceivedAck(); EXPECT_TRUE(delegate->tap_down()); delegate->Reset(); delegate->ReceivedAckPreventDefaulted(); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_cancel()); } TEST_F(GestureRecognizerTest, PinchScrollWithPreventDefaultedRelease) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kTouchId1 = 7; const int kTouchId2 = 5; gfx::Rect bounds(10, 20, 100, 100); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); { delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(15, 25), kTouchId1, tes.Now()); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(20, 95), kTouchId1, tes.LeapForward(200)); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(15, 25), kTouchId1, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&press); DispatchEventUsingWindowDispatcher(&move); DispatchEventUsingWindowDispatcher(&release); delegate->Reset(); // Ack the press event. delegate->ReceivedAck(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); delegate->Reset(); // Ack the move event. delegate->ReceivedAck(); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); delegate->Reset(); // Ack the release event. Although the release event has been processed, it // should still generate a scroll-end event. delegate->ReceivedAckPreventDefaulted(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_SCROLL_END, ui::ET_GESTURE_END); } ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(15, 25), kTouchId1, tes.Now()); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(20, 95), kTouchId1, tes.LeapForward(200)); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(15, 25), kTouchId1, tes.LeapForward(50)); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(55, 25), kTouchId2, tes.Now()); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(145, 85), kTouchId2, tes.LeapForward(1000)); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(145, 85), kTouchId2, tes.LeapForward(14)); // Do a pinch. DispatchEventUsingWindowDispatcher(&press); DispatchEventUsingWindowDispatcher(&move); DispatchEventUsingWindowDispatcher(&press2); DispatchEventUsingWindowDispatcher(&move2); DispatchEventUsingWindowDispatcher(&release); DispatchEventUsingWindowDispatcher(&release2); // Ack the press and move events. delegate->Reset(); delegate->ReceivedAck(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); delegate->Reset(); delegate->ReceivedAck(); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); delegate->Reset(); delegate->ReceivedAck(); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_BEGIN); delegate->Reset(); delegate->ReceivedAck(); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); // Ack the first release. Although the release is processed, it should still // generate a pinch-end event. delegate->Reset(); delegate->ReceivedAckPreventDefaulted(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_PINCH_END, ui::ET_GESTURE_END); delegate->Reset(); delegate->ReceivedAckPreventDefaulted(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_SCROLL_END, ui::ET_GESTURE_END); } TEST_F(GestureRecognizerTest, GestureEndLocation) { GestureEventConsumeDelegate delegate; scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( &delegate, -1234, gfx::Rect(10, 10, 300, 300), root_window())); ui::test::EventGenerator generator(root_window(), window.get()); const gfx::Point begin(20, 20); const gfx::Point end(150, 150); const gfx::Vector2d window_offset = window->bounds().origin().OffsetFromOrigin(); generator.GestureScrollSequence(begin, end, base::TimeDelta::FromMilliseconds(20), 10); EXPECT_EQ((begin - window_offset).ToString(), delegate.scroll_begin_position().ToString()); EXPECT_EQ((end - window_offset).ToString(), delegate.gesture_end_location().ToString()); } TEST_F(GestureRecognizerTest, CaptureSendsGestureEnd) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window())); ui::test::EventGenerator generator(root_window()); generator.MoveMouseRelativeTo(window.get(), gfx::Point(10, 10)); generator.PressTouch(); RunAllPendingInMessageLoop(); EXPECT_TRUE(delegate->tap_down()); scoped_ptr<aura::Window> capture(CreateTestWindowWithBounds( gfx::Rect(10, 10, 200, 200), root_window())); capture->SetCapture(); RunAllPendingInMessageLoop(); EXPECT_TRUE(delegate->end()); EXPECT_TRUE(delegate->tap_cancel()); } // Check that previous touch actions that are completely finished (either // released or cancelled), do not receive extra synthetic cancels upon change of // capture. TEST_F(GestureRecognizerTest, CaptureDoesNotCancelFinishedTouches) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); scoped_ptr<TestEventHandler> handler(new TestEventHandler); root_window()->AddPreTargetHandler(handler.get()); // Create a window and set it as the capture window. scoped_ptr<aura::Window> window1(CreateTestWindowWithDelegate(delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window())); window1->SetCapture(); ui::test::EventGenerator generator(root_window()); TimedEvents tes; // Generate two touch-press events on the window. scoped_ptr<ui::TouchEvent> touch0(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(20, 20), 0, tes.Now())); scoped_ptr<ui::TouchEvent> touch1(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(30, 30), 1, tes.Now())); generator.Dispatch(touch0.get()); generator.Dispatch(touch1.get()); RunAllPendingInMessageLoop(); EXPECT_EQ(2, handler->touch_pressed_count()); // Advance time. tes.LeapForward(1000); // End the two touches, one by a touch-release and one by a touch-cancel; to // cover both cases. touch0.reset(new ui::TouchEvent(ui::ET_TOUCH_RELEASED, gfx::Point(20, 20), 0, tes.Now())); touch1.reset(new ui::TouchEvent(ui::ET_TOUCH_CANCELLED, gfx::Point(30, 30), 1, tes.Now())); generator.Dispatch(touch0.get()); generator.Dispatch(touch1.get()); RunAllPendingInMessageLoop(); EXPECT_EQ(1, handler->touch_released_count()); EXPECT_EQ(1, handler->touch_cancelled_count()); // Create a new window and set it as the new capture window. scoped_ptr<aura::Window> window2(CreateTestWindowWithBounds( gfx::Rect(100, 100, 300, 300), root_window())); window2->SetCapture(); RunAllPendingInMessageLoop(); // Check that setting capture does not generate any synthetic touch-cancels // for the two previously finished touch actions. EXPECT_EQ(1, handler->touch_cancelled_count()); root_window()->RemovePreTargetHandler(handler.get()); } // Tests that a press with the same touch id as an existing touch is ignored. TEST_F(GestureRecognizerTest, PressDoesNotCrash) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, gfx::Rect(10, 10, 300, 300), root_window())); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(45, 45), 7, tes.Now()); SetTouchRadius(&press, 40, 0); DispatchEventUsingWindowDispatcher(&press); EXPECT_TRUE(delegate->tap_down()); EXPECT_EQ(gfx::Rect(5, 5, 80, 80).ToString(), delegate->bounding_box().ToString()); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(55, 45), 7, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); // FIXME(tdresser): this should not generate a tap down; however, // there is at least one case where we need to allow a touch press // from a currently used touch id. See crbug.com/373125 for details. EXPECT_TRUE(delegate->begin()); EXPECT_TRUE(delegate->tap_down()); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); } TEST_F(GestureRecognizerTest, TwoFingerTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_BEGIN); // Little bit of touch move should not affect our state. delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(102, 202), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(131, 202), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_SCROLL_UPDATE); // Make sure there is enough delay before the touch is released so that it is // recognized as a tap. delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_TWO_FINGER_TAP, ui::ET_GESTURE_END); // Lift second finger. // Make sure there is enough delay before the touch is released so that it is // recognized as a tap. delegate->Reset(); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_SCROLL_END, ui::ET_GESTURE_END); } TEST_F(GestureRecognizerTest, TwoFingerTapExpired) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); // Send release event after sufficient delay so that two finger time expires. delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.LeapForward(1000)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->two_finger_tap()); // Lift second finger. // Make sure there is enough delay before the touch is released so that it is // recognized as a tap. delegate->Reset(); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_FALSE(delegate->two_finger_tap()); } TEST_F(GestureRecognizerTest, TwoFingerTapChangesToPinch) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; TimedEvents tes; // Test moving first finger { gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); tes.SendScrollEvent(event_processor(), 230, 330, kTouchId1, delegate.get()); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_TRUE(delegate->pinch_begin()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_TRUE(delegate->pinch_end()); } // Test moving second finger { gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); tes.SendScrollEvent(event_processor(), 301, 230, kTouchId2, delegate.get()); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_TRUE(delegate->pinch_begin()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_TRUE(delegate->pinch_end()); } } TEST_F(GestureRecognizerTest, NoTwoFingerTapWhenFirstFingerHasScrolled) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; TimedEvents tes; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); tes.SendScrollEvent(event_processor(), 130, 230, kTouchId1, delegate.get()); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->pinch_begin()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_FALSE(delegate->pinch_end()); } TEST_F(GestureRecognizerTest, MultiFingerSwipe) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; gfx::Rect bounds(5, 10, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); const int kSteps = 15; const int kTouchPoints = 4; gfx::Point points[kTouchPoints] = { gfx::Point(10, 30), gfx::Point(30, 20), gfx::Point(50, 30), gfx::Point(80, 50) }; ui::test::EventGenerator generator(root_window(), window.get()); // The unified gesture recognizer assumes a finger has stopped if it hasn't // moved for too long. See ui/events/gesture_detection/velocity_tracker.cc's // kAssumePointerStoppedTimeMs. for (int count = 2; count <= kTouchPoints; ++count) { generator.GestureMultiFingerScroll( count, points, 10, kSteps, 0, -11 * kSteps); EXPECT_TRUE(delegate->swipe_up()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, 0, 11 * kSteps); EXPECT_TRUE(delegate->swipe_down()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, -11 * kSteps, 0); EXPECT_TRUE(delegate->swipe_left()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, 11 * kSteps, 0); EXPECT_TRUE(delegate->swipe_right()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, 5 * kSteps, 12 * kSteps); EXPECT_FALSE(delegate->swipe_down()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, 4 * kSteps, 12 * kSteps); EXPECT_TRUE(delegate->swipe_down()); delegate->Reset(); generator.GestureMultiFingerScroll( count, points, 10, kSteps, 3 * kSteps, 12 * kSteps); EXPECT_TRUE(delegate->swipe_down()); delegate->Reset(); } } TEST_F(GestureRecognizerTest, TwoFingerTapCancelled) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; TimedEvents tes; // Test canceling first finger. { gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); delegate->Reset(); ui::TouchEvent cancel(ui::ET_TOUCH_CANCELLED, gfx::Point(130, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&cancel); EXPECT_FALSE(delegate->two_finger_tap()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->two_finger_tap()); } // Test canceling second finger { gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); delegate->Reset(); ui::TouchEvent cancel(ui::ET_TOUCH_CANCELLED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&cancel); EXPECT_FALSE(delegate->two_finger_tap()); // Make sure there is enough delay before the touch is released so that it // is recognized as a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->two_finger_tap()); } } TEST_F(GestureRecognizerTest, VeryWideTwoFingerTouchDownShouldBeAPinch) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 523; const int kWindowHeight = 45; const int kTouchId1 = 2; const int kTouchId2 = 3; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); EXPECT_FALSE(delegate->two_finger_tap()); delegate->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(430, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); // no touch down for second tap. EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_FALSE(delegate->pinch_begin()); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(530, 301), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); // Pinch & Scroll only when there is enough movement. EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->long_press()); EXPECT_FALSE(delegate->two_finger_tap()); EXPECT_TRUE(delegate->pinch_begin()); } // Verifies if a window is the target of multiple touch-ids and we hide the // window everything is cleaned up correctly. TEST_F(GestureRecognizerTest, FlushAllOnHide) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); gfx::Rect bounds(0, 0, 200, 200); scoped_ptr<aura::Window> window( CreateTestWindowWithDelegate(delegate.get(), 0, bounds, root_window())); const int kTouchId1 = 8; const int kTouchId2 = 2; TimedEvents tes; ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(20, 20), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); window->Hide(); EXPECT_EQ(NULL, ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1)); EXPECT_EQ(NULL, ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2)); } TEST_F(GestureRecognizerTest, LongPressTimerStopsOnPreventDefaultedTouchMoves) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); const int kTouchId = 2; gfx::Rect bounds(100, 200, 100, 100); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); TimedEvents tes; delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); // Scroll around, to cancel the long press tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); delegate->Reset(); delegate->ReceivedAck(); EXPECT_TRUE(delegate->tap_down()); // Wait long enough that long press would have fired if the touchmove hadn't // prevented it. DelayByLongPressTimeout(); delegate->Reset(); delegate->ReceivedAckPreventDefaulted(); EXPECT_FALSE(delegate->long_press()); } // Same as GestureEventConsumeDelegate, but consumes all the touch-move events. class ConsumesTouchMovesDelegate : public GestureEventConsumeDelegate { public: ConsumesTouchMovesDelegate() : consume_touch_move_(true) {} ~ConsumesTouchMovesDelegate() override {} void set_consume_touch_move(bool consume) { consume_touch_move_ = consume; } private: void OnTouchEvent(ui::TouchEvent* touch) override { if (consume_touch_move_ && touch->type() == ui::ET_TOUCH_MOVED) touch->SetHandled(); else GestureEventConsumeDelegate::OnTouchEvent(touch); } bool consume_touch_move_; DISALLOW_COPY_AND_ASSIGN(ConsumesTouchMovesDelegate); }; // Same as GestureEventScroll, but tests that the behavior is the same // even if all the touch-move events are consumed. TEST_F(GestureRecognizerTest, GestureEventScrollTouchMoveConsumed) { scoped_ptr<ConsumesTouchMovesDelegate> delegate( new ConsumesTouchMovesDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 5; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Move the touch-point enough so that it would normally be considered a // scroll. But since the touch-moves will be consumed, the scroll should not // start. tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_TRUE(delegate->scroll_begin()); // Release the touch back at the start point. This should end without causing // a tap. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(130, 230), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_TRUE(delegate->scroll_end()); } // Tests the behavior of 2F scroll when some of the touch-move events are // consumed. TEST_F(GestureRecognizerTest, GestureEventScrollTwoFingerTouchMoveConsumed) { scoped_ptr<ConsumesTouchMovesDelegate> delegate( new ConsumesTouchMovesDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 100; const int kTouchId1 = 2; const int kTouchId2 = 3; TimedEvents tes; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); tes.SendScrollEvent(event_processor(), 131, 231, kTouchId1, delegate.get()); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN); delegate->Reset(); // Second finger touches down and moves. ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(130, 201), kTouchId2, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&press2); tes.SendScrollEvent(event_processor(), 161, 231, kTouchId2, delegate.get()); EXPECT_0_EVENTS(delegate->events()); delegate->Reset(); // Move first finger again, no PinchUpdate & ScrollUpdate. tes.SendScrollEvent(event_processor(), 161, 261, kTouchId1, delegate.get()); EXPECT_0_EVENTS(delegate->events()); // Stops consuming touch-move. delegate->set_consume_touch_move(false); delegate->Reset(); // Making a pinch gesture. tes.SendScrollEvent(event_processor(), 161, 260, kTouchId1, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); delegate->Reset(); tes.SendScrollEvent(event_processor(), 161, 261, kTouchId2, delegate.get()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId1, tes.Now()); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(130, 201), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&release1); DispatchEventUsingWindowDispatcher(&release2); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_END, ui::ET_SCROLL_FLING_START, ui::ET_GESTURE_END); } // Like as GestureEventTouchMoveConsumed but tests the different behavior // depending on whether the events were consumed before or after the scroll // started. TEST_F(GestureRecognizerTest, GestureEventScrollTouchMovePartialConsumed) { scoped_ptr<ConsumesTouchMovesDelegate> delegate( new ConsumesTouchMovesDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 5; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Move the touch-point enough so that it would normally be considered a // scroll. But since the touch-moves will be consumed, the scroll should not // start. tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); // Consuming the first touch move event won't prevent all future scrolling. EXPECT_TRUE(delegate->scroll_begin()); // Now, stop consuming touch-move events, and move the touch-point again. delegate->set_consume_touch_move(false); tes.SendScrollEvent(event_processor(), 159, 259, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_end()); // Scroll not prevented by consumed first touch move. EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(29, delegate->scroll_x()); EXPECT_EQ(29, delegate->scroll_y()); EXPECT_EQ(gfx::Point(0, 0).ToString(), delegate->scroll_begin_position().ToString()); // Start consuming touch-move events again. delegate->set_consume_touch_move(true); // Move some more to generate a few more scroll updates. tes.SendScrollEvent(event_processor(), 110, 211, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_y()); tes.SendScrollEvent(event_processor(), 140, 215, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_y()); // Release the touch. delegate->Reset(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->fling()); EXPECT_TRUE(delegate->scroll_end()); } // Check that appropriate touch events generate double tap gesture events. TEST_F(GestureRecognizerTest, GestureEventDoubleTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; // First tap (tested in GestureEventTap) ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(104, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(104, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); delegate->Reset(); // Second tap ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 203), kTouchId, tes.LeapForward(200)); DispatchEventUsingWindowDispatcher(&press2); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_TRUE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(2, delegate->tap_count()); } // Check that appropriate touch events generate triple tap gesture events. TEST_F(GestureRecognizerTest, GestureEventTripleTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; // First tap (tested in GestureEventTap) ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(104, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(104, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_EQ(1, delegate->tap_count()); delegate->Reset(); // Second tap (tested in GestureEventDoubleTap) ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 203), kTouchId, tes.LeapForward(200)); DispatchEventUsingWindowDispatcher(&press2); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_EQ(2, delegate->tap_count()); delegate->Reset(); // Third tap ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(102, 206), kTouchId, tes.LeapForward(200)); DispatchEventUsingWindowDispatcher(&press3); ui::TouchEvent release3(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release3); // Third, Fourth and Fifth Taps. Taps after the third should have their // |tap_count| wrap around back to 1. for (int i = 3; i < 5; ++i) { ui::TouchEvent press3(ui::ET_TOUCH_PRESSED, gfx::Point(102, 206), kTouchId, tes.LeapForward(200)); DispatchEventUsingWindowDispatcher(&press3); ui::TouchEvent release3(ui::ET_TOUCH_RELEASED, gfx::Point(102, 206), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release3); EXPECT_TRUE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1 + (i % 3), delegate->tap_count()); } } // Check that we don't get a double tap when the two taps are far apart. TEST_F(GestureRecognizerTest, TwoTapsFarApart) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; // First tap (tested in GestureEventTap) ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); delegate->Reset(); // Second tap, close in time but far in distance ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(201, 201), kTouchId, tes.LeapForward(200)); DispatchEventUsingWindowDispatcher(&press2); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(201, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_TRUE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); } // Check that we don't get a double tap when the two taps have a long enough // delay in between. TEST_F(GestureRecognizerTest, TwoTapsWithDelayBetween) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; // First tap (tested in GestureEventTap) ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); delegate->Reset(); // Second tap, close in distance but after some delay ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.LeapForward(2000)); DispatchEventUsingWindowDispatcher(&press2); ui::TouchEvent release2(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release2); EXPECT_TRUE(delegate->tap()); EXPECT_TRUE(delegate->tap_down()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->begin()); EXPECT_TRUE(delegate->end()); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_EQ(1, delegate->tap_count()); } // Checks that if the bounding-box of a gesture changes because of change in // radius of a touch-point, and not because of change in position, then there // are not gesture events from that. TEST_F(GestureRecognizerTest, BoundingBoxRadiusChange) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 234; const int kWindowHeight = 345; const int kTouchId = 5, kTouchId2 = 7; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; ui::TouchEvent press1( ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->bounding_box().IsEmpty()); delegate->Reset(); ui::TouchEvent press2( ui::ET_TOUCH_PRESSED, gfx::Point(201, 201), kTouchId2, tes.LeapForward(400)); SetTouchRadius(&press2, 5, 0); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_EQ(gfx::Rect(101, 196, 105, 10).ToString(), delegate->bounding_box().ToString()); delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(50, 50), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move1); EXPECT_TRUE(delegate->pinch_begin()); EXPECT_EQ(gfx::Rect(50, 50, 156, 156).ToString(), delegate->bounding_box().ToString()); delegate->Reset(); // The position doesn't move, but the radius changes. ui::TouchEvent move2( ui::ET_TOUCH_MOVED, gfx::Point(50, 50), kTouchId, tes.LeapForward(40)); SetTouchRadius(&move2, 50, 60); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->pinch_update()); delegate->Reset(); } // Checks that slow scrolls deliver the correct deltas. // In particular, fix for http;//crbug.com/150573. TEST_F(GestureRecognizerTest, NoDriftInScroll) { ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(3); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 234; const int kWindowHeight = 345; const int kTouchId = 5; TimedEvents tes; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press1( ui::ET_TOUCH_PRESSED, gfx::Point(101, 208), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->begin()); delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(101, 206), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move1); EXPECT_FALSE(delegate->scroll_begin()); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(101, 204), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move2); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); // 3 px consumed by touch slop region. EXPECT_EQ(-1, delegate->scroll_y()); EXPECT_EQ(-4, delegate->scroll_y_hint()); delegate->Reset(); ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(101, 204), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move3); EXPECT_FALSE(delegate->scroll_update()); delegate->Reset(); ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(101, 203), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move4); EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(-1, delegate->scroll_y()); delegate->Reset(); } // Ensure that move events which are preventDefaulted will cause a tap // cancel gesture event to be fired if the move would normally cause a // scroll. See bug http://crbug.com/146397. TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMoveCanFireTapCancel) { scoped_ptr<ConsumesTouchMovesDelegate> delegate( new ConsumesTouchMovesDelegate()); const int kTouchId = 5; gfx::Rect bounds(100, 200, 123, 45); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); TimedEvents tes; delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); delegate->set_consume_touch_move(false); DispatchEventUsingWindowDispatcher(&press); delegate->set_consume_touch_move(true); delegate->Reset(); // Move the touch-point enough so that it would normally be considered a // scroll. But since the touch-moves will be consumed, no scrolling should // occur. // With the unified gesture detector, we will receive a scroll begin gesture, // whereas with the aura gesture recognizer we won't. tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); EXPECT_FALSE(delegate->tap()); EXPECT_FALSE(delegate->tap_down()); EXPECT_TRUE(delegate->tap_cancel()); EXPECT_FALSE(delegate->begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); } TEST_F(GestureRecognizerTest, CancelAllActiveTouches) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 800; const int kWindowHeight = 600; const int kTouchId1 = 1; const int kTouchId2 = 2; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); scoped_ptr<TestEventHandler> handler(new TestEventHandler()); window->AddPreTargetHandler(handler.get()); // Start a gesture sequence on |window|. Then cancel all touches. // Make sure |window| receives a touch-cancel event. delegate->Reset(); ui::TouchEvent press( ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); delegate->Reset(); ui::TouchEvent p2( ui::ET_TOUCH_PRESSED, gfx::Point(50, 50), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&p2); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_BEGIN); delegate->Reset(); ui::TouchEvent move( ui::ET_TOUCH_MOVED, gfx::Point(350, 300), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); EXPECT_EQ(2, handler->touch_pressed_count()); delegate->Reset(); handler->Reset(); ui::GestureRecognizer* gesture_recognizer = ui::GestureRecognizer::Get(); EXPECT_EQ(window.get(), gesture_recognizer->GetTouchLockedTarget(press)); ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr); EXPECT_EQ(NULL, gesture_recognizer->GetTouchLockedTarget(press)); EXPECT_4_EVENTS(delegate->events(), ui::ET_GESTURE_PINCH_END, ui::ET_GESTURE_SCROLL_END, ui::ET_GESTURE_END, ui::ET_GESTURE_END); const std::vector<gfx::PointF>& points = handler->cancelled_touch_points(); EXPECT_EQ(2U, points.size()); EXPECT_EQ(gfx::PointF(101.f, 201.f), points[0]); EXPECT_EQ(gfx::PointF(350.f, 300.f), points[1]); } // Check that appropriate touch events generate show press events TEST_F(GestureRecognizerTest, GestureEventShowPress) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); EXPECT_TRUE(delegate->begin()); EXPECT_FALSE(delegate->tap_cancel()); // We haven't pressed long enough for a show press to occur EXPECT_FALSE(delegate->show_press()); // Wait until the timer runs out delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_SHOW_PRESS); EXPECT_TRUE(delegate->show_press()); EXPECT_FALSE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->long_press()); // Note the tap isn't dispatched until the release EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->tap()); } // Check that scrolling cancels a show press TEST_F(GestureRecognizerTest, GestureEventShowPressCancelledByScroll) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 6; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); // We haven't pressed long enough for a show press to occur EXPECT_FALSE(delegate->show_press()); EXPECT_FALSE(delegate->tap_cancel()); // Scroll around, to cancel the show press tes.SendScrollEvent(event_processor(), 130, 230, kTouchId, delegate.get()); // Wait until the timer runs out DelayByShowPressTimeout(); EXPECT_FALSE(delegate->show_press()); EXPECT_TRUE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(10)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_FALSE(delegate->show_press()); EXPECT_FALSE(delegate->tap_cancel()); } // Test that show press events are sent immediately on tap TEST_F(GestureRecognizerTest, GestureEventShowPressSentOnTap) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 6; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); // We haven't pressed long enough for a show press to occur EXPECT_FALSE(delegate->show_press()); EXPECT_FALSE(delegate->tap_cancel()); delegate->Reset(); ui::TouchEvent release1(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release1); EXPECT_TRUE(delegate->show_press()); EXPECT_FALSE(delegate->tap_cancel()); EXPECT_TRUE(delegate->tap()); } // Test that consuming the first move touch event prevents a scroll. TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMoveScrollTest) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); delegate->ReceivedAck(); // A touch move within the slop region is never consumed in web contents. The // unified GR won't prevent scroll if a touch move within the slop region is // consumed, so make sure this touch move exceeds the slop region. ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(10, 10), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); delegate->ReceivedAckPreventDefaulted(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(20, 20), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); delegate->ReceivedAck(); // With the unified gesture detector, consuming the first touch move event // won't prevent all future scrolling. EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); } // Test that consuming the first move touch doesn't prevent a tap. TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMoveTapTest) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kTouchId = 7; gfx::Rect bounds(0, 0, 1000, 1000); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); delegate->ReceivedAck(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(2, 2), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->ReceivedAckPreventDefaulted(); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(2, 2), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); delegate->ReceivedAck(); EXPECT_TRUE(delegate->tap()); } // Test that consuming the first move touch doesn't prevent a long press. TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMoveLongPressTest) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->ReceivedAck(); ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(103, 203), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move); delegate->ReceivedAckPreventDefaulted(); // Wait until the timer runs out delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS); EXPECT_TRUE(delegate->long_press()); } // Tests that the deltas are correct when leaving the slop region very slowly. TEST_F(GestureRecognizerTest, TestExceedingSlopSlowly) { ui::GestureConfiguration::GetInstance() ->set_max_touch_move_in_pixels_for_click(3); scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); const int kWindowWidth = 234; const int kWindowHeight = 345; const int kTouchId = 5; TimedEvents tes; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press( ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(11, 10), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move1); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_x_hint()); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(12, 10), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move2); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_EQ(0, delegate->scroll_x()); EXPECT_EQ(0, delegate->scroll_x_hint()); delegate->Reset(); ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(), kTouchId, tes.LeapForward(40)); move3.set_location_f(gfx::PointF(13.1f, 10.f)); move3.set_root_location_f(gfx::PointF(13.1f, 10.f)); DispatchEventUsingWindowDispatcher(&move3); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_NEAR(0.1, delegate->scroll_x(), 0.0001); EXPECT_FLOAT_EQ(3.1f, delegate->scroll_x_hint()); delegate->Reset(); ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(14, 10), kTouchId, tes.LeapForward(40)); DispatchEventUsingWindowDispatcher(&move4); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_NEAR(0.9, delegate->scroll_x(), 0.0001); EXPECT_EQ(0.f, delegate->scroll_x_hint()); delegate->Reset(); } TEST_F(GestureRecognizerTest, ScrollAlternatelyConsumedTest) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 3000; const int kWindowHeight = 3000; const int kTouchId = 2; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); delegate->Reset(); int x = 0; int y = 0; ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->ReceivedAck(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); delegate->Reset(); x += 100; y += 100; ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); delegate->ReceivedAck(); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); delegate->Reset(); for (int i = 0; i < 3; ++i) { x += 10; y += 10; ui::TouchEvent move2( ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); delegate->ReceivedAck(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_EQ(10, delegate->scroll_x()); EXPECT_EQ(10, delegate->scroll_y()); delegate->Reset(); x += 20; y += 20; ui::TouchEvent move3( ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); delegate->ReceivedAckPreventDefaulted(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); delegate->Reset(); } } TEST_F(GestureRecognizerTest, PinchAlternatelyConsumedTest) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 3000; const int kWindowHeight = 3000; const int kTouchId1 = 5; const int kTouchId2 = 7; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->ReceivedAck(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); delegate->Reset(); int x = 0; int y = 0; ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); delegate->ReceivedAck(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_FALSE(delegate->pinch_update()); delegate->Reset(); x += 100; y += 100; ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); delegate->ReceivedAck(); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_TRUE(delegate->pinch_begin()); EXPECT_FALSE(delegate->pinch_update()); delegate->Reset(); const float expected_scales[] = {1.5f, 1.2f, 1.125f}; for (int i = 0; i < 3; ++i) { x += 50; y += 50; ui::TouchEvent move2( ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); delegate->ReceivedAck(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_TRUE(delegate->pinch_update()); EXPECT_FALSE(delegate->pinch_end()); EXPECT_EQ(25, delegate->scroll_x()); EXPECT_EQ(25, delegate->scroll_y()); EXPECT_FLOAT_EQ(expected_scales[i], delegate->scale()); delegate->Reset(); x += 100; y += 100; ui::TouchEvent move3( ui::ET_TOUCH_MOVED, gfx::Point(x, y), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); delegate->ReceivedAckPreventDefaulted(); EXPECT_FALSE(delegate->scroll_begin()); EXPECT_FALSE(delegate->scroll_update()); EXPECT_FALSE(delegate->scroll_end()); EXPECT_FALSE(delegate->pinch_begin()); EXPECT_FALSE(delegate->pinch_update()); EXPECT_FALSE(delegate->pinch_end()); delegate->Reset(); } } // Test that touch event flags are passed through to the gesture event. TEST_F(GestureRecognizerTest, GestureEventFlagsPassedFromTouchEvent) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 6; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->Reset(); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(delegate->tap_down()); int default_flags = delegate->flags(); ui::TouchEvent move1( ui::ET_TOUCH_MOVED, gfx::Point(397, 149), kTouchId, tes.LeapForward(50)); move1.set_flags(992); DispatchEventUsingWindowDispatcher(&move1); EXPECT_NE(default_flags, delegate->flags()); } // A delegate that deletes a window on long press. class GestureEventDeleteWindowOnLongPress : public GestureEventConsumeDelegate { public: GestureEventDeleteWindowOnLongPress() : window_(NULL) {} void set_window(aura::Window** window) { window_ = window; } void OnGestureEvent(ui::GestureEvent* gesture) override { GestureEventConsumeDelegate::OnGestureEvent(gesture); if (gesture->type() != ui::ET_GESTURE_LONG_PRESS) return; ui::GestureRecognizer::Get()->CleanupStateForConsumer(*window_); delete *window_; *window_ = NULL; } private: aura::Window** window_; DISALLOW_COPY_AND_ASSIGN(GestureEventDeleteWindowOnLongPress); }; // Check that deleting the window in response to a long press gesture doesn't // crash. TEST_F(GestureRecognizerTest, GestureEventLongPressDeletingWindow) { GestureEventDeleteWindowOnLongPress delegate; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId = 2; gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight); aura::Window* window(CreateTestWindowWithDelegate( &delegate, -1234, bounds, root_window())); delegate.set_window(&window); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, ui::EventTimeForNow()); DispatchEventUsingWindowDispatcher(&press1); EXPECT_TRUE(window != NULL); // Wait until the timer runs out. delegate.WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS); EXPECT_EQ(NULL, window); } TEST_F(GestureRecognizerWithSwitchTest, GestureEventSmallPinchDisabled) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 3; const int kTouchId2 = 5; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); // Move the first finger. delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(65, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); // No pinch update occurs, as kCompensateForUnstablePinchZoom is on and // |min_pinch_update_span_delta| was nonzero, and this is a very small pinch. delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(65, 202), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); } TEST_F(GestureRecognizerTest, GestureEventSmallPinchEnabled) { scoped_ptr<GestureEventConsumeDelegate> delegate( new GestureEventConsumeDelegate()); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 3; const int kTouchId2 = 5; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); // Move the first finger. delegate->Reset(); ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(65, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_BEGIN); delegate->Reset(); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(65, 202), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_PINCH_UPDATE); } // Tests that delaying the ack of a touch release doesn't trigger a long press // gesture. TEST_F(GestureRecognizerTest, EagerGestureDetection) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kTouchId = 2; gfx::Rect bounds(100, 200, 100, 100); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), kTouchId, tes.Now()); DispatchEventUsingWindowDispatcher(&press); ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), kTouchId, tes.LeapForward(50)); DispatchEventUsingWindowDispatcher(&release); delegate->Reset(); // Ack the touch press. delegate->ReceivedAck(); EXPECT_TRUE(delegate->tap_down()); delegate->Reset(); // Wait until the long press event would fire (if we weren't eager). DelayByLongPressTimeout(); // Ack the touch release. delegate->ReceivedAck(); EXPECT_TRUE(delegate->tap()); EXPECT_FALSE(delegate->long_press()); } // This tests crbug.com/405519, in which touch events which the gesture detector // ignores interfere with gesture recognition. TEST_F(GestureRecognizerTest, IgnoredEventsDontBreakGestureRecognition) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 3; gfx::Rect bounds(5, 5, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); ui::TouchEvent press1( ui::ET_TOUCH_PRESSED, gfx::Point(101, 301), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->ReceivedAck(); EXPECT_2_EVENTS( delegate->events(), ui::ET_GESTURE_BEGIN, ui::ET_GESTURE_TAP_DOWN); // Move the first finger. delegate->Reset(); ui::TouchEvent move1( ui::ET_TOUCH_MOVED, gfx::Point(65, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); delegate->ReceivedAck(); EXPECT_3_EVENTS(delegate->events(), ui::ET_GESTURE_TAP_CANCEL, ui::ET_GESTURE_SCROLL_BEGIN, ui::ET_GESTURE_SCROLL_UPDATE); delegate->Reset(); // Send a valid event, but don't ack it. ui::TouchEvent move2( ui::ET_TOUCH_MOVED, gfx::Point(65, 202), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_0_EVENTS(delegate->events()); // Send a touchmove event at the same location as the previous touchmove // event. This shouldn't do anything. ui::TouchEvent move3( ui::ET_TOUCH_MOVED, gfx::Point(65, 202), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); // Ack the previous valid event. The intermediary invalid event shouldn't // interfere. delegate->ReceivedAck(); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); } // Tests that an event stream can have a mix of sync and async acks. TEST_F(GestureRecognizerTest, MixedSyncAndAsyncAcksDontCauseOutOfOrderDispatch) { scoped_ptr<QueueTouchEventDelegate> delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 300; const int kWindowHeight = 400; const int kTouchId1 = 3; gfx::Rect bounds(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( delegate.get(), -1234, bounds, root_window())); delegate->set_window(window.get()); // Start a scroll gesture. ui::TouchEvent press1( ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press1); delegate->ReceivedAck(); ui::TouchEvent move1( ui::ET_TOUCH_MOVED, gfx::Point(100, 100), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move1); delegate->ReceivedAck(); delegate->Reset(); // Dispatch a synchronously consumed touch move, which should be ignored. delegate->set_synchronous_ack_for_next_event(true); ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(200, 200), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move2); EXPECT_0_EVENTS(delegate->events()); // Dispatch a touch move, but don't ack it. ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(300, 300), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move3); // Dispatch two synchronously consumed touch moves, which should be ignored. delegate->set_synchronous_ack_for_next_event(true); ui::TouchEvent move4( ui::ET_TOUCH_MOVED, gfx::Point(400, 400), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move4); delegate->set_synchronous_ack_for_next_event(true); ui::TouchEvent move5( ui::ET_TOUCH_MOVED, gfx::Point(500, 500), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move5); EXPECT_0_EVENTS(delegate->events()); EXPECT_EQ(100, delegate->bounding_box().x()); // Ack the pending touch move, and ensure the most recent gesture event // used its co-ordinates. delegate->ReceivedAck(); EXPECT_EQ(300, delegate->bounding_box().x()); EXPECT_1_EVENT(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE); // Dispatch a touch move, but don't ack it. delegate->Reset(); ui::TouchEvent move6(ui::ET_TOUCH_MOVED, gfx::Point(600, 600), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move6); // Dispatch a synchronously unconsumed touch move. delegate->set_synchronous_ack_for_next_event(false); ui::TouchEvent move7( ui::ET_TOUCH_MOVED, gfx::Point(700, 700), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&move7); // The synchronous ack is stuck behind the pending touch move. EXPECT_0_EVENTS(delegate->events()); delegate->ReceivedAck(); EXPECT_2_EVENTS(delegate->events(), ui::ET_GESTURE_SCROLL_UPDATE, ui::ET_GESTURE_SCROLL_UPDATE); } TEST_F(GestureRecognizerTest, GestureEventTwoWindowsActive) { scoped_ptr<QueueTouchEventDelegate> queued_delegate( new QueueTouchEventDelegate(host()->dispatcher())); TimedEvents tes; const int kWindowWidth = 123; const int kWindowHeight = 45; const int kTouchId1 = 6; const int kTouchId2 = 4; gfx::Rect bounds(150, 200, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate( queued_delegate.get(), -1234, bounds, root_window())); queued_delegate->set_window(window.get()); // Touch down on the window. This should not generate any gesture event. queued_delegate->Reset(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(151, 201), kTouchId1, tes.Now()); DispatchEventUsingWindowDispatcher(&press); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_FALSE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); // Touch down on the second window. This should not generate any // gesture event. scoped_ptr<QueueTouchEventDelegate> queued_delegate2( new QueueTouchEventDelegate(host()->dispatcher())); gfx::Rect bounds2(0, 0, kWindowWidth, kWindowHeight); scoped_ptr<aura::Window> window2(CreateTestWindowWithDelegate( queued_delegate2.get(), -2345, bounds2, root_window())); queued_delegate2->set_window(window2.get()); queued_delegate2->Reset(); ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(1, 1), kTouchId2, tes.Now()); DispatchEventUsingWindowDispatcher(&press2); EXPECT_FALSE(queued_delegate2->tap()); EXPECT_FALSE(queued_delegate2->tap_down()); EXPECT_FALSE(queued_delegate2->tap_cancel()); EXPECT_FALSE(queued_delegate2->begin()); EXPECT_FALSE(queued_delegate2->scroll_begin()); EXPECT_FALSE(queued_delegate2->scroll_update()); EXPECT_FALSE(queued_delegate2->scroll_end()); // Ack the first window's touch; make sure it is processed by the first // window. queued_delegate->Reset(); queued_delegate->ReceivedAck(); EXPECT_FALSE(queued_delegate->tap()); EXPECT_FALSE(queued_delegate->show_press()); EXPECT_TRUE(queued_delegate->tap_down()); EXPECT_FALSE(queued_delegate->tap_cancel()); EXPECT_TRUE(queued_delegate->begin()); EXPECT_FALSE(queued_delegate->scroll_begin()); EXPECT_FALSE(queued_delegate->scroll_update()); EXPECT_FALSE(queued_delegate->scroll_end()); EXPECT_FALSE(queued_delegate->long_press()); // Ack the second window's touch; make sure it is processed by the second // window. queued_delegate2->Reset(); queued_delegate2->ReceivedAck(); EXPECT_FALSE(queued_delegate2->tap()); EXPECT_FALSE(queued_delegate2->show_press()); EXPECT_TRUE(queued_delegate2->tap_down()); EXPECT_FALSE(queued_delegate2->tap_cancel()); EXPECT_TRUE(queued_delegate2->begin()); EXPECT_FALSE(queued_delegate2->scroll_begin()); EXPECT_FALSE(queued_delegate2->scroll_update()); EXPECT_FALSE(queued_delegate2->scroll_end()); EXPECT_FALSE(queued_delegate2->long_press()); queued_delegate->Reset(); queued_delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_SHOW_PRESS); EXPECT_TRUE(queued_delegate->show_press()); EXPECT_FALSE(queued_delegate->tap_down()); } } // namespace test } // namespace aura