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