| Index: content/browser/renderer_host/input/touch_disposition_gesture_filter.cc
|
| diff --git a/content/browser/renderer_host/input/touch_disposition_gesture_filter.cc b/content/browser/renderer_host/input/touch_disposition_gesture_filter.cc
|
| index 35db76779b68b34b26912e864c09b3983abf38ee..36de573810acd82f49292c4dc797bc485433b556 100644
|
| --- a/content/browser/renderer_host/input/touch_disposition_gesture_filter.cc
|
| +++ b/content/browser/renderer_host/input/touch_disposition_gesture_filter.cc
|
| @@ -24,6 +24,79 @@ WebGestureEvent CreateGesture(WebInputEvent::Type type) {
|
| return event;
|
| }
|
|
|
| +enum RequiredTouches {
|
| + RT_NONE = 0,
|
| + RT_START = 1 << 0,
|
| + RT_CURRENT = 1 << 1,
|
| +};
|
| +
|
| +struct DispositionHandlingInfo {
|
| + // A bitwise-OR of |RequiredTouches|.
|
| + int required_touches;
|
| + blink::WebInputEvent::Type antecedent_event_type;
|
| +
|
| + DispositionHandlingInfo(int required_touches)
|
| + : required_touches(required_touches) {}
|
| +
|
| + DispositionHandlingInfo(int required_touches,
|
| + blink::WebInputEvent::Type antecedent_event_type)
|
| + : required_touches(required_touches),
|
| + antecedent_event_type(antecedent_event_type) {}
|
| +};
|
| +
|
| +DispositionHandlingInfo Info(int required_touches) {
|
| + return DispositionHandlingInfo(required_touches);
|
| +}
|
| +
|
| +DispositionHandlingInfo Info(int required_touches,
|
| + blink::WebInputEvent::Type antecedent_event_type) {
|
| + return DispositionHandlingInfo(required_touches, antecedent_event_type);
|
| +}
|
| +
|
| +// This approach to disposition handling is described at http://goo.gl/5G8PWJ.
|
| +DispositionHandlingInfo GetDispositionHandlingInfo(
|
| + blink::WebInputEvent::Type type) {
|
| + switch (type) {
|
| + case WebInputEvent::GestureTapDown:
|
| + return Info(RT_START);
|
| + case WebInputEvent::GestureTapCancel:
|
| + return Info(RT_START);
|
| + case WebInputEvent::GestureShowPress:
|
| + return Info(RT_START);
|
| + case WebInputEvent::GestureLongPress:
|
| + return Info(RT_START);
|
| + case WebInputEvent::GestureLongTap:
|
| + return Info(RT_START | RT_CURRENT);
|
| + case WebInputEvent::GestureTap:
|
| + return Info(RT_START | RT_CURRENT, WebInputEvent::GestureTapUnconfirmed);
|
| + case WebInputEvent::GestureTwoFingerTap:
|
| + return Info(RT_START | RT_CURRENT);
|
| + case WebInputEvent::GestureTapUnconfirmed:
|
| + return Info(RT_START | RT_CURRENT);
|
| + case WebInputEvent::GestureDoubleTap:
|
| + return Info(RT_START | RT_CURRENT, WebInputEvent::GestureTapUnconfirmed);
|
| + case WebInputEvent::GestureScrollBegin:
|
| + return Info(RT_START | RT_CURRENT);
|
| + case WebInputEvent::GestureScrollUpdate:
|
| + return Info(RT_CURRENT, WebInputEvent::GestureScrollBegin);
|
| + case WebInputEvent::GestureScrollEnd:
|
| + return Info(RT_NONE, WebInputEvent::GestureScrollBegin);
|
| + case WebInputEvent::GestureFlingStart:
|
| + return Info(RT_NONE, WebInputEvent::GestureScrollBegin);
|
| + case WebInputEvent::GestureFlingCancel:
|
| + return Info(RT_NONE, WebInputEvent::GestureFlingStart);
|
| + case WebInputEvent::GesturePinchBegin:
|
| + return Info(RT_START, WebInputEvent::GestureScrollBegin);
|
| + case WebInputEvent::GesturePinchUpdate:
|
| + return Info(RT_CURRENT, WebInputEvent::GesturePinchBegin);
|
| + case WebInputEvent::GesturePinchEnd:
|
| + return Info(RT_NONE, WebInputEvent::GesturePinchBegin);
|
| + default:
|
| + NOTREACHED();
|
| + return Info(RT_NONE);
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| // TouchDispositionGestureFilter
|
| @@ -41,23 +114,23 @@ TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {}
|
| TouchDispositionGestureFilter::PacketResult
|
| TouchDispositionGestureFilter::OnGestureEventPacket(
|
| const GestureEventPacket& packet) {
|
| - if (packet.gesture_source() == GestureEventPacket::INVALID)
|
| + if (packet.gesture_source() == GestureEventPacket::UNDEFINED ||
|
| + packet.gesture_source() == GestureEventPacket::INVALID)
|
| return INVALID_PACKET_TYPE;
|
|
|
| - if (packet.gesture_source() == GestureEventPacket::TOUCH_BEGIN)
|
| + if (packet.gesture_source() == GestureEventPacket::TOUCH_SEQUENCE_BEGIN)
|
| sequences_.push(GestureSequence());
|
|
|
| if (IsEmpty())
|
| return INVALID_PACKET_ORDER;
|
|
|
| - if (packet.gesture_source() == GestureEventPacket::TOUCH_TIMEOUT) {
|
| - // Handle the timeout packet immediately if the packet preceding the
|
| - // timeout has already been dispatched.
|
| - if (Tail().IsEmpty()) {
|
| - if (!Tail().IsGesturePrevented())
|
| - SendPacket(packet);
|
| - return SUCCESS;
|
| - }
|
| + if (packet.gesture_source() == GestureEventPacket::TOUCH_TIMEOUT &&
|
| + Tail().IsEmpty()) {
|
| + // Handle the timeout packet immediately if the packet preceding the timeout
|
| + // has already been dispatched.
|
| + FilterAndSendPacket(
|
| + packet, Tail().state(), INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + return SUCCESS;
|
| }
|
|
|
| Tail().Push(packet);
|
| @@ -73,48 +146,51 @@ void TouchDispositionGestureFilter::OnTouchEventAck(
|
| if (Head().IsEmpty()) {
|
| CancelTapIfNecessary();
|
| CancelFlingIfNecessary();
|
| + last_event_of_type_dropped_.clear();
|
| sequences_.pop();
|
| }
|
|
|
| GestureSequence& sequence = Head();
|
| - sequence.UpdateState(ack_state);
|
|
|
| // Dispatch the packet corresponding to the ack'ed touch, as well as any
|
| // additional timeout-based packets queued before the ack was received.
|
| bool touch_packet_for_current_ack_handled = false;
|
| while (!sequence.IsEmpty()) {
|
| const GestureEventPacket& packet = sequence.Front();
|
| + DCHECK_NE(packet.gesture_source(), GestureEventPacket::UNDEFINED);
|
| + DCHECK_NE(packet.gesture_source(), GestureEventPacket::INVALID);
|
|
|
| - if (packet.gesture_source() == GestureEventPacket::TOUCH_BEGIN ||
|
| - packet.gesture_source() == GestureEventPacket::TOUCH) {
|
| - // We should handle at most one touch-based packet corresponding to a
|
| - // given ack.
|
| + if (packet.gesture_source() != GestureEventPacket::TOUCH_TIMEOUT) {
|
| + // We should handle at most one non-timeout based packet.
|
| if (touch_packet_for_current_ack_handled)
|
| break;
|
| + sequence.UpdateState(packet.gesture_source(), ack_state);
|
| touch_packet_for_current_ack_handled = true;
|
| }
|
| -
|
| - if (!sequence.IsGesturePrevented())
|
| - SendPacket(packet);
|
| -
|
| + FilterAndSendPacket(packet, sequence.state(), ack_state);
|
| sequence.Pop();
|
| }
|
| DCHECK(touch_packet_for_current_ack_handled);
|
| -
|
| - // Immediately cancel a TapDown if TouchStart went unconsumed, but a
|
| - // subsequent TouchMove is consumed.
|
| - if (sequence.IsGesturePrevented())
|
| - CancelTapIfNecessary();
|
| }
|
|
|
| bool TouchDispositionGestureFilter::IsEmpty() const {
|
| return sequences_.empty();
|
| }
|
|
|
| -void TouchDispositionGestureFilter::SendPacket(
|
| - const GestureEventPacket& packet) {
|
| - for (size_t i = 0; i < packet.gesture_count(); ++i)
|
| - SendGesture(packet.gesture(i));
|
| +void TouchDispositionGestureFilter::FilterAndSendPacket(
|
| + const GestureEventPacket& packet,
|
| + const GestureSequence::GestureHandlingState& sequence_state,
|
| + InputEventAckState ack_state) {
|
| + for (size_t i = 0; i < packet.gesture_count(); ++i) {
|
| + const blink::WebGestureEvent& gesture = packet.gesture(i);
|
| + if (IsGesturePrevented(gesture.type, ack_state, sequence_state)) {
|
| + last_event_of_type_dropped_.insert(gesture.type);
|
| + CancelTapIfNecessary();
|
| + continue;
|
| + }
|
| + last_event_of_type_dropped_.erase(gesture.type);
|
| + SendGesture(gesture);
|
| + }
|
| }
|
|
|
| void TouchDispositionGestureFilter::SendGesture(const WebGestureEvent& event) {
|
| @@ -124,6 +200,7 @@ void TouchDispositionGestureFilter::SendGesture(const WebGestureEvent& event) {
|
| CancelFlingIfNecessary();
|
| break;
|
| case WebInputEvent::GestureTapDown:
|
| + DCHECK(!needs_tap_ending_event_);
|
| needs_tap_ending_event_ = true;
|
| break;
|
| case WebInputEvent::GestureTapCancel:
|
| @@ -165,6 +242,14 @@ void TouchDispositionGestureFilter::CancelFlingIfNecessary() {
|
| DCHECK(!needs_fling_ending_event_);
|
| }
|
|
|
| +// TouchDispositionGestureFilter::GestureSequence
|
| +
|
| +TouchDispositionGestureFilter::GestureSequence::GestureHandlingState::
|
| + GestureHandlingState()
|
| + : seen_ack(false),
|
| + start_consumed(false),
|
| + no_consumer(false) {}
|
| +
|
| TouchDispositionGestureFilter::GestureSequence&
|
| TouchDispositionGestureFilter::Head() {
|
| DCHECK(!sequences_.empty());
|
| @@ -177,11 +262,7 @@ TouchDispositionGestureFilter::Tail() {
|
| return sequences_.back();
|
| }
|
|
|
| -
|
| -// TouchDispositionGestureFilter::GestureSequence
|
| -
|
| -TouchDispositionGestureFilter::GestureSequence::GestureSequence()
|
| - : state_(PENDING) {}
|
| +TouchDispositionGestureFilter::GestureSequence::GestureSequence() {}
|
|
|
| TouchDispositionGestureFilter::GestureSequence::~GestureSequence() {}
|
|
|
| @@ -202,25 +283,46 @@ TouchDispositionGestureFilter::GestureSequence::Front() const {
|
| }
|
|
|
| void TouchDispositionGestureFilter::GestureSequence::UpdateState(
|
| + GestureEventPacket::GestureSource gesture_source,
|
| InputEventAckState ack_state) {
|
| DCHECK_NE(INPUT_EVENT_ACK_STATE_UNKNOWN, ack_state);
|
| // Permanent states will not be affected by subsequent ack's.
|
| - if (state_ != PENDING && state_ != ALLOWED_UNTIL_PREVENTED)
|
| + if (state_.no_consumer || state_.start_consumed)
|
| return;
|
|
|
| // |NO_CONSUMER| should only be effective when the *first* touch is ack'ed.
|
| - if (state_ == PENDING &&
|
| - ack_state == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
|
| - state_ = ALWAYS_ALLOWED;
|
| - else if (ack_state == INPUT_EVENT_ACK_STATE_CONSUMED)
|
| - state_ = ALWAYS_PREVENTED;
|
| - else
|
| - state_ = ALLOWED_UNTIL_PREVENTED;
|
| -}
|
| -
|
| -bool TouchDispositionGestureFilter::GestureSequence::IsGesturePrevented()
|
| - const {
|
| - return state_ == ALWAYS_PREVENTED;
|
| + if (!state_.seen_ack &&
|
| + ack_state == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
|
| + state_.no_consumer = true;
|
| + } else if (ack_state == INPUT_EVENT_ACK_STATE_CONSUMED) {
|
| + if (gesture_source == GestureEventPacket::TOUCH_SEQUENCE_BEGIN ||
|
| + gesture_source == GestureEventPacket::TOUCH_BEGIN) {
|
| + state_.start_consumed = true;
|
| + }
|
| + }
|
| + state_.seen_ack = true;
|
| +}
|
| +
|
| +bool TouchDispositionGestureFilter::IsGesturePrevented(
|
| + WebInputEvent::Type gesture_type,
|
| + InputEventAckState current,
|
| + const GestureSequence::GestureHandlingState& state) const {
|
| +
|
| + if (state.no_consumer)
|
| + return false;
|
| +
|
| + DispositionHandlingInfo disposition_handling_info =
|
| + GetDispositionHandlingInfo(gesture_type);
|
| +
|
| + int required_touches = disposition_handling_info.required_touches;
|
| + bool current_consumed = current == INPUT_EVENT_ACK_STATE_CONSUMED;
|
| + if ((required_touches & RT_START && state.start_consumed) ||
|
| + (required_touches & RT_CURRENT && current_consumed) ||
|
| + (last_event_of_type_dropped_.count(
|
| + disposition_handling_info.antecedent_event_type) > 0)) {
|
| + return true;
|
| + }
|
| + return false;
|
| }
|
|
|
| bool TouchDispositionGestureFilter::GestureSequence::IsEmpty() const {
|
|
|