Commit e52a14ab authored by tdresser@chromium.org's avatar tdresser@chromium.org

Consuming any touch move before SCROLL_START prevents the scroll from occuring in Aura

This patch does NOT prevent tap or long press when a touch move is consumed (see
crbug.com/327444).

An additional state is added to GestureState - GS_SYNTHETIC_CLICK_ABORTED. In addition,
the meaning of GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL has changed.

GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL indicates that one finger is down and no scrolling
can happen, but a tap or long press can still happen.

GS_SYNTHETIC_CLICK_ABORTED indicates that a finger is down, but no gestures can be
created until the number of active touch points changes.

BUG=333947

Review URL: https://codereview.chromium.org/133273013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245635 0039d316-1c4b-4281-b951-d872f2087c98
parent 4e77fc4a
......@@ -3741,5 +3741,154 @@ TEST_F(GestureRecognizerTest, GestureEventShowPressSentOnTap) {
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(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()));
ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&press);
delegate->ReceivedAck();
ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(2, 2),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move1);
delegate->ReceivedAckPreventDefaulted();
ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(20, 20),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move2);
delegate->ReceivedAck();
EXPECT_FALSE(delegate->scroll_begin());
EXPECT_FALSE(delegate->scroll_update());
}
// Test that consuming the first touch move event of a touch point doesn't
// prevent pinching once an additional touch has been pressed.
TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMovePinchTest) {
scoped_ptr<QueueTouchEventDelegate> delegate(
new QueueTouchEventDelegate(dispatcher()));
TimedEvents tes;
const int kTouchId1 = 7;
const int kTouchId2 = 4;
gfx::Rect bounds(0, 0, 1000, 1000);
scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
delegate.get(), -1234, bounds, root_window()));
ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
kTouchId1, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&press1);
delegate->ReceivedAck();
ui::TouchEvent move1(ui::ET_TOUCH_MOVED, gfx::Point(2, 2),
kTouchId1, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move1);
delegate->ReceivedAckPreventDefaulted();
ui::TouchEvent move2(ui::ET_TOUCH_MOVED, gfx::Point(20, 20),
kTouchId1, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move2);
delegate->ReceivedAck();
// We can't scroll, because a move has been consumed.
EXPECT_FALSE(delegate->scroll_begin());
EXPECT_FALSE(delegate->scroll_update());
EXPECT_FALSE(delegate->pinch_begin());
// An additional press will allow us to pinch.
ui::TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
kTouchId2, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&press2);
delegate->ReceivedAck();
ui::TouchEvent move3(ui::ET_TOUCH_MOVED, gfx::Point(20, 20),
kTouchId2, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move3);
delegate->ReceivedAck();
EXPECT_TRUE(delegate->pinch_begin());
EXPECT_FALSE(delegate->pinch_update());
delegate->Reset();
ui::TouchEvent move4(ui::ET_TOUCH_MOVED, gfx::Point(40, 40),
kTouchId2, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move4);
delegate->ReceivedAck();
EXPECT_TRUE(delegate->pinch_update());
EXPECT_EQ(10, delegate->scroll_x());
EXPECT_EQ(10, delegate->scroll_y());
}
// Test that consuming the first move touch doesn't prevent a tap.
TEST_F(GestureRecognizerTest, GestureEventConsumedTouchMoveTapTest) {
scoped_ptr<QueueTouchEventDelegate> delegate(
new QueueTouchEventDelegate(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()));
ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&press);
delegate->ReceivedAck();
ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(2, 2),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move);
delegate->ReceivedAckPreventDefaulted();
ui::TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(2, 2),
kTouchId, tes.LeapForward(50));
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&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(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->Reset();
TimerTestGestureRecognizer* gesture_recognizer =
new TimerTestGestureRecognizer();
ScopedGestureRecognizerSetter gr_setter(gesture_recognizer);
ui::TouchEvent press1(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&press1);
delegate->ReceivedAck();
ui::TouchEvent move(ui::ET_TOUCH_MOVED, gfx::Point(103, 203),
kTouchId, tes.Now());
dispatcher()->AsWindowTreeHostDelegate()->OnHostTouchEvent(&move);
delegate->ReceivedAckPreventDefaulted();
// Wait until the timer runs out
delegate->WaitUntilReceivedGesture(ui::ET_GESTURE_LONG_PRESS);
EXPECT_TRUE(delegate->long_press());
}
} // namespace test
} // namespace aura
......@@ -129,6 +129,12 @@ enum EdgeStateSignatureType {
GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED =
G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED),
GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED =
G(GS_SYNTHETIC_CLICK_ABORTED, 0, TS_RELEASED, TSI_ALWAYS),
GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED =
G(GS_SYNTHETIC_CLICK_ABORTED, 1, TS_PRESSED, TSI_NOT_PROCESSED),
GST_SCROLL_FIRST_RELEASED =
G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
......@@ -342,6 +348,8 @@ EdgeStateSignatureType Signature(GestureState gesture_state,
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED:
case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_MOVED:
case GST_SCROLL_FIRST_CANCELLED:
......@@ -474,6 +482,16 @@ void UpdateGestureEventLatencyInfo(const TouchEvent& event,
}
}
bool GestureStateSupportsActiveTimer(GestureState state) {
switch(state) {
case GS_PENDING_SYNTHETIC_CLICK:
case GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL:
return true;
default:
return false;
}
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
......@@ -557,6 +575,7 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
set_state(GS_PENDING_SYNTHETIC_CLICK);
break;
case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
if (Click(event, point, gestures.get()))
point.UpdateForTap();
else
......@@ -572,24 +591,24 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
point.UpdateForScroll();
}
break;
case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_STATIONARY:
// No scrolling allowed, so nothing happens.
break;
case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED:
if (point.IsInScrollWindow(event)) {
PrependTapCancelGestureEvent(point, gestures.get());
set_state(GS_SYNTHETIC_CLICK_ABORTED);
} else {
set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
}
break;
case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED:
case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
PrependTapCancelGestureEvent(point, gestures.get());
set_state(GS_NO_GESTURE);
break;
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED:
case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED:
set_state(GS_NO_GESTURE);
break;
case GST_SCROLL_FIRST_MOVED:
......@@ -605,10 +624,11 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
set_state(GS_NO_GESTURE);
break;
case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
PrependTapCancelGestureEvent(point, gestures.get());
TwoFingerTapOrPinch(event, point, gestures.get());
break;
case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED:
case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
TwoFingerTapOrPinch(event, point, gestures.get());
break;
case GST_SCROLL_SECOND_PRESSED:
......@@ -751,7 +771,10 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
<< " State: " << state_
<< " touch id: " << event.touch_id();
if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state) {
// If the state has changed from one in which a long/show press is possible to
// one in which they are not possible, cancel the timers.
if (GestureStateSupportsActiveTimer(last_state) &&
!GestureStateSupportsActiveTimer(state_)) {
GetLongPressTimer()->Stop();
GetShowPressTimer()->Stop();
}
......@@ -1102,7 +1125,8 @@ void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) {
bool GestureSequence::Click(const TouchEvent& event,
const GesturePoint& point,
Gestures* gestures) {
DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK);
DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL);
if (point.IsInClickWindow(event)) {
int tap_count = 1;
if (point.IsInTripleClickWindow(event))
......@@ -1187,6 +1211,7 @@ bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event,
Gestures* gestures) {
DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK ||
state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL ||
state_ == GS_SYNTHETIC_CLICK_ABORTED ||
state_ == GS_SCROLL);
if (state_ == GS_SCROLL) {
......
......@@ -19,7 +19,12 @@ class GestureEvent;
enum GestureState {
GS_NO_GESTURE,
GS_PENDING_SYNTHETIC_CLICK,
// One finger is down: tap could occur, but scroll cannot until the number of
// active touch points changes.
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL,
// One finger is down: no gestures can occur until the number of active touch
// points changes.
GS_SYNTHETIC_CLICK_ABORTED,
GS_SCROLL,
GS_PINCH,
GS_PENDING_TWO_FINGER_TAP,
......
......@@ -25,11 +25,16 @@ GS_PENDING_SYNTHETIC_CLICK -> GS_NO_GESTURE [label= "C0\n R0"];
GS_PENDING_SYNTHETIC_CLICK -> GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL [label= "M0"];
GS_PENDING_SYNTHETIC_CLICK -> GS_PENDING_TWO_FINGER_TAP [label= "D1"];
GS_PENDING_SYNTHETIC_CLICK -> GS_PENDING_PINCH [label= "D1"];
GS_PENDING_SYNTHETIC_CLICK -> GS_SYNTHETIC_CLICK_ABORTED [label= "M0"];
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL -> GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL [label= "M0\n S0"];
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL -> GS_NO_GESTURE [label= "C0\n R0"];
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL -> GS_PENDING_TWO_FINGER_TAP [label= "D1"];
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL -> GS_PENDING_PINCH [label= "D1"];
GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL -> GS_SYNTHETIC_CLICK_ABORTED [label= "M0"];
GS_SYNTHETIC_CLICK_ABORTED -> GS_NO_GESTURE [label= "C0\n R0"];
GS_SYNTHETIC_CLICK_ABORTED -> GS_PENDING_PINCH [label= "D1"];
GS_SCROLL -> GS_SCROLL [label= "M0"];
GS_SCROLL -> GS_NO_GESTURE [label= "C0\n R0\n"];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment