diff options
Diffstat (limited to 'chromium/content/browser/renderer_host/input/touch_action_filter.cc')
-rw-r--r-- | chromium/content/browser/renderer_host/input/touch_action_filter.cc | 172 |
1 files changed, 153 insertions, 19 deletions
diff --git a/chromium/content/browser/renderer_host/input/touch_action_filter.cc b/chromium/content/browser/renderer_host/input/touch_action_filter.cc index 665ff15bd30..53d47c4234c 100644 --- a/chromium/content/browser/renderer_host/input/touch_action_filter.cc +++ b/chromium/content/browser/renderer_host/input/touch_action_filter.cc @@ -4,6 +4,9 @@ #include "content/browser/renderer_host/input/touch_action_filter.h" +#include <math.h> + +#include "base/logging.h" #include "third_party/WebKit/public/web/WebInputEvent.h" using blink::WebInputEvent; @@ -13,31 +16,108 @@ namespace content { TouchActionFilter::TouchActionFilter() : drop_scroll_gesture_events_(false), + drop_pinch_gesture_events_(false), + drop_current_tap_ending_event_(false), + allow_current_double_tap_event_(true), allowed_touch_action_(TOUCH_ACTION_AUTO) { } -bool TouchActionFilter::FilterGestureEvent( - const WebGestureEvent& gesture_event) { +bool TouchActionFilter::FilterGestureEvent(WebGestureEvent* gesture_event) { // Filter for allowable touch actions first (eg. before the TouchEventQueue // can decide to send a touch cancel event). - // TODO(rbyers): Add touch-action control over for pinch. crbug.com/247566. - switch(gesture_event.type) { + switch(gesture_event->type) { case WebInputEvent::GestureScrollBegin: - if (allowed_touch_action_ == TOUCH_ACTION_NONE) - drop_scroll_gesture_events_ = true; - // FALL THROUGH + DCHECK(!drop_scroll_gesture_events_); + drop_scroll_gesture_events_ = ShouldSuppressScroll(*gesture_event); + return drop_scroll_gesture_events_; + case WebInputEvent::GestureScrollUpdate: if (drop_scroll_gesture_events_) return true; + else { + if (allowed_touch_action_ == TOUCH_ACTION_PAN_X) { + gesture_event->data.scrollUpdate.deltaY = 0; + gesture_event->data.scrollUpdate.velocityY = 0; + } else if (allowed_touch_action_ == TOUCH_ACTION_PAN_Y) { + gesture_event->data.scrollUpdate.deltaX = 0; + gesture_event->data.scrollUpdate.velocityX = 0; + } + } break; - case WebInputEvent::GestureScrollEnd: case WebInputEvent::GestureFlingStart: - allowed_touch_action_ = content::TOUCH_ACTION_AUTO; - if (drop_scroll_gesture_events_) { - drop_scroll_gesture_events_ = false; + if (gesture_event->sourceDevice != blink::WebGestureDeviceTouchscreen) + break; + if (!drop_scroll_gesture_events_) { + if (allowed_touch_action_ == TOUCH_ACTION_PAN_X) + gesture_event->data.flingStart.velocityY = 0; + if (allowed_touch_action_ == TOUCH_ACTION_PAN_Y) + gesture_event->data.flingStart.velocityX = 0; + } + return FilterScrollEndingGesture(); + + case WebInputEvent::GestureScrollEnd: + return FilterScrollEndingGesture(); + + case WebInputEvent::GesturePinchBegin: + DCHECK(!drop_pinch_gesture_events_); + if (allowed_touch_action_ == TOUCH_ACTION_AUTO || + allowed_touch_action_ & TOUCH_ACTION_PINCH_ZOOM) { + // Pinch events are always bracketed by scroll events, and the W3C + // standard touch-action provides no way to disable scrolling without + // also disabling pinching (validated by the IPC ENUM traits). + DCHECK(allowed_touch_action_ == TOUCH_ACTION_AUTO || + allowed_touch_action_ == TOUCH_ACTION_MANIPULATION); + DCHECK(!drop_scroll_gesture_events_); + } else { + drop_pinch_gesture_events_ = true; + } + return drop_pinch_gesture_events_; + + case WebInputEvent::GesturePinchUpdate: + return drop_pinch_gesture_events_; + + case WebInputEvent::GesturePinchEnd: + if (drop_pinch_gesture_events_) { + drop_pinch_gesture_events_ = false; return true; } + DCHECK(!drop_scroll_gesture_events_); + break; + + // The double tap gesture is a tap ending event. If a double tap gesture is + // filtered out, replace it with a tap event. + case WebInputEvent::GestureDoubleTap: + DCHECK_EQ(1, gesture_event->data.tap.tapCount); + if (!allow_current_double_tap_event_) + gesture_event->type = WebInputEvent::GestureTap; + allow_current_double_tap_event_ = true; + break; + + // If double tap is disabled, there's no reason for the tap delay. + case WebInputEvent::GestureTapUnconfirmed: + DCHECK_EQ(1, gesture_event->data.tap.tapCount); + allow_current_double_tap_event_ = + allowed_touch_action_ == TOUCH_ACTION_AUTO; + if (!allow_current_double_tap_event_) { + gesture_event->type = WebInputEvent::GestureTap; + drop_current_tap_ending_event_ = true; + } + break; + + case WebInputEvent::GestureTap: + allow_current_double_tap_event_ = + allowed_touch_action_ == TOUCH_ACTION_AUTO; + // Fall through. + case WebInputEvent::GestureTapCancel: + if (drop_current_tap_ending_event_) { + drop_current_tap_ending_event_ = false; + return true; + } + break; + + case WebInputEvent::GestureTapDown: + DCHECK(!drop_current_tap_ending_event_); break; default: @@ -49,15 +129,69 @@ bool TouchActionFilter::FilterGestureEvent( return false; } -void TouchActionFilter::OnSetTouchAction( - content::TouchAction touch_action) { +bool TouchActionFilter::FilterScrollEndingGesture() { + DCHECK(!drop_pinch_gesture_events_); + if (drop_scroll_gesture_events_) { + drop_scroll_gesture_events_ = false; + return true; + } + return false; +} + +void TouchActionFilter::OnSetTouchAction(TouchAction touch_action) { // For multiple fingers, we take the intersection of the touch actions for - // all fingers that have gone down during this action. - // TODO(rbyers): What exact multi-finger semantic do we want? This is left - // as implementation-defined in the pointer events specification. - // crbug.com/247566. - if (touch_action == content::TOUCH_ACTION_NONE) - allowed_touch_action_ = content::TOUCH_ACTION_NONE; + // all fingers that have gone down during this action. In the majority of + // real-world scenarios the touch action for all fingers will be the same. + // This is left as implementation-defined in the pointer events + // specification because of the relationship to gestures (which are off + // limits for the spec). I believe the following are desirable properties + // of this choice: + // 1. Not sensitive to finger touch order. Behavior of putting two fingers + // down "at once" will be deterministic. + // 2. Only subtractive - eg. can't trigger scrolling on a element that + // otherwise has scrolling disabling by the addition of a finger. + allowed_touch_action_ = Intersect(allowed_touch_action_, touch_action); +} + +void TouchActionFilter::ResetTouchAction() { + // Note that resetting the action mid-sequence is tolerated. Gestures that had + // their begin event(s) suppressed will be suppressed until the next sequence. + allowed_touch_action_ = TOUCH_ACTION_AUTO; +} + +bool TouchActionFilter::ShouldSuppressScroll( + const blink::WebGestureEvent& gesture_event) { + DCHECK_EQ(gesture_event.type, WebInputEvent::GestureScrollBegin); + if (allowed_touch_action_ == TOUCH_ACTION_AUTO) + return false; + if (allowed_touch_action_ == TOUCH_ACTION_NONE) + return true; + + // If there's no hint or it's perfectly diagonal, then allow the scroll. + if (fabs(gesture_event.data.scrollBegin.deltaXHint) == + fabs(gesture_event.data.scrollBegin.deltaYHint)) + return false; + + // Determine the primary initial axis of the scroll, and check whether + // panning along that axis is permitted. + if (fabs(gesture_event.data.scrollBegin.deltaXHint) > + fabs(gesture_event.data.scrollBegin.deltaYHint)) + return !(allowed_touch_action_ & TOUCH_ACTION_PAN_X); + return !(allowed_touch_action_ & TOUCH_ACTION_PAN_Y); +} + +TouchAction TouchActionFilter::Intersect(TouchAction ta1, TouchAction ta2) { + if (ta1 == TOUCH_ACTION_NONE || ta2 == TOUCH_ACTION_NONE) + return TOUCH_ACTION_NONE; + if (ta1 == TOUCH_ACTION_AUTO) + return ta2; + if (ta2 == TOUCH_ACTION_AUTO) + return ta1; + + // Only the true flags are left - take their intersection. + if (!(ta1 & ta2)) + return TOUCH_ACTION_NONE; + return static_cast<TouchAction>(ta1 & ta2); } } |