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; ...@@ -14,34 +14,42 @@ using blink::WebTouchPoint;
namespace content { namespace content {
namespace { namespace {
bool IsTouchSequenceStart(const WebTouchEvent& event) { GestureEventPacket::GestureSource ToGestureSource(const WebTouchEvent& event) {
if (event.type != WebInputEvent::TouchStart)
return false;
if (!event.touchesLength) if (!event.touchesLength)
return false; return GestureEventPacket::INVALID;
switch (event.type) {
case WebInputEvent::TouchStart:
for (size_t i = 0; i < event.touchesLength; i++) { for (size_t i = 0; i < event.touchesLength; i++) {
if (event.touches[i].state != WebTouchPoint::StatePressed) if (event.touches[i].state != WebTouchPoint::StatePressed)
return false; 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 } // namespace
GestureEventPacket::GestureEventPacket() GestureEventPacket::GestureEventPacket()
: gesture_count_(0), : gesture_count_(0),
gesture_source_(INVALID) {} gesture_source_(UNDEFINED) {}
GestureEventPacket::GestureEventPacket(GestureSource source) GestureEventPacket::GestureEventPacket(GestureSource source)
: gesture_count_(0), : gesture_count_(0),
gesture_source_(source) { gesture_source_(source) {
DCHECK_NE(gesture_source_, INVALID); DCHECK_NE(gesture_source_, UNDEFINED);
} }
GestureEventPacket::~GestureEventPacket() {} GestureEventPacket::~GestureEventPacket() {}
......
...@@ -15,9 +15,13 @@ namespace content { ...@@ -15,9 +15,13 @@ namespace content {
class CONTENT_EXPORT GestureEventPacket { class CONTENT_EXPORT GestureEventPacket {
public: public:
enum GestureSource { enum GestureSource {
INVALID = -1, // Used only for a default-constructed packet.. UNDEFINED = -1, // Used only for a default-constructed packet.
TOUCH_BEGIN, // The start of a new gesture sequence. INVALID, // The source of the gesture was invalid.
TOUCH, // Continuation of an existing gesture sequence. 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. TOUCH_TIMEOUT, // Timeout from an existing gesture sequence.
}; };
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_ #ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_DISPOSITION_GESTURE_FILTER_H_
#define 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 <queue>
#include <set>
#include "content/browser/renderer_host/input/gesture_event_packet.h" #include "content/browser/renderer_host/input/gesture_event_packet.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
...@@ -56,30 +56,45 @@ class CONTENT_EXPORT TouchDispositionGestureFilter { ...@@ -56,30 +56,45 @@ class CONTENT_EXPORT TouchDispositionGestureFilter {
// generated by timeouts from a statinoary finger. // generated by timeouts from a statinoary finger.
class GestureSequence { class GestureSequence {
public: 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();
~GestureSequence(); ~GestureSequence();
void Push(const GestureEventPacket& packet); void Push(const GestureEventPacket& packet);
void Pop(); void Pop();
const GestureEventPacket& Front() const; const GestureEventPacket& Front() const;
void UpdateState(InputEventAckState ack_state); void UpdateState(GestureEventPacket::GestureSource gesture_source,
bool IsGesturePrevented() const; InputEventAckState ack_state);
bool IsEmpty() const; bool IsEmpty() const;
const GestureHandlingState& state() const { return state_; };
private: private:
std::queue<GestureEventPacket> packets_; 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_; GestureHandlingState state_;
}; };
bool IsGesturePrevented(blink::WebInputEvent::Type type,
InputEventAckState current,
const GestureSequence::GestureHandlingState& state)
const;
void UpdateAndDispatchPackets(GestureSequence* sequence, void UpdateAndDispatchPackets(GestureSequence* sequence,
InputEventAckState ack_result); 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 SendGesture(const blink::WebGestureEvent& gesture);
void CancelTapIfNecessary(); void CancelTapIfNecessary();
void CancelFlingIfNecessary(); void CancelFlingIfNecessary();
...@@ -89,6 +104,11 @@ class CONTENT_EXPORT TouchDispositionGestureFilter { ...@@ -89,6 +104,11 @@ class CONTENT_EXPORT TouchDispositionGestureFilter {
TouchDispositionGestureFilterClient* client_; TouchDispositionGestureFilterClient* client_;
std::queue<GestureSequence> sequences_; 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 // Bookkeeping for inserting synthetic Gesture{Tap,Fling}Cancel events
// when necessary, e.g., GestureTapCancel when scrolling begins, or // when necessary, e.g., GestureTapCancel when scrolling begins, or
// GestureFlingCancel when a user taps following a GestureFlingStart. // GestureFlingCancel when a user taps following a GestureFlingStart.
......
...@@ -4038,5 +4038,62 @@ TEST_F(GestureRecognizerTest, TestExceedingSlopSlowly) { ...@@ -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 test
} // namespace aura } // namespace aura
...@@ -138,9 +138,11 @@ enum EdgeStateSignatureType { ...@@ -138,9 +138,11 @@ enum EdgeStateSignatureType {
GST_SCROLL_FIRST_RELEASED = GST_SCROLL_FIRST_RELEASED =
G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS), G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS),
// Once scroll has started, process all touch-move events.
GST_SCROLL_FIRST_MOVED = 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 = GST_SCROLL_FIRST_CANCELLED =
G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS), G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS),
...@@ -352,6 +354,7 @@ EdgeStateSignatureType Signature(GestureState gesture_state, ...@@ -352,6 +354,7 @@ EdgeStateSignatureType Signature(GestureState gesture_state,
case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED: case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED:
case GST_SCROLL_FIRST_RELEASED: case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_MOVED: case GST_SCROLL_FIRST_MOVED:
case GST_SCROLL_FIRST_MOVED_HANDLED:
case GST_SCROLL_FIRST_CANCELLED: case GST_SCROLL_FIRST_CANCELLED:
case GST_SCROLL_SECOND_PRESSED: case GST_SCROLL_SECOND_PRESSED:
case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED: case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED:
...@@ -608,6 +611,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -608,6 +611,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (ScrollUpdate(event, point, gestures.get(), FS_NOT_FIRST_SCROLL)) if (ScrollUpdate(event, point, gestures.get(), FS_NOT_FIRST_SCROLL))
point.UpdateForScroll(); point.UpdateForScroll();
break; break;
case GST_SCROLL_FIRST_MOVED_HANDLED:
break;
case GST_SCROLL_FIRST_RELEASED: case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_CANCELLED: case GST_SCROLL_FIRST_CANCELLED:
ScrollEnd(event, point, gestures.get()); 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