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/touch_event_queue.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/stl_util.h"
     10 #include "content/browser/renderer_host/input/timeout_monitor.h"
     11 #include "content/common/input/web_touch_event_traits.h"
     12 #include "ui/gfx/geometry/point_f.h"
     13 
     14 using blink::WebInputEvent;
     15 using blink::WebTouchEvent;
     16 using blink::WebTouchPoint;
     17 using ui::LatencyInfo;
     18 
     19 namespace content {
     20 namespace {
     21 
     22 // Time interval at which touchmove events will be forwarded to the client while
     23 // scrolling is active and possible.
     24 const double kAsyncTouchMoveIntervalSec = .2;
     25 
     26 // A slop region just larger than that used by many web applications. When
     27 // touchmove's are being sent asynchronously, movement outside this region will
     28 // trigger an immediate async touchmove to cancel potential tap-related logic.
     29 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.;
     30 
     31 // Using a small epsilon when comparing slop distances allows pixel perfect
     32 // slop determination when using fractional DIP coordinates (assuming the slop
     33 // region and DPI scale are reasonably proportioned).
     34 const float kSlopEpsilon = .05f;
     35 
     36 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
     37     const TouchEventWithLatencyInfo& event_to_cancel) {
     38   TouchEventWithLatencyInfo event = event_to_cancel;
     39   WebTouchEventTraits::ResetTypeAndTouchStates(
     40       WebInputEvent::TouchCancel,
     41       // TODO(rbyers): Shouldn't we use a fresh timestamp?
     42       event.event.timeStampSeconds,
     43       &event.event);
     44   return event;
     45 }
     46 
     47 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
     48   return (event.type == WebInputEvent::TouchStart ||
     49           event.type == WebInputEvent::TouchMove) &&
     50          !WebInputEventTraits::IgnoresAckDisposition(event);
     51 }
     52 
     53 bool OutsideApplicationSlopRegion(const WebTouchEvent& event,
     54                                   const gfx::PointF& anchor) {
     55   return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() >
     56          kApplicationSlopRegionLengthDipsSqared;
     57 }
     58 
     59 }  // namespace
     60 
     61 
     62 // Cancels a touch sequence if a touchstart or touchmove ack response is
     63 // sufficiently delayed.
     64 class TouchEventQueue::TouchTimeoutHandler {
     65  public:
     66   TouchTimeoutHandler(TouchEventQueue* touch_queue,
     67                       base::TimeDelta timeout_delay)
     68       : touch_queue_(touch_queue),
     69         timeout_delay_(timeout_delay),
     70         pending_ack_state_(PENDING_ACK_NONE),
     71         timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
     72                                     base::Unretained(this))),
     73         enabled_(true),
     74         enabled_for_current_sequence_(false) {
     75     DCHECK(timeout_delay != base::TimeDelta());
     76   }
     77 
     78   ~TouchTimeoutHandler() {}
     79 
     80   void StartIfNecessary(const TouchEventWithLatencyInfo& event) {
     81     DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
     82     if (!enabled_)
     83       return;
     84 
     85     if (!ShouldTouchTriggerTimeout(event.event))
     86       return;
     87 
     88     if (WebTouchEventTraits::IsTouchSequenceStart(event.event))
     89       enabled_for_current_sequence_ = true;
     90 
     91     if (!enabled_for_current_sequence_)
     92       return;
     93 
     94     timeout_event_ = event;
     95     timeout_monitor_.Restart(timeout_delay_);
     96   }
     97 
     98   bool ConfirmTouchEvent(InputEventAckState ack_result) {
     99     switch (pending_ack_state_) {
    100       case PENDING_ACK_NONE:
    101         if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
    102           enabled_for_current_sequence_ = false;
    103         timeout_monitor_.Stop();
    104         return false;
    105       case PENDING_ACK_ORIGINAL_EVENT:
    106         if (AckedTimeoutEventRequiresCancel(ack_result)) {
    107           SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
    108           TouchEventWithLatencyInfo cancel_event =
    109               ObtainCancelEventForTouchEvent(timeout_event_);
    110           touch_queue_->SendTouchEventImmediately(cancel_event);
    111         } else {
    112           SetPendingAckState(PENDING_ACK_NONE);
    113           touch_queue_->UpdateTouchConsumerStates(timeout_event_.event,
    114                                                   ack_result);
    115         }
    116         return true;
    117       case PENDING_ACK_CANCEL_EVENT:
    118         SetPendingAckState(PENDING_ACK_NONE);
    119         return true;
    120     }
    121     return false;
    122   }
    123 
    124   bool FilterEvent(const WebTouchEvent& event) {
    125     return HasTimeoutEvent();
    126   }
    127 
    128   void SetEnabled(bool enabled) {
    129     if (enabled_ == enabled)
    130       return;
    131 
    132     enabled_ = enabled;
    133 
    134     if (enabled_)
    135       return;
    136 
    137     enabled_for_current_sequence_ = false;
    138     // Only reset the |timeout_handler_| if the timer is running and has not
    139     // yet timed out. This ensures that an already timed out sequence is
    140     // properly flushed by the handler.
    141     if (IsTimeoutTimerRunning()) {
    142       pending_ack_state_ = PENDING_ACK_NONE;
    143       timeout_monitor_.Stop();
    144     }
    145   }
    146 
    147   bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); }
    148 
    149   bool enabled() const { return enabled_; }
    150 
    151  private:
    152   enum PendingAckState {
    153     PENDING_ACK_NONE,
    154     PENDING_ACK_ORIGINAL_EVENT,
    155     PENDING_ACK_CANCEL_EVENT,
    156   };
    157 
    158   void OnTimeOut() {
    159     SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT);
    160     touch_queue_->FlushQueue();
    161   }
    162 
    163   // Skip a cancel event if the timed-out event had no consumer and was the
    164   // initial event in the gesture.
    165   bool AckedTimeoutEventRequiresCancel(InputEventAckState ack_result) const {
    166     DCHECK(HasTimeoutEvent());
    167     if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
    168       return true;
    169     return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_.event);
    170   }
    171 
    172   void SetPendingAckState(PendingAckState new_pending_ack_state) {
    173     DCHECK_NE(pending_ack_state_, new_pending_ack_state);
    174     switch (new_pending_ack_state) {
    175       case PENDING_ACK_ORIGINAL_EVENT:
    176         DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
    177         TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventTimeout", this);
    178         break;
    179       case PENDING_ACK_CANCEL_EVENT:
    180         DCHECK_EQ(pending_ack_state_, PENDING_ACK_ORIGINAL_EVENT);
    181         DCHECK(!timeout_monitor_.IsRunning());
    182         DCHECK(touch_queue_->empty());
    183         TRACE_EVENT_ASYNC_STEP_INTO0(
    184             "input", "TouchEventTimeout", this, "CancelEvent");
    185         break;
    186       case PENDING_ACK_NONE:
    187         DCHECK(!timeout_monitor_.IsRunning());
    188         DCHECK(touch_queue_->empty());
    189         TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
    190         break;
    191     }
    192     pending_ack_state_ = new_pending_ack_state;
    193   }
    194 
    195   bool HasTimeoutEvent() const {
    196     return pending_ack_state_ != PENDING_ACK_NONE;
    197   }
    198 
    199 
    200   TouchEventQueue* touch_queue_;
    201 
    202   // How long to wait on a touch ack before cancelling the touch sequence.
    203   base::TimeDelta timeout_delay_;
    204 
    205   // The touch event source for which we expect the next ack.
    206   PendingAckState pending_ack_state_;
    207 
    208   // The event for which the ack timeout is triggered.
    209   TouchEventWithLatencyInfo timeout_event_;
    210 
    211   // Provides timeout-based callback behavior.
    212   TimeoutMonitor timeout_monitor_;
    213 
    214   bool enabled_;
    215   bool enabled_for_current_sequence_;
    216 };
    217 
    218 // Provides touchmove slop suppression for a single touch that remains within
    219 // a given slop region, unless the touchstart is preventDefault'ed.
    220 // TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it
    221 // has exceeded the slop region, removing duplicated slop determination logic.
    222 class TouchEventQueue::TouchMoveSlopSuppressor {
    223  public:
    224   TouchMoveSlopSuppressor(double slop_suppression_length_dips)
    225       : slop_suppression_length_dips_squared_(0),
    226         suppressing_touchmoves_(false) {
    227     if (slop_suppression_length_dips) {
    228       slop_suppression_length_dips += kSlopEpsilon;
    229       slop_suppression_length_dips_squared_ =
    230           slop_suppression_length_dips * slop_suppression_length_dips;
    231     }
    232   }
    233 
    234   bool FilterEvent(const WebTouchEvent& event) {
    235     if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
    236       touch_sequence_start_position_ =
    237           gfx::PointF(event.touches[0].position);
    238       suppressing_touchmoves_ = slop_suppression_length_dips_squared_ != 0;
    239     }
    240 
    241     if (event.type == WebInputEvent::TouchEnd ||
    242         event.type == WebInputEvent::TouchCancel)
    243       suppressing_touchmoves_ = false;
    244 
    245     if (event.type != WebInputEvent::TouchMove)
    246       return false;
    247 
    248     if (suppressing_touchmoves_) {
    249       // Movement with a secondary pointer should terminate suppression.
    250       if (event.touchesLength > 1) {
    251         suppressing_touchmoves_ = false;
    252       } else if (event.touchesLength == 1) {
    253         // Movement outside of the slop region should terminate suppression.
    254         gfx::PointF position(event.touches[0].position);
    255         if ((position - touch_sequence_start_position_).LengthSquared() >
    256             slop_suppression_length_dips_squared_)
    257           suppressing_touchmoves_ = false;
    258       }
    259     }
    260     return suppressing_touchmoves_;
    261   }
    262 
    263   void ConfirmTouchEvent(InputEventAckState ack_result) {
    264     if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
    265       suppressing_touchmoves_ = false;
    266   }
    267 
    268   bool suppressing_touchmoves() const { return suppressing_touchmoves_; }
    269 
    270  private:
    271   double slop_suppression_length_dips_squared_;
    272   gfx::PointF touch_sequence_start_position_;
    273   bool suppressing_touchmoves_;
    274 
    275   DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
    276 };
    277 
    278 // This class represents a single coalesced touch event. However, it also keeps
    279 // track of all the original touch-events that were coalesced into a single
    280 // event. The coalesced event is forwarded to the renderer, while the original
    281 // touch-events are sent to the Client (on ACK for the coalesced event) so that
    282 // the Client receives the event with their original timestamp.
    283 class CoalescedWebTouchEvent {
    284  public:
    285   CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
    286                          bool suppress_client_ack)
    287       : coalesced_event_(event), suppress_client_ack_(suppress_client_ack) {
    288     TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
    289   }
    290 
    291   ~CoalescedWebTouchEvent() {
    292     TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
    293   }
    294 
    295   // Coalesces the event with the existing event if possible. Returns whether
    296   // the event was coalesced.
    297   bool CoalesceEventIfPossible(
    298       const TouchEventWithLatencyInfo& event_with_latency) {
    299     if (suppress_client_ack_)
    300       return false;
    301 
    302     if (!coalesced_event_.CanCoalesceWith(event_with_latency))
    303       return false;
    304 
    305     // Addition of the first event to |uncoaleseced_events_to_ack_| is deferred
    306     // until the first coalesced event, optimizing the (common) case where the
    307     // event is not coalesced at all.
    308     if (uncoaleseced_events_to_ack_.empty())
    309       uncoaleseced_events_to_ack_.push_back(coalesced_event_);
    310 
    311     TRACE_EVENT_INSTANT0(
    312         "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
    313     coalesced_event_.CoalesceWith(event_with_latency);
    314     uncoaleseced_events_to_ack_.push_back(event_with_latency);
    315     DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
    316     return true;
    317   }
    318 
    319   void DispatchAckToClient(InputEventAckState ack_result,
    320                            const ui::LatencyInfo* optional_latency_info,
    321                            TouchEventQueueClient* client) {
    322     DCHECK(client);
    323     if (suppress_client_ack_)
    324       return;
    325 
    326     if (uncoaleseced_events_to_ack_.empty()) {
    327       if (optional_latency_info)
    328         coalesced_event_.latency.AddNewLatencyFrom(*optional_latency_info);
    329       client->OnTouchEventAck(coalesced_event_, ack_result);
    330       return;
    331     }
    332 
    333     DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
    334     for (WebTouchEventWithLatencyList::iterator
    335              iter = uncoaleseced_events_to_ack_.begin(),
    336              end = uncoaleseced_events_to_ack_.end();
    337          iter != end;
    338          ++iter) {
    339       if (optional_latency_info)
    340         iter->latency.AddNewLatencyFrom(*optional_latency_info);
    341       client->OnTouchEventAck(*iter, ack_result);
    342     }
    343   }
    344 
    345   const TouchEventWithLatencyInfo& coalesced_event() const {
    346     return coalesced_event_;
    347   }
    348 
    349  private:
    350   // This is the event that is forwarded to the renderer.
    351   TouchEventWithLatencyInfo coalesced_event_;
    352 
    353   // This is the list of the original events that were coalesced, each requiring
    354   // future ack dispatch to the client.
    355   // Note that this will be empty if no coalescing has occurred.
    356   typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
    357   WebTouchEventWithLatencyList uncoaleseced_events_to_ack_;
    358 
    359   bool suppress_client_ack_;
    360 
    361   DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
    362 };
    363 
    364 TouchEventQueue::Config::Config()
    365     : touchmove_slop_suppression_length_dips(0),
    366       touch_scrolling_mode(TOUCH_SCROLLING_MODE_DEFAULT),
    367       touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
    368       touch_ack_timeout_supported(false) {
    369 }
    370 
    371 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
    372                                  const Config& config)
    373     : client_(client),
    374       dispatching_touch_ack_(NULL),
    375       dispatching_touch_(false),
    376       has_handlers_(true),
    377       drop_remaining_touches_in_sequence_(false),
    378       touchmove_slop_suppressor_(new TouchMoveSlopSuppressor(
    379           config.touchmove_slop_suppression_length_dips)),
    380       send_touch_events_async_(false),
    381       needs_async_touchmove_for_outer_slop_region_(false),
    382       last_sent_touch_timestamp_sec_(0),
    383       touch_scrolling_mode_(config.touch_scrolling_mode) {
    384   DCHECK(client);
    385   if (config.touch_ack_timeout_supported) {
    386     timeout_handler_.reset(
    387         new TouchTimeoutHandler(this, config.touch_ack_timeout_delay));
    388   }
    389 }
    390 
    391 TouchEventQueue::~TouchEventQueue() {
    392   if (!touch_queue_.empty())
    393     STLDeleteElements(&touch_queue_);
    394 }
    395 
    396 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
    397   TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
    398 
    399   // If the queueing of |event| was triggered by an ack dispatch, defer
    400   // processing the event until the dispatch has finished.
    401   if (touch_queue_.empty() && !dispatching_touch_ack_) {
    402     // Optimization of the case without touch handlers.  Removing this path
    403     // yields identical results, but this avoids unnecessary allocations.
    404     PreFilterResult filter_result = FilterBeforeForwarding(event.event);
    405     if (filter_result != FORWARD_TO_RENDERER) {
    406       client_->OnTouchEventAck(event,
    407                                filter_result == ACK_WITH_NO_CONSUMER_EXISTS
    408                                    ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
    409                                    : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
    410       return;
    411     }
    412 
    413     // There is no touch event in the queue. Forward it to the renderer
    414     // immediately.
    415     touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
    416     ForwardNextEventToRenderer();
    417     return;
    418   }
    419 
    420   // If the last queued touch-event was a touch-move, and the current event is
    421   // also a touch-move, then the events can be coalesced into a single event.
    422   if (touch_queue_.size() > 1) {
    423     CoalescedWebTouchEvent* last_event = touch_queue_.back();
    424     if (last_event->CoalesceEventIfPossible(event))
    425       return;
    426   }
    427   touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
    428 }
    429 
    430 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
    431                                       const LatencyInfo& latency_info) {
    432   TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
    433 
    434   DCHECK(!dispatching_touch_ack_);
    435   dispatching_touch_ = false;
    436 
    437   if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
    438     return;
    439 
    440   touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result);
    441 
    442   if (touch_queue_.empty())
    443     return;
    444 
    445   PopTouchEventToClient(ack_result, latency_info);
    446   TryForwardNextEventToRenderer();
    447 }
    448 
    449 void TouchEventQueue::TryForwardNextEventToRenderer() {
    450   DCHECK(!dispatching_touch_ack_);
    451   // If there are queued touch events, then try to forward them to the renderer
    452   // immediately, or ACK the events back to the client if appropriate.
    453   while (!touch_queue_.empty()) {
    454     PreFilterResult filter_result =
    455         FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event);
    456     switch (filter_result) {
    457       case ACK_WITH_NO_CONSUMER_EXISTS:
    458         PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
    459         break;
    460       case ACK_WITH_NOT_CONSUMED:
    461         PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
    462         break;
    463       case FORWARD_TO_RENDERER:
    464         ForwardNextEventToRenderer();
    465         return;
    466     }
    467   }
    468 }
    469 
    470 void TouchEventQueue::ForwardNextEventToRenderer() {
    471   TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
    472 
    473   DCHECK(!empty());
    474   DCHECK(!dispatching_touch_);
    475   TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
    476 
    477   if (send_touch_events_async_ &&
    478       touch.event.type == WebInputEvent::TouchMove) {
    479     // Throttling touchmove's in a continuous touchmove stream while scrolling
    480     // reduces the risk of jank. However, it's still important that the web
    481     // application be sent touches at key points in the gesture stream,
    482     // e.g., when the application slop region is exceeded or touchmove
    483     // coalescing fails because of different modifiers.
    484     const bool send_touchmove_now =
    485         size() > 1 ||
    486         (touch.event.timeStampSeconds >=
    487          last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) ||
    488         (needs_async_touchmove_for_outer_slop_region_ &&
    489          OutsideApplicationSlopRegion(touch.event,
    490                                       touch_sequence_start_position_)) ||
    491         (pending_async_touchmove_ &&
    492          !pending_async_touchmove_->CanCoalesceWith(touch));
    493 
    494     if (!send_touchmove_now) {
    495       if (!pending_async_touchmove_) {
    496         pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch));
    497       } else {
    498         DCHECK(pending_async_touchmove_->CanCoalesceWith(touch));
    499         pending_async_touchmove_->CoalesceWith(touch);
    500       }
    501       DCHECK_EQ(1U, size());
    502       PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
    503       // It's possible (though unlikely) that ack'ing the current touch will
    504       // trigger the queueing of another touch event (e.g., a touchcancel). As
    505       // forwarding of the queued event will be deferred while the ack is being
    506       // dispatched (see |OnTouchEvent()|), try forwarding it now.
    507       TryForwardNextEventToRenderer();
    508       return;
    509     }
    510   }
    511 
    512   last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds;
    513 
    514   // Flush any pending async touch move. If it can be combined with the current
    515   // (touchmove) event, great, otherwise send it immediately but separately. Its
    516   // ack will trigger forwarding of the original |touch| event.
    517   if (pending_async_touchmove_) {
    518     if (pending_async_touchmove_->CanCoalesceWith(touch)) {
    519       pending_async_touchmove_->CoalesceWith(touch);
    520       pending_async_touchmove_->event.cancelable = !send_touch_events_async_;
    521       touch = *pending_async_touchmove_.Pass();
    522     } else {
    523       scoped_ptr<TouchEventWithLatencyInfo> async_move =
    524           pending_async_touchmove_.Pass();
    525       async_move->event.cancelable = false;
    526       touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true));
    527       SendTouchEventImmediately(*async_move);
    528       return;
    529     }
    530   }
    531 
    532   // Note: Marking touchstart events as not-cancelable prevents them from
    533   // blocking subsequent gestures, but it may not be the best long term solution
    534   // for tracking touch point dispatch.
    535   if (send_touch_events_async_)
    536     touch.event.cancelable = false;
    537 
    538   // A synchronous ack will reset |dispatching_touch_|, in which case
    539   // the touch timeout should not be started.
    540   base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
    541   SendTouchEventImmediately(touch);
    542   if (dispatching_touch_ && timeout_handler_)
    543     timeout_handler_->StartIfNecessary(touch);
    544 }
    545 
    546 void TouchEventQueue::OnGestureScrollEvent(
    547     const GestureEventWithLatencyInfo& gesture_event) {
    548   if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) {
    549     if (!touch_consumer_states_.is_empty() &&
    550         !drop_remaining_touches_in_sequence_) {
    551       DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
    552           <<  "A touch handler should be offered a touchmove before scrolling.";
    553     }
    554     if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE &&
    555         !drop_remaining_touches_in_sequence_ &&
    556         touch_consumer_states_.is_empty()) {
    557       // If no touch points have a consumer, prevent all subsequent touch events
    558       // received during the scroll from reaching the renderer. This ensures
    559       // that the first touchstart the renderer sees in any given sequence can
    560       // always be preventDefault'ed (cancelable == true).
    561       // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable.
    562       drop_remaining_touches_in_sequence_ = true;
    563     }
    564 
    565     if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) {
    566       needs_async_touchmove_for_outer_slop_region_ = true;
    567       pending_async_touchmove_.reset();
    568     }
    569 
    570     return;
    571   }
    572 
    573   if (gesture_event.event.type != blink::WebInputEvent::GestureScrollUpdate)
    574     return;
    575 
    576   if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE)
    577     send_touch_events_async_ = true;
    578 
    579   if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL)
    580     return;
    581 
    582   // We assume that scroll events are generated synchronously from
    583   // dispatching a touch event ack. This allows us to generate a synthetic
    584   // cancel event that has the same touch ids as the touch event that
    585   // is being acked. Otherwise, we don't perform the touch-cancel optimization.
    586   if (!dispatching_touch_ack_)
    587     return;
    588 
    589   if (drop_remaining_touches_in_sequence_)
    590     return;
    591 
    592   drop_remaining_touches_in_sequence_ = true;
    593 
    594   // Fake a TouchCancel to cancel the touch points of the touch event
    595   // that is currently being acked.
    596   // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
    597   // are in the scope of PopTouchEventToClient() and that no touch event
    598   // in the queue is waiting for ack from renderer. So we can just insert
    599   // the touch cancel at the beginning of the queue.
    600   touch_queue_.push_front(new CoalescedWebTouchEvent(
    601       ObtainCancelEventForTouchEvent(
    602           dispatching_touch_ack_->coalesced_event()), true));
    603 }
    604 
    605 void TouchEventQueue::OnGestureEventAck(
    606     const GestureEventWithLatencyInfo& event,
    607     InputEventAckState ack_result) {
    608   if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE)
    609     return;
    610 
    611   if (event.event.type != blink::WebInputEvent::GestureScrollUpdate)
    612     return;
    613 
    614   // Throttle sending touchmove events as long as the scroll events are handled.
    615   // Note that there's no guarantee that this ACK is for the most recent
    616   // gesture event (or even part of the current sequence).  Worst case, the
    617   // delay in updating the absorption state will result in minor UI glitches.
    618   // A valid |pending_async_touchmove_| will be flushed when the next event is
    619   // forwarded.
    620   send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
    621   if (!send_touch_events_async_)
    622     needs_async_touchmove_for_outer_slop_region_ = false;
    623 }
    624 
    625 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
    626   DCHECK(!dispatching_touch_ack_);
    627   DCHECK(!dispatching_touch_);
    628   has_handlers_ = has_handlers;
    629 }
    630 
    631 bool TouchEventQueue::IsPendingAckTouchStart() const {
    632   DCHECK(!dispatching_touch_ack_);
    633   if (touch_queue_.empty())
    634     return false;
    635 
    636   const blink::WebTouchEvent& event =
    637       touch_queue_.front()->coalesced_event().event;
    638   return (event.type == WebInputEvent::TouchStart);
    639 }
    640 
    641 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
    642   if (timeout_handler_)
    643     timeout_handler_->SetEnabled(enabled);
    644 }
    645 
    646 bool TouchEventQueue::IsAckTimeoutEnabled() const {
    647   return timeout_handler_ && timeout_handler_->enabled();
    648 }
    649 
    650 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
    651   return pending_async_touchmove_;
    652 }
    653 
    654 bool TouchEventQueue::IsTimeoutRunningForTesting() const {
    655   return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
    656 }
    657 
    658 const TouchEventWithLatencyInfo&
    659 TouchEventQueue::GetLatestEventForTesting() const {
    660   return touch_queue_.back()->coalesced_event();
    661 }
    662 
    663 void TouchEventQueue::FlushQueue() {
    664   DCHECK(!dispatching_touch_ack_);
    665   DCHECK(!dispatching_touch_);
    666   pending_async_touchmove_.reset();
    667   drop_remaining_touches_in_sequence_ = true;
    668   while (!touch_queue_.empty())
    669     PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
    670 }
    671 
    672 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
    673   AckTouchEventToClient(ack_result, PopTouchEvent(), NULL);
    674 }
    675 
    676 void TouchEventQueue::PopTouchEventToClient(
    677     InputEventAckState ack_result,
    678     const LatencyInfo& renderer_latency_info) {
    679   AckTouchEventToClient(ack_result, PopTouchEvent(), &renderer_latency_info);
    680 }
    681 
    682 void TouchEventQueue::AckTouchEventToClient(
    683     InputEventAckState ack_result,
    684     scoped_ptr<CoalescedWebTouchEvent> acked_event,
    685     const ui::LatencyInfo* optional_latency_info) {
    686   DCHECK(acked_event);
    687   DCHECK(!dispatching_touch_ack_);
    688   UpdateTouchConsumerStates(acked_event->coalesced_event().event, ack_result);
    689 
    690   // Note that acking the touch-event may result in multiple gestures being sent
    691   // to the renderer, or touch-events being queued.
    692   base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack(
    693       &dispatching_touch_ack_, acked_event.get());
    694   acked_event->DispatchAckToClient(ack_result, optional_latency_info, client_);
    695 }
    696 
    697 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() {
    698   DCHECK(!touch_queue_.empty());
    699   scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front());
    700   touch_queue_.pop_front();
    701   return event.Pass();
    702 }
    703 
    704 void TouchEventQueue::SendTouchEventImmediately(
    705     const TouchEventWithLatencyInfo& touch) {
    706   if (needs_async_touchmove_for_outer_slop_region_) {
    707     // Any event other than a touchmove (e.g., touchcancel or secondary
    708     // touchstart) after a scroll has started will interrupt the need to send a
    709     // an outer slop-region exceeding touchmove.
    710     if (touch.event.type != WebInputEvent::TouchMove ||
    711         OutsideApplicationSlopRegion(touch.event,
    712                                      touch_sequence_start_position_))
    713       needs_async_touchmove_for_outer_slop_region_ = false;
    714   }
    715 
    716   client_->SendTouchEventImmediately(touch);
    717 }
    718 
    719 TouchEventQueue::PreFilterResult
    720 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
    721   if (timeout_handler_ && timeout_handler_->FilterEvent(event))
    722     return ACK_WITH_NO_CONSUMER_EXISTS;
    723 
    724   if (touchmove_slop_suppressor_->FilterEvent(event))
    725     return ACK_WITH_NOT_CONSUMED;
    726 
    727   if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
    728     touch_consumer_states_.clear();
    729     send_touch_events_async_ = false;
    730     pending_async_touchmove_.reset();
    731     touch_sequence_start_position_ = gfx::PointF(event.touches[0].position);
    732     drop_remaining_touches_in_sequence_ = false;
    733     if (!has_handlers_) {
    734       drop_remaining_touches_in_sequence_ = true;
    735       return ACK_WITH_NO_CONSUMER_EXISTS;
    736     }
    737   }
    738 
    739   if (drop_remaining_touches_in_sequence_ &&
    740       event.type != WebInputEvent::TouchCancel) {
    741     return ACK_WITH_NO_CONSUMER_EXISTS;
    742   }
    743 
    744   if (event.type == WebInputEvent::TouchStart)
    745     return has_handlers_ ? FORWARD_TO_RENDERER : ACK_WITH_NO_CONSUMER_EXISTS;
    746 
    747   for (unsigned int i = 0; i < event.touchesLength; ++i) {
    748     const WebTouchPoint& point = event.touches[i];
    749     // If a point has been stationary, then don't take it into account.
    750     if (point.state == WebTouchPoint::StateStationary)
    751       continue;
    752 
    753     if (touch_consumer_states_.has_bit(point.id))
    754       return FORWARD_TO_RENDERER;
    755   }
    756 
    757   return ACK_WITH_NO_CONSUMER_EXISTS;
    758 }
    759 
    760 void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event,
    761                                                 InputEventAckState ack_result) {
    762   // Update the ACK status for each touch point in the ACKed event.
    763   if (event.type == WebInputEvent::TouchEnd ||
    764       event.type == WebInputEvent::TouchCancel) {
    765     // The points have been released. Erase the ACK states.
    766     for (unsigned i = 0; i < event.touchesLength; ++i) {
    767       const WebTouchPoint& point = event.touches[i];
    768       if (point.state == WebTouchPoint::StateReleased ||
    769           point.state == WebTouchPoint::StateCancelled)
    770         touch_consumer_states_.clear_bit(point.id);
    771     }
    772   } else if (event.type == WebInputEvent::TouchStart) {
    773     for (unsigned i = 0; i < event.touchesLength; ++i) {
    774       const WebTouchPoint& point = event.touches[i];
    775       if (point.state == WebTouchPoint::StatePressed) {
    776         if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
    777           touch_consumer_states_.mark_bit(point.id);
    778         else
    779           touch_consumer_states_.clear_bit(point.id);
    780       }
    781     }
    782   }
    783 }
    784 
    785 }  // namespace content
    786