Home | History | Annotate | Download | only in input
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/renderer_host/input/gesture_event_filter.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "content/browser/renderer_host/input/input_router.h"
     10 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
     11 #include "content/browser/renderer_host/input/touchscreen_tap_suppression_controller.h"
     12 #include "content/public/common/content_switches.h"
     13 
     14 using WebKit::WebGestureEvent;
     15 using WebKit::WebInputEvent;
     16 
     17 namespace content {
     18 namespace {
     19 
     20 // Default maximum time between the GestureRecognizer generating a
     21 // GestureTapDown and when it is forwarded to the renderer.
     22 #if !defined(OS_ANDROID)
     23 static const int kTapDownDeferralTimeMs = 150;
     24 #else
     25 // Android OS sends this gesture with a delay already.
     26 static const int kTapDownDeferralTimeMs = 0;
     27 #endif
     28 
     29 // Default debouncing interval duration: if a scroll is in progress, non-scroll
     30 // events during this interval are deferred to either its end or discarded on
     31 // receipt of another GestureScrollUpdate.
     32 static const int kDebouncingIntervalTimeMs = 30;
     33 
     34 // Sets |*value| to |switchKey| if it exists or sets it to |defaultValue|.
     35 static void GetParamHelper(int* value,
     36                            int defaultValue,
     37                            const char switchKey[]) {
     38   if (*value < 0) {
     39     *value = defaultValue;
     40     CommandLine* command_line = CommandLine::ForCurrentProcess();
     41     std::string command_line_param =
     42         command_line->GetSwitchValueASCII(switchKey);
     43     if (!command_line_param.empty()) {
     44       int v;
     45       if (base::StringToInt(command_line_param, &v))
     46         *value = v;
     47     }
     48     DCHECK_GE(*value, 0);
     49   }
     50 }
     51 
     52 static int GetTapDownDeferralTimeMs() {
     53   static int tap_down_deferral_time_window = -1;
     54   GetParamHelper(&tap_down_deferral_time_window,
     55                  kTapDownDeferralTimeMs,
     56                  switches::kTapDownDeferralTimeMs);
     57   return tap_down_deferral_time_window;
     58 }
     59 }  // namespace
     60 
     61 GestureEventFilter::GestureEventFilter(InputRouter* input_router)
     62      : input_router_(input_router),
     63        fling_in_progress_(false),
     64        scrolling_in_progress_(false),
     65        ignore_next_ack_(false),
     66        combined_scroll_pinch_(gfx::Transform()),
     67        touchpad_tap_suppression_controller_(
     68            new TouchpadTapSuppressionController(input_router)),
     69        touchscreen_tap_suppression_controller_(
     70            new TouchscreenTapSuppressionController(this)),
     71        maximum_tap_gap_time_ms_(GetTapDownDeferralTimeMs()),
     72        debounce_interval_time_ms_(kDebouncingIntervalTimeMs) {
     73 }
     74 
     75 GestureEventFilter::~GestureEventFilter() { }
     76 
     77 bool GestureEventFilter::ShouldDiscardFlingCancelEvent(
     78     const GestureEventWithLatencyInfo& gesture_event) const {
     79   if (coalesced_gesture_events_.empty() && fling_in_progress_)
     80     return false;
     81   GestureEventQueue::const_reverse_iterator it =
     82       coalesced_gesture_events_.rbegin();
     83   while (it != coalesced_gesture_events_.rend()) {
     84     if (it->event.type == WebInputEvent::GestureFlingStart)
     85       return false;
     86     if (it->event.type == WebInputEvent::GestureFlingCancel)
     87       return true;
     88     it++;
     89   }
     90   return true;
     91 }
     92 
     93 bool GestureEventFilter::ShouldForwardForBounceReduction(
     94     const GestureEventWithLatencyInfo& gesture_event) {
     95   if (debounce_interval_time_ms_ ==  0)
     96     return true;
     97   switch (gesture_event.event.type) {
     98     case WebInputEvent::GestureScrollUpdate:
     99       if (!scrolling_in_progress_) {
    100         debounce_deferring_timer_.Start(
    101             FROM_HERE,
    102             base::TimeDelta::FromMilliseconds(debounce_interval_time_ms_),
    103             this,
    104             &GestureEventFilter::SendScrollEndingEventsNow);
    105       } else {
    106         // Extend the bounce interval.
    107         debounce_deferring_timer_.Reset();
    108       }
    109       scrolling_in_progress_ = true;
    110       debouncing_deferral_queue_.clear();
    111       return true;
    112     case WebInputEvent::GesturePinchBegin:
    113     case WebInputEvent::GesturePinchEnd:
    114     case WebInputEvent::GesturePinchUpdate:
    115       // TODO(rjkroege): Debounce pinch (http://crbug.com/147647)
    116       return true;
    117     default:
    118       if (scrolling_in_progress_) {
    119         debouncing_deferral_queue_.push_back(gesture_event);
    120         return false;
    121       }
    122       return true;
    123   }
    124 
    125   NOTREACHED();
    126   return false;
    127 }
    128 
    129 // NOTE: The filters are applied successively. This simplifies the change.
    130 bool GestureEventFilter::ShouldForward(
    131     const GestureEventWithLatencyInfo& gesture_event) {
    132   return ShouldForwardForZeroVelocityFlingStart(gesture_event) &&
    133       ShouldForwardForBounceReduction(gesture_event) &&
    134       ShouldForwardForGFCFiltering(gesture_event) &&
    135       ShouldForwardForTapSuppression(gesture_event) &&
    136       ShouldForwardForTapDeferral(gesture_event) &&
    137       ShouldForwardForCoalescing(gesture_event);
    138 }
    139 
    140 bool GestureEventFilter::ShouldForwardForZeroVelocityFlingStart(
    141     const GestureEventWithLatencyInfo& gesture_event) const {
    142   return gesture_event.event.type != WebInputEvent::GestureFlingStart ||
    143       gesture_event.event.sourceDevice != WebGestureEvent::Touchpad ||
    144       gesture_event.event.data.flingStart.velocityX != 0 ||
    145       gesture_event.event.data.flingStart.velocityY != 0;
    146 }
    147 
    148 bool GestureEventFilter::ShouldForwardForGFCFiltering(
    149     const GestureEventWithLatencyInfo& gesture_event) const {
    150   return gesture_event.event.type != WebInputEvent::GestureFlingCancel ||
    151       !ShouldDiscardFlingCancelEvent(gesture_event);
    152 }
    153 
    154 bool GestureEventFilter::ShouldForwardForTapSuppression(
    155     const GestureEventWithLatencyInfo& gesture_event) {
    156   switch (gesture_event.event.type) {
    157     case WebInputEvent::GestureFlingCancel:
    158       if (gesture_event.event.sourceDevice == WebGestureEvent::Touchscreen)
    159         touchscreen_tap_suppression_controller_->GestureFlingCancel();
    160       else
    161         touchpad_tap_suppression_controller_->GestureFlingCancel();
    162       return true;
    163     case WebInputEvent::GestureTapDown:
    164       return !touchscreen_tap_suppression_controller_->
    165           ShouldDeferGestureTapDown(gesture_event);
    166     case WebInputEvent::GestureTapCancel:
    167       return !touchscreen_tap_suppression_controller_->
    168           ShouldSuppressGestureTapCancel();
    169     case WebInputEvent::GestureTap:
    170     case WebInputEvent::GestureTapUnconfirmed:
    171       return !touchscreen_tap_suppression_controller_->
    172           ShouldSuppressGestureTap();
    173     default:
    174       return true;
    175   }
    176   NOTREACHED();
    177   return false;
    178 }
    179 
    180 bool GestureEventFilter::ShouldForwardForTapDeferral(
    181     const GestureEventWithLatencyInfo& gesture_event) {
    182   switch (gesture_event.event.type) {
    183     case WebInputEvent::GestureTapDown:
    184       // GestureTapDown is always paired with either a Tap, or TapCancel, so it
    185       // should be impossible to have more than one outstanding at a time.
    186       DCHECK_EQ(deferred_tap_down_event_.event.type, WebInputEvent::Undefined);
    187       deferred_tap_down_event_ = gesture_event;
    188       send_gtd_timer_.Start(
    189           FROM_HERE,
    190           base::TimeDelta::FromMilliseconds(maximum_tap_gap_time_ms_),
    191           this,
    192           &GestureEventFilter::SendGestureTapDownNow);
    193       return false;
    194     case WebInputEvent::GestureTapCancel:
    195       if (deferred_tap_down_event_.event.type == WebInputEvent::Undefined) {
    196         // The TapDown has already been put in the queue, must send the
    197         // corresponding TapCancel as well.
    198         return true;
    199       }
    200       // Cancelling a deferred TapDown, just drop them on the floor.
    201       send_gtd_timer_.Stop();
    202       deferred_tap_down_event_.event.type = WebInputEvent::Undefined;
    203       return false;
    204     case WebInputEvent::GestureTap:
    205       send_gtd_timer_.Stop();
    206       if (deferred_tap_down_event_.event.type != WebInputEvent::Undefined) {
    207         ForwardGestureEventSkipDeferral(deferred_tap_down_event_);
    208         deferred_tap_down_event_.event.type = WebInputEvent::Undefined;
    209       }
    210       return true;
    211     case WebInputEvent::GestureFlingStart:
    212     case WebInputEvent::GestureScrollBegin:
    213     case WebInputEvent::GesturePinchBegin:
    214       send_gtd_timer_.Stop();
    215       deferred_tap_down_event_.event.type = WebInputEvent::Undefined;
    216       return true;
    217     default:
    218       return true;
    219   }
    220 
    221   NOTREACHED();
    222   return true;
    223 }
    224 
    225 bool GestureEventFilter::ShouldForwardForCoalescing(
    226     const GestureEventWithLatencyInfo& gesture_event) {
    227   switch (gesture_event.event.type) {
    228     case WebInputEvent::GestureFlingCancel:
    229       fling_in_progress_ = false;
    230       break;
    231     case WebInputEvent::GestureFlingStart:
    232       fling_in_progress_ = true;
    233       break;
    234     case WebInputEvent::GesturePinchUpdate:
    235     case WebInputEvent::GestureScrollUpdate:
    236       MergeOrInsertScrollAndPinchEvent(gesture_event);
    237       return ShouldHandleEventNow();
    238     default:
    239       break;
    240   }
    241   coalesced_gesture_events_.push_back(gesture_event);
    242   return ShouldHandleEventNow();
    243 }
    244 
    245 void GestureEventFilter::ProcessGestureAck(bool processed, int type) {
    246   if (coalesced_gesture_events_.empty()) {
    247     DLOG(ERROR) << "Received unexpected ACK for event type " << type;
    248     return;
    249   }
    250   DCHECK_EQ(coalesced_gesture_events_.front().event.type, type);
    251   if (type == WebInputEvent::GestureFlingCancel) {
    252     if (coalesced_gesture_events_.front().event.sourceDevice ==
    253         WebGestureEvent::Touchscreen)
    254       touchscreen_tap_suppression_controller_->GestureFlingCancelAck(processed);
    255     else
    256       touchpad_tap_suppression_controller_->GestureFlingCancelAck(processed);
    257   }
    258   coalesced_gesture_events_.pop_front();
    259   if (ignore_next_ack_) {
    260     ignore_next_ack_ = false;
    261   } else if (!coalesced_gesture_events_.empty()) {
    262     const GestureEventWithLatencyInfo& next_gesture_event =
    263         coalesced_gesture_events_.front();
    264     input_router_->SendGestureEventImmediately(next_gesture_event);
    265     // TODO(yusufo): Introduce GesturePanScroll so that these can be combined
    266     // into one gesture and kept inside the queue that way.
    267     if (coalesced_gesture_events_.size() > 1) {
    268       const GestureEventWithLatencyInfo& second_gesture_event =
    269           coalesced_gesture_events_[1];
    270       if (next_gesture_event.event.type ==
    271               WebInputEvent::GestureScrollUpdate &&
    272           second_gesture_event.event.type ==
    273               WebInputEvent::GesturePinchUpdate) {
    274         input_router_->SendGestureEventImmediately(second_gesture_event);
    275         ignore_next_ack_ = true;
    276         combined_scroll_pinch_ = gfx::Transform();
    277       }
    278     }
    279   }
    280 }
    281 
    282 TouchpadTapSuppressionController*
    283     GestureEventFilter::GetTouchpadTapSuppressionController() {
    284   return touchpad_tap_suppression_controller_.get();
    285 }
    286 
    287 bool GestureEventFilter::HasQueuedGestureEvents() const {
    288   return !coalesced_gesture_events_.empty();
    289 }
    290 
    291 const WebKit::WebGestureEvent&
    292 GestureEventFilter::GetGestureEventAwaitingAck() const {
    293   DCHECK(!coalesced_gesture_events_.empty());
    294   if (!ignore_next_ack_)
    295     return coalesced_gesture_events_.front().event;
    296   else
    297     return coalesced_gesture_events_.at(1).event;
    298 }
    299 
    300 void GestureEventFilter::FlingHasBeenHalted() {
    301   fling_in_progress_ = false;
    302 }
    303 
    304 bool GestureEventFilter::ShouldHandleEventNow() const {
    305   return coalesced_gesture_events_.size() == 1;
    306 }
    307 
    308 void GestureEventFilter::ForwardGestureEventForDeferral(
    309     const GestureEventWithLatencyInfo& gesture_event) {
    310   if (ShouldForwardForTapDeferral(gesture_event))
    311     ForwardGestureEventSkipDeferral(gesture_event);
    312 }
    313 
    314 void GestureEventFilter::ForwardGestureEventSkipDeferral(
    315     const GestureEventWithLatencyInfo& gesture_event) {
    316   if (ShouldForwardForCoalescing(gesture_event))
    317       input_router_->SendGestureEventImmediately(gesture_event);
    318 }
    319 
    320 void GestureEventFilter::SendGestureTapDownNow() {
    321   // We must not have already sent the deferred TapDown (if we did, we would
    322   // have stopped the timer, which prevents this task from running - even if
    323   // it's time had already elapsed).
    324   DCHECK_EQ(deferred_tap_down_event_.event.type, WebInputEvent::GestureTapDown);
    325   ForwardGestureEventSkipDeferral(deferred_tap_down_event_);
    326   deferred_tap_down_event_.event.type = WebInputEvent::Undefined;
    327 }
    328 
    329 void GestureEventFilter::SendScrollEndingEventsNow() {
    330   scrolling_in_progress_ = false;
    331   for (GestureEventQueue::const_iterator it =
    332       debouncing_deferral_queue_.begin();
    333       it != debouncing_deferral_queue_.end(); it++) {
    334     if (ShouldForwardForGFCFiltering(*it) &&
    335         ShouldForwardForTapSuppression(*it) &&
    336         ShouldForwardForTapDeferral(*it) &&
    337         ShouldForwardForCoalescing(*it)) {
    338       input_router_->SendGestureEventImmediately(*it);
    339     }
    340   }
    341   debouncing_deferral_queue_.clear();
    342 }
    343 
    344 void GestureEventFilter::MergeOrInsertScrollAndPinchEvent(
    345     const GestureEventWithLatencyInfo& gesture_event) {
    346   if (coalesced_gesture_events_.size() <= 1) {
    347     coalesced_gesture_events_.push_back(gesture_event);
    348     return;
    349   }
    350   GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
    351   if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate &&
    352       last_event->event.type == WebInputEvent::GestureScrollUpdate &&
    353       last_event->event.modifiers == gesture_event.event.modifiers) {
    354     last_event->event.data.scrollUpdate.deltaX +=
    355         gesture_event.event.data.scrollUpdate.deltaX;
    356     last_event->event.data.scrollUpdate.deltaY +=
    357         gesture_event.event.data.scrollUpdate.deltaY;
    358     last_event->latency.MergeWith(gesture_event.latency);
    359     return;
    360   }
    361   if (coalesced_gesture_events_.size() == 2 ||
    362       (coalesced_gesture_events_.size() == 3 && ignore_next_ack_) ||
    363       !ShouldTryMerging(gesture_event, *last_event)) {
    364     coalesced_gesture_events_.push_back(gesture_event);
    365     return;
    366   }
    367   GestureEventWithLatencyInfo scroll_event;
    368   GestureEventWithLatencyInfo pinch_event;
    369   scroll_event.event.modifiers |= gesture_event.event.modifiers;
    370   scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
    371   scroll_event.latency = gesture_event.latency;
    372   scroll_event.latency.MergeWith(last_event->latency);
    373   pinch_event = scroll_event;
    374   scroll_event.event.type = WebInputEvent::GestureScrollUpdate;
    375   pinch_event.event.type = WebInputEvent::GesturePinchUpdate;
    376   pinch_event.event.x = gesture_event.event.type ==
    377       WebInputEvent::GesturePinchUpdate ?
    378           gesture_event.event.x : last_event->event.x;
    379   pinch_event.event.y = gesture_event.event.type ==
    380       WebInputEvent::GesturePinchUpdate ?
    381           gesture_event.event.y : last_event->event.y;
    382 
    383   combined_scroll_pinch_.ConcatTransform(GetTransformForEvent(gesture_event));
    384   GestureEventWithLatencyInfo* second_last_event = &coalesced_gesture_events_
    385       [coalesced_gesture_events_.size() - 2];
    386   if (ShouldTryMerging(gesture_event, *second_last_event)) {
    387     scroll_event.latency.MergeWith(second_last_event->latency);
    388     pinch_event.latency.MergeWith(second_last_event->latency);
    389     coalesced_gesture_events_.pop_back();
    390   } else {
    391     DCHECK(combined_scroll_pinch_ == GetTransformForEvent(gesture_event));
    392     combined_scroll_pinch_.
    393         PreconcatTransform(GetTransformForEvent(*last_event));
    394   }
    395   coalesced_gesture_events_.pop_back();
    396   float combined_scale = combined_scroll_pinch_.matrix().getDouble(0, 0);
    397   scroll_event.event.data.scrollUpdate.deltaX =
    398       (combined_scroll_pinch_.matrix().getDouble(0, 3) + pinch_event.event.x)
    399           / combined_scale - pinch_event.event.x;
    400   scroll_event.event.data.scrollUpdate.deltaY =
    401       (combined_scroll_pinch_.matrix().getDouble(1, 3) + pinch_event.event.y)
    402           / combined_scale - pinch_event.event.y;
    403   coalesced_gesture_events_.push_back(scroll_event);
    404   pinch_event.event.data.pinchUpdate.scale = combined_scale;
    405   coalesced_gesture_events_.push_back(pinch_event);
    406 }
    407 
    408 bool GestureEventFilter::ShouldTryMerging(
    409     const GestureEventWithLatencyInfo& new_event,
    410     const GestureEventWithLatencyInfo& event_in_queue) const {
    411   DLOG_IF(WARNING,
    412           new_event.event.timeStampSeconds <
    413           event_in_queue.event.timeStampSeconds)
    414           << "Event time not monotonic?\n";
    415   return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
    416       event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
    417       event_in_queue.event.modifiers == new_event.event.modifiers;
    418 }
    419 
    420 gfx::Transform GestureEventFilter::GetTransformForEvent(
    421     const GestureEventWithLatencyInfo& gesture_event) const {
    422   gfx::Transform gesture_transform = gfx::Transform();
    423   if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) {
    424     gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX,
    425                                 gesture_event.event.data.scrollUpdate.deltaY);
    426   } else if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate) {
    427     float scale = gesture_event.event.data.pinchUpdate.scale;
    428     gesture_transform.Translate(-gesture_event.event.x, -gesture_event.event.y);
    429     gesture_transform.Scale(scale,scale);
    430     gesture_transform.Translate(gesture_event.event.x, gesture_event.event.y);
    431   }
    432   return gesture_transform;
    433 }
    434 }  // namespace content
    435