Home | History | Annotate | Download | only in input
      1 // Copyright 2014 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_queue.h"
      6 
      7 #include "base/debug/trace_event.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 blink::WebGestureEvent;
     15 using blink::WebInputEvent;
     16 
     17 namespace content {
     18 
     19 GestureEventQueue::Config::Config() {
     20 }
     21 
     22 GestureEventQueue::GestureEventQueue(
     23     GestureEventQueueClient* client,
     24     TouchpadTapSuppressionControllerClient* touchpad_client,
     25     const Config& config)
     26     : client_(client),
     27       fling_in_progress_(false),
     28       scrolling_in_progress_(false),
     29       ignore_next_ack_(false),
     30       touchpad_tap_suppression_controller_(
     31           touchpad_client,
     32           config.touchpad_tap_suppression_config),
     33       touchscreen_tap_suppression_controller_(
     34           this,
     35           config.touchscreen_tap_suppression_config),
     36       debounce_interval_(config.debounce_interval) {
     37   DCHECK(client);
     38   DCHECK(touchpad_client);
     39 }
     40 
     41 GestureEventQueue::~GestureEventQueue() { }
     42 
     43 bool GestureEventQueue::ShouldDiscardFlingCancelEvent(
     44     const GestureEventWithLatencyInfo& gesture_event) const {
     45   if (coalesced_gesture_events_.empty() && fling_in_progress_)
     46     return false;
     47   GestureQueue::const_reverse_iterator it =
     48       coalesced_gesture_events_.rbegin();
     49   while (it != coalesced_gesture_events_.rend()) {
     50     if (it->event.type == WebInputEvent::GestureFlingStart)
     51       return false;
     52     if (it->event.type == WebInputEvent::GestureFlingCancel)
     53       return true;
     54     it++;
     55   }
     56   return true;
     57 }
     58 
     59 bool GestureEventQueue::ShouldForwardForBounceReduction(
     60     const GestureEventWithLatencyInfo& gesture_event) {
     61   if (debounce_interval_ <= base::TimeDelta())
     62     return true;
     63   switch (gesture_event.event.type) {
     64     case WebInputEvent::GestureScrollUpdate:
     65       if (!scrolling_in_progress_) {
     66         debounce_deferring_timer_.Start(
     67             FROM_HERE,
     68             debounce_interval_,
     69             this,
     70             &GestureEventQueue::SendScrollEndingEventsNow);
     71       } else {
     72         // Extend the bounce interval.
     73         debounce_deferring_timer_.Reset();
     74       }
     75       scrolling_in_progress_ = true;
     76       debouncing_deferral_queue_.clear();
     77       return true;
     78     case WebInputEvent::GesturePinchBegin:
     79     case WebInputEvent::GesturePinchEnd:
     80     case WebInputEvent::GesturePinchUpdate:
     81       // TODO(rjkroege): Debounce pinch (http://crbug.com/147647)
     82       return true;
     83     default:
     84       if (scrolling_in_progress_) {
     85         debouncing_deferral_queue_.push_back(gesture_event);
     86         return false;
     87       }
     88       return true;
     89   }
     90 }
     91 
     92 // NOTE: The filters are applied successively. This simplifies the change.
     93 bool GestureEventQueue::ShouldForward(
     94     const GestureEventWithLatencyInfo& gesture_event) {
     95   TRACE_EVENT0("input", "GestureEventQueue::ShouldForward");
     96   return ShouldForwardForBounceReduction(gesture_event) &&
     97          ShouldForwardForGFCFiltering(gesture_event) &&
     98          ShouldForwardForTapSuppression(gesture_event) &&
     99          ShouldForwardForCoalescing(gesture_event);
    100 }
    101 
    102 bool GestureEventQueue::ShouldForwardForGFCFiltering(
    103     const GestureEventWithLatencyInfo& gesture_event) const {
    104   return gesture_event.event.type != WebInputEvent::GestureFlingCancel ||
    105       !ShouldDiscardFlingCancelEvent(gesture_event);
    106 }
    107 
    108 bool GestureEventQueue::ShouldForwardForTapSuppression(
    109     const GestureEventWithLatencyInfo& gesture_event) {
    110   switch (gesture_event.event.type) {
    111     case WebInputEvent::GestureFlingCancel:
    112       if (gesture_event.event.sourceDevice ==
    113           blink::WebGestureDeviceTouchscreen)
    114         touchscreen_tap_suppression_controller_.GestureFlingCancel();
    115       else
    116         touchpad_tap_suppression_controller_.GestureFlingCancel();
    117       return true;
    118     case WebInputEvent::GestureTapDown:
    119     case WebInputEvent::GestureShowPress:
    120     case WebInputEvent::GestureTapUnconfirmed:
    121     case WebInputEvent::GestureTapCancel:
    122     case WebInputEvent::GestureTap:
    123     case WebInputEvent::GestureDoubleTap:
    124       if (gesture_event.event.sourceDevice ==
    125           blink::WebGestureDeviceTouchscreen) {
    126         return !touchscreen_tap_suppression_controller_.FilterTapEvent(
    127             gesture_event);
    128       }
    129       return true;
    130     default:
    131       return true;
    132   }
    133 }
    134 
    135 bool GestureEventQueue::ShouldForwardForCoalescing(
    136     const GestureEventWithLatencyInfo& gesture_event) {
    137   switch (gesture_event.event.type) {
    138     case WebInputEvent::GestureFlingCancel:
    139       fling_in_progress_ = false;
    140       break;
    141     case WebInputEvent::GestureFlingStart:
    142       fling_in_progress_ = true;
    143       break;
    144     case WebInputEvent::GesturePinchUpdate:
    145     case WebInputEvent::GestureScrollUpdate:
    146       MergeOrInsertScrollAndPinchEvent(gesture_event);
    147       return ShouldHandleEventNow();
    148     default:
    149       break;
    150   }
    151   coalesced_gesture_events_.push_back(gesture_event);
    152   return ShouldHandleEventNow();
    153 }
    154 
    155 void GestureEventQueue::ProcessGestureAck(InputEventAckState ack_result,
    156                                            WebInputEvent::Type type,
    157                                            const ui::LatencyInfo& latency) {
    158   TRACE_EVENT0("input", "GestureEventQueue::ProcessGestureAck");
    159 
    160   if (coalesced_gesture_events_.empty()) {
    161     DLOG(ERROR) << "Received unexpected ACK for event type " << type;
    162     return;
    163   }
    164 
    165   // It's possible that the ack for the second event in an in-flight, coalesced
    166   // Gesture{Scroll,Pinch}Update pair is received prior to the first event ack.
    167   // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
    168   size_t event_index = 0;
    169   if (ignore_next_ack_ &&
    170       coalesced_gesture_events_.size() > 1 &&
    171       coalesced_gesture_events_[0].event.type != type &&
    172       coalesced_gesture_events_[1].event.type == type) {
    173     event_index = 1;
    174   }
    175   GestureEventWithLatencyInfo event_with_latency =
    176       coalesced_gesture_events_[event_index];
    177   DCHECK_EQ(event_with_latency.event.type, type);
    178   event_with_latency.latency.AddNewLatencyFrom(latency);
    179 
    180   // Ack'ing an event may enqueue additional gesture events.  By ack'ing the
    181   // event before the forwarding of queued events below, such additional events
    182   // can be coalesced with existing queued events prior to dispatch.
    183   client_->OnGestureEventAck(event_with_latency, ack_result);
    184 
    185   const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
    186   if (type == WebInputEvent::GestureFlingCancel) {
    187     if (event_with_latency.event.sourceDevice ==
    188         blink::WebGestureDeviceTouchscreen)
    189       touchscreen_tap_suppression_controller_.GestureFlingCancelAck(processed);
    190     else
    191       touchpad_tap_suppression_controller_.GestureFlingCancelAck(processed);
    192   }
    193   DCHECK_LT(event_index, coalesced_gesture_events_.size());
    194   coalesced_gesture_events_.erase(coalesced_gesture_events_.begin() +
    195                                   event_index);
    196 
    197   if (ignore_next_ack_) {
    198     ignore_next_ack_ = false;
    199     return;
    200   }
    201 
    202   if (coalesced_gesture_events_.empty())
    203     return;
    204 
    205   const GestureEventWithLatencyInfo& first_gesture_event =
    206       coalesced_gesture_events_.front();
    207 
    208   // TODO(jdduke): Unify GSU/GPU pairs into a single event, crbug.com/359115.
    209   // Check for the coupled GesturePinchUpdate before sending either event,
    210   // handling the case where the first GestureScrollUpdate ack is synchronous.
    211   GestureEventWithLatencyInfo second_gesture_event;
    212   if (first_gesture_event.event.type == WebInputEvent::GestureScrollUpdate &&
    213       coalesced_gesture_events_.size() > 1 &&
    214       coalesced_gesture_events_[1].event.type ==
    215           WebInputEvent::GesturePinchUpdate) {
    216     second_gesture_event = coalesced_gesture_events_[1];
    217     ignore_next_ack_ = true;
    218   }
    219 
    220   client_->SendGestureEventImmediately(first_gesture_event);
    221   if (second_gesture_event.event.type != WebInputEvent::Undefined)
    222     client_->SendGestureEventImmediately(second_gesture_event);
    223 }
    224 
    225 TouchpadTapSuppressionController*
    226     GestureEventQueue::GetTouchpadTapSuppressionController() {
    227   return &touchpad_tap_suppression_controller_;
    228 }
    229 
    230 bool GestureEventQueue::ExpectingGestureAck() const {
    231   return !coalesced_gesture_events_.empty();
    232 }
    233 
    234 void GestureEventQueue::FlingHasBeenHalted() {
    235   fling_in_progress_ = false;
    236 }
    237 
    238 bool GestureEventQueue::ShouldHandleEventNow() const {
    239   return coalesced_gesture_events_.size() == 1;
    240 }
    241 
    242 void GestureEventQueue::ForwardGestureEvent(
    243     const GestureEventWithLatencyInfo& gesture_event) {
    244   if (ShouldForwardForCoalescing(gesture_event))
    245     client_->SendGestureEventImmediately(gesture_event);
    246 }
    247 
    248 void GestureEventQueue::SendScrollEndingEventsNow() {
    249   scrolling_in_progress_ = false;
    250   if (debouncing_deferral_queue_.empty())
    251     return;
    252   GestureQueue debouncing_deferral_queue;
    253   debouncing_deferral_queue.swap(debouncing_deferral_queue_);
    254   for (GestureQueue::const_iterator it = debouncing_deferral_queue.begin();
    255        it != debouncing_deferral_queue.end(); it++) {
    256     if (ShouldForwardForGFCFiltering(*it) &&
    257         ShouldForwardForTapSuppression(*it) &&
    258         ShouldForwardForCoalescing(*it)) {
    259       client_->SendGestureEventImmediately(*it);
    260     }
    261   }
    262 }
    263 
    264 void GestureEventQueue::MergeOrInsertScrollAndPinchEvent(
    265     const GestureEventWithLatencyInfo& gesture_event) {
    266   const size_t unsent_events_count =
    267       coalesced_gesture_events_.size() - EventsInFlightCount();
    268   if (!unsent_events_count) {
    269     coalesced_gesture_events_.push_back(gesture_event);
    270     return;
    271   }
    272 
    273   GestureEventWithLatencyInfo* last_event = &coalesced_gesture_events_.back();
    274   if (last_event->CanCoalesceWith(gesture_event)) {
    275     last_event->CoalesceWith(gesture_event);
    276     return;
    277   }
    278 
    279   if (!ShouldTryMerging(gesture_event, *last_event)) {
    280     coalesced_gesture_events_.push_back(gesture_event);
    281     return;
    282   }
    283 
    284   GestureEventWithLatencyInfo scroll_event;
    285   GestureEventWithLatencyInfo pinch_event;
    286   scroll_event.event.modifiers |= gesture_event.event.modifiers;
    287   scroll_event.event.sourceDevice = gesture_event.event.sourceDevice;
    288   scroll_event.event.timeStampSeconds = gesture_event.event.timeStampSeconds;
    289   // Keep the oldest LatencyInfo.
    290   DCHECK_LE(last_event->latency.trace_id, gesture_event.latency.trace_id);
    291   scroll_event.latency = last_event->latency;
    292   pinch_event = scroll_event;
    293   scroll_event.event.type = WebInputEvent::GestureScrollUpdate;
    294   pinch_event.event.type = WebInputEvent::GesturePinchUpdate;
    295   pinch_event.event.x = gesture_event.event.type ==
    296       WebInputEvent::GesturePinchUpdate ?
    297           gesture_event.event.x : last_event->event.x;
    298   pinch_event.event.y = gesture_event.event.type ==
    299       WebInputEvent::GesturePinchUpdate ?
    300           gesture_event.event.y : last_event->event.y;
    301 
    302   gfx::Transform combined_scroll_pinch = GetTransformForEvent(*last_event);
    303   // Only include the second-to-last event in the coalesced pair if it exists
    304   // and can be combined with the new event.
    305   if (unsent_events_count > 1) {
    306     const GestureEventWithLatencyInfo& second_last_event =
    307         coalesced_gesture_events_[coalesced_gesture_events_.size() - 2];
    308     if (ShouldTryMerging(gesture_event, second_last_event)) {
    309       // Keep the oldest LatencyInfo.
    310       DCHECK_LE(second_last_event.latency.trace_id,
    311                 scroll_event.latency.trace_id);
    312       scroll_event.latency = second_last_event.latency;
    313       pinch_event.latency = second_last_event.latency;
    314       combined_scroll_pinch.PreconcatTransform(
    315           GetTransformForEvent(second_last_event));
    316       coalesced_gesture_events_.pop_back();
    317     }
    318   }
    319   combined_scroll_pinch.ConcatTransform(GetTransformForEvent(gesture_event));
    320   coalesced_gesture_events_.pop_back();
    321 
    322   float combined_scale =
    323       SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 0));
    324   float combined_scroll_pinch_x =
    325       SkMScalarToFloat(combined_scroll_pinch.matrix().get(0, 3));
    326   float combined_scroll_pinch_y =
    327       SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3));
    328   scroll_event.event.data.scrollUpdate.deltaX =
    329       (combined_scroll_pinch_x + pinch_event.event.x) / combined_scale -
    330       pinch_event.event.x;
    331   scroll_event.event.data.scrollUpdate.deltaY =
    332       (combined_scroll_pinch_y + pinch_event.event.y) / combined_scale -
    333       pinch_event.event.y;
    334   coalesced_gesture_events_.push_back(scroll_event);
    335   pinch_event.event.data.pinchUpdate.scale = combined_scale;
    336   coalesced_gesture_events_.push_back(pinch_event);
    337 }
    338 
    339 bool GestureEventQueue::ShouldTryMerging(
    340     const GestureEventWithLatencyInfo& new_event,
    341     const GestureEventWithLatencyInfo& event_in_queue) const {
    342   DLOG_IF(WARNING,
    343           new_event.event.timeStampSeconds <
    344           event_in_queue.event.timeStampSeconds)
    345           << "Event time not monotonic?\n";
    346   return (event_in_queue.event.type == WebInputEvent::GestureScrollUpdate ||
    347       event_in_queue.event.type == WebInputEvent::GesturePinchUpdate) &&
    348       event_in_queue.event.modifiers == new_event.event.modifiers &&
    349       event_in_queue.event.sourceDevice == new_event.event.sourceDevice;
    350 }
    351 
    352 gfx::Transform GestureEventQueue::GetTransformForEvent(
    353     const GestureEventWithLatencyInfo& gesture_event) const {
    354   gfx::Transform gesture_transform;
    355   if (gesture_event.event.type == WebInputEvent::GestureScrollUpdate) {
    356     gesture_transform.Translate(gesture_event.event.data.scrollUpdate.deltaX,
    357                                 gesture_event.event.data.scrollUpdate.deltaY);
    358   } else if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate) {
    359     float scale = gesture_event.event.data.pinchUpdate.scale;
    360     gesture_transform.Translate(-gesture_event.event.x, -gesture_event.event.y);
    361     gesture_transform.Scale(scale,scale);
    362     gesture_transform.Translate(gesture_event.event.x, gesture_event.event.y);
    363   }
    364   return gesture_transform;
    365 }
    366 
    367 size_t GestureEventQueue::EventsInFlightCount() const {
    368   if (coalesced_gesture_events_.empty())
    369     return 0;
    370 
    371   if (!ignore_next_ack_)
    372     return 1;
    373 
    374   DCHECK_GT(coalesced_gesture_events_.size(), 1U);
    375   return 2;
    376 }
    377 
    378 }  // namespace content
    379