Commit 657e2cdb authored by tdresser@chromium.org's avatar tdresser@chromium.org

Consuming a touch move prevents only the next scroll update.

This implements (In Clank) the touch disposition handling method outlined here:
https://docs.google.com/a/chromium.org/document/d/176xYUC3WbiSl7qd08SBW4NiI4_gO0N4PN6tPAq49-68/edit?pli=1#bookmark=id.wu82d53abe2w

We also modify the way in which Aura handles touch move events to line up with the new way Android handles them. That is, consuming a touch move event prevents the next scroll update event, but does not prevent future scroll update events.

This change will have little impact until https://codereview.chromium.org/166923002/ has landed.

BUG=328503

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@251533 0039d316-1c4b-4281-b951-d872f2087c98
parent de71a379
......@@ -14,34 +14,42 @@ using blink::WebTouchPoint;
namespace content {
namespace {
bool IsTouchSequenceStart(const WebTouchEvent& event) {
if (event.type != WebInputEvent::TouchStart)
return false;
GestureEventPacket::GestureSource ToGestureSource(const WebTouchEvent& event) {
if (!event.touchesLength)
return false;
for (size_t i = 0; i < event.touchesLength; i++) {
if (event.touches[i].state != WebTouchPoint::StatePressed)
return false;
return GestureEventPacket::INVALID;
switch (event.type) {
case WebInputEvent::TouchStart:
for (size_t i = 0; i < event.touchesLength; i++) {
if (event.touches[i].state != WebTouchPoint::StatePressed)
return GestureEventPacket::TOUCH_BEGIN;
}
return GestureEventPacket::TOUCH_SEQUENCE_BEGIN;
case WebInputEvent::TouchMove:
return GestureEventPacket::TOUCH_MOVE;
case WebInputEvent::TouchEnd:
case WebInputEvent::TouchCancel:
for (size_t i = 0; i < event.touchesLength; i++) {
if (event.touches[i].state != WebTouchPoint::StateReleased &&
event.touches[i].state != WebTouchPoint::StateCancelled) {
return GestureEventPacket::TOUCH_END;
}
}
return GestureEventPacket::TOUCH_SEQUENCE_END;
default:
return GestureEventPacket::INVALID;
}
return true;
}
GestureEventPacket::GestureSource
ToGestureSource(const WebTouchEvent& event) {
return IsTouchSequenceStart(event) ? GestureEventPacket::TOUCH_BEGIN
: GestureEventPacket::TOUCH;
}
} // namespace
GestureEventPacket::GestureEventPacket()
: gesture_count_(0),
gesture_source_(INVALID) {}
gesture_source_(UNDEFINED) {}
GestureEventPacket::GestureEventPacket(GestureSource source)
: gesture_count_(0),
gesture_source_(source) {
DCHECK_NE(gesture_source_, INVALID);
DCHECK_NE(gesture_source_, UNDEFINED);
}
GestureEventPacket::~GestureEventPacket() {}
......
......@@ -15,10 +15,14 @@ namespace content {
class CONTENT_EXPORT GestureEventPacket {
public:
enum GestureSource {
INVALID = -1, // Used only for a default-constructed packet..
TOUCH_BEGIN, // The start of a new gesture sequence.
TOUCH, // Continuation of an existing gesture sequence.
TOUCH_TIMEOUT, // Timeout from an existing gesture sequence.
UNDEFINED = -1, // Used only for a default-constructed packet.
INVALID, // The source of the gesture was invalid.
TOUCH_SEQUENCE_BEGIN, // The start of a new gesture sequence.
TOUCH_SEQUENCE_END, // The end of gesture sequence.
TOUCH_BEGIN, // A touch down occured during a gesture sequence.
TOUCH_MOVE, // A touch move occured during a gesture sequence.
TOUCH_END, // A touch up occured during a gesture sequence.
TOUCH_TIMEOUT, // Timeout from an existing gesture sequence.
};
GestureEventPacket();
......
......@@ -5,8 +5,8 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_
#include <deque>
#include <queue>
#include <set>
#include "content/browser/renderer_host/input/gesture_event_packet.h"
#include "content/common/content_export.h"
......@@ -56,30 +56,45 @@ class CONTENT_EXPORT TouchDispositionGestureFilter {
// generated by timeouts from a statinoary finger.
class GestureSequence {
public:
struct GestureHandlingState {
GestureHandlingState();
// True iff the sequence has had at least one touch acked.
bool seen_ack;
// True iff the sequence has had any touch down event consumed.
bool start_consumed;
// True iff the first ack received for this sequence reported that no
// consumer exists.
bool no_consumer;
};
GestureSequence();
~GestureSequence();
void Push(const GestureEventPacket& packet);
void Pop();
const GestureEventPacket& Front() const;
void UpdateState(InputEventAckState ack_state);
bool IsGesturePrevented() const;
void UpdateState(GestureEventPacket::GestureSource gesture_source,
InputEventAckState ack_state);
bool IsEmpty() const;
const GestureHandlingState& state() const { return state_; };
private:
std::queue<GestureEventPacket> packets_;
enum GestureHandlingState {
PENDING, // The sequence has yet to receive an ack.
ALLOWED_UNTIL_PREVENTED, // Gestures in the sequence are allowed until
// a source touch is preventDefault'ed.
ALWAYS_ALLOWED, // All remaining sequence gestures are forwarded.
ALWAYS_PREVENTED // All remaining sequence gestures are dropped.
};
GestureHandlingState state_;
};
bool IsGesturePrevented(blink::WebInputEvent::Type type,
InputEventAckState current,
const GestureSequence::GestureHandlingState& state)
const;
void UpdateAndDispatchPackets(GestureSequence* sequence,
InputEventAckState ack_result);
void SendPacket(const GestureEventPacket& packet);
void FilterAndSendPacket(
const GestureEventPacket& packet,
const GestureSequence::GestureHandlingState& sequence_state,
InputEventAckState ack_state);
void SendGesture(const blink::WebGestureEvent& gesture);
void CancelTapIfNecessary();
void CancelFlingIfNecessary();
......@@ -89,6 +104,11 @@ class CONTENT_EXPORT TouchDispositionGestureFilter {
TouchDispositionGestureFilterClient* client_;
std::queue<GestureSequence> sequences_;
// If the previous gesture of a given type was dropped instead of being
// dispatched, its type will occur in this set. Cleared when a new touch
// sequence begins to be acked.
std::set<blink::WebInputEvent::Type> last_event_of_type_dropped_;
// Bookkeeping for inserting synthetic Gesture{Tap,Fling}Cancel events
// when necessary, e.g., GestureTapCancel when scrolling begins, or
// GestureFlingCancel when a user taps following a GestureFlingStart.
......
......@@ -4038,5 +4038,62 @@ TEST_F(GestureRecognizerTest, TestExceedingSlopSlowly) {
}
TEST_F(GestureRecognizerTest, ScrollAlternatelyConsumedTest) {
scoped_ptr<QueueTouchEventDelegate> delegate(
new QueueTouchEventDelegate(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->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());
delegate->Reset();
x -= 10;
y += 10;
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();
}
}
} // namespace test
} // namespace aura
......@@ -138,9 +138,11 @@ enum EdgeStateSignatureType {
GST_SCROLL_FIRST_RELEASED =
G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
// Once scroll has started, process all touch-move events.
GST_SCROLL_FIRST_MOVED =
G(GS_SCROLL, 0, TS_MOVED, TSI_ALWAYS),
G(GS_SCROLL, 0, TS_MOVED, TSI_NOT_PROCESSED),
GST_SCROLL_FIRST_MOVED_HANDLED =
G(GS_SCROLL, 0, TS_MOVED, TSI_PROCESSED),
GST_SCROLL_FIRST_CANCELLED =
G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
......@@ -352,6 +354,7 @@ EdgeStateSignatureType Signature(GestureState gesture_state,
case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_MOVED:
case GST_SCROLL_FIRST_MOVED_HANDLED:
case GST_SCROLL_FIRST_CANCELLED:
case GST_SCROLL_SECOND_PRESSED:
case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
......@@ -608,6 +611,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (ScrollUpdate(event, point, gestures.get(), FS_NOT_FIRST_SCROLL))
point.UpdateForScroll();
break;
case GST_SCROLL_FIRST_MOVED_HANDLED:
break;
case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_CANCELLED:
ScrollEnd(event, point, gestures.get());
......
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