Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / input / touch_event_queue.cc
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/command_line.h"
9 #include "base/debug/trace_event.h"
10 #include "base/stl_util.h"
11 #include "content/browser/renderer_host/input/timeout_monitor.h"
12 #include "content/browser/renderer_host/input/web_touch_event_traits.h"
13 #include "content/public/common/content_switches.h"
14 #include "ui/gfx/geometry/point_f.h"
15
16 using blink::WebInputEvent;
17 using blink::WebTouchEvent;
18 using blink::WebTouchPoint;
19 using ui::LatencyInfo;
20
21 namespace content {
22 namespace {
23
24 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
25
26 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
27     const TouchEventWithLatencyInfo& event_to_cancel) {
28   TouchEventWithLatencyInfo event = event_to_cancel;
29   event.event.type = WebInputEvent::TouchCancel;
30   for (size_t i = 0; i < event.event.touchesLength; i++)
31     event.event.touches[i].state = WebTouchPoint::StateCancelled;
32   return event;
33 }
34
35 bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) {
36   return type == WebInputEvent::TouchStart ||
37          type == WebInputEvent::TouchMove;
38 }
39
40 }  // namespace
41
42
43 // Cancels a touch sequence if a touchstart or touchmove ack response is
44 // sufficiently delayed.
45 class TouchEventQueue::TouchTimeoutHandler {
46  public:
47   TouchTimeoutHandler(TouchEventQueue* touch_queue,
48                       base::TimeDelta timeout_delay)
49       : touch_queue_(touch_queue),
50         timeout_delay_(timeout_delay),
51         pending_ack_state_(PENDING_ACK_NONE),
52         timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
53                                     base::Unretained(this))) {}
54
55   ~TouchTimeoutHandler() {}
56
57   void Start(const TouchEventWithLatencyInfo& event) {
58     DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
59     DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type));
60     timeout_event_ = event;
61     timeout_monitor_.Restart(timeout_delay_);
62   }
63
64   bool ConfirmTouchEvent(InputEventAckState ack_result) {
65     switch (pending_ack_state_) {
66       case PENDING_ACK_NONE:
67         timeout_monitor_.Stop();
68         return false;
69       case PENDING_ACK_ORIGINAL_EVENT:
70         if (AckedTimeoutEventRequiresCancel(ack_result)) {
71           SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
72           TouchEventWithLatencyInfo cancel_event =
73               ObtainCancelEventForTouchEvent(timeout_event_);
74           touch_queue_->client_->SendTouchEventImmediately(cancel_event);
75         } else {
76           SetPendingAckState(PENDING_ACK_NONE);
77           touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result);
78         }
79         return true;
80       case PENDING_ACK_CANCEL_EVENT:
81         SetPendingAckState(PENDING_ACK_NONE);
82         return true;
83     }
84     return false;
85   }
86
87   bool FilterEvent(const WebTouchEvent& event) {
88     return HasTimeoutEvent();
89   }
90
91   bool IsTimeoutTimerRunning() const {
92     return timeout_monitor_.IsRunning();
93   }
94
95   void Reset() {
96     pending_ack_state_ = PENDING_ACK_NONE;
97     timeout_monitor_.Stop();
98   }
99
100   void set_timeout_delay(base::TimeDelta timeout_delay) {
101     timeout_delay_ = timeout_delay;
102   }
103
104  private:
105   enum PendingAckState {
106     PENDING_ACK_NONE,
107     PENDING_ACK_ORIGINAL_EVENT,
108     PENDING_ACK_CANCEL_EVENT,
109   };
110
111   void OnTimeOut() {
112     SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT);
113     touch_queue_->FlushQueue();
114   }
115
116   // Skip a cancel event if the timed-out event had no consumer and was the
117   // initial event in the gesture.
118   bool AckedTimeoutEventRequiresCancel(InputEventAckState ack_result) const {
119     DCHECK(HasTimeoutEvent());
120     if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
121       return true;
122     return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_.event);
123   }
124
125   void SetPendingAckState(PendingAckState new_pending_ack_state) {
126     DCHECK_NE(pending_ack_state_, new_pending_ack_state);
127     switch (new_pending_ack_state) {
128       case PENDING_ACK_ORIGINAL_EVENT:
129         DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
130         TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventTimeout", this);
131         break;
132       case PENDING_ACK_CANCEL_EVENT:
133         DCHECK_EQ(pending_ack_state_, PENDING_ACK_ORIGINAL_EVENT);
134         DCHECK(!timeout_monitor_.IsRunning());
135         DCHECK(touch_queue_->empty());
136         TRACE_EVENT_ASYNC_STEP_INTO0(
137             "input", "TouchEventTimeout", this, "CancelEvent");
138         break;
139       case PENDING_ACK_NONE:
140         DCHECK(!timeout_monitor_.IsRunning());
141         DCHECK(touch_queue_->empty());
142         TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
143         break;
144     }
145     pending_ack_state_ = new_pending_ack_state;
146   }
147
148   bool HasTimeoutEvent() const {
149     return pending_ack_state_ != PENDING_ACK_NONE;
150   }
151
152
153   TouchEventQueue* touch_queue_;
154
155   // How long to wait on a touch ack before cancelling the touch sequence.
156   base::TimeDelta timeout_delay_;
157
158   // The touch event source for which we expect the next ack.
159   PendingAckState pending_ack_state_;
160
161   // The event for which the ack timeout is triggered.
162   TouchEventWithLatencyInfo timeout_event_;
163
164   // Provides timeout-based callback behavior.
165   TimeoutMonitor timeout_monitor_;
166 };
167
168 // Provides touchmove slop suppression for a single touch that remains within
169 // a given slop region, unless the touchstart is preventDefault'ed.
170 class TouchEventQueue::TouchMoveSlopSuppressor {
171  public:
172   TouchMoveSlopSuppressor(double slop_suppression_length_dips)
173       : slop_suppression_length_dips_squared_(slop_suppression_length_dips *
174                                               slop_suppression_length_dips),
175         suppressing_touch_moves_(false) {}
176
177   bool FilterEvent(const WebTouchEvent& event) {
178     if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
179       touch_sequence_start_position_ =
180           gfx::PointF(event.touches[0].position);
181       suppressing_touch_moves_ = slop_suppression_length_dips_squared_ != 0;
182     }
183
184     if (event.type != WebInputEvent::TouchMove)
185       return false;
186
187     if (suppressing_touch_moves_) {
188       // Movement with a secondary pointer should terminate suppression.
189       if (event.touchesLength > 1) {
190         suppressing_touch_moves_ = false;
191       } else if (event.touchesLength == 1) {
192         // Movement outside of the slop region should terminate suppression.
193         gfx::PointF position(event.touches[0].position);
194         if ((position - touch_sequence_start_position_).LengthSquared() >
195             slop_suppression_length_dips_squared_)
196           suppressing_touch_moves_ = false;
197       }
198     }
199     return suppressing_touch_moves_;
200   }
201
202   void ConfirmTouchEvent(InputEventAckState ack_result) {
203     if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
204       suppressing_touch_moves_ = false;
205   }
206
207  private:
208   double slop_suppression_length_dips_squared_;
209   gfx::PointF touch_sequence_start_position_;
210   bool suppressing_touch_moves_;
211
212   DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
213 };
214
215 // This class represents a single coalesced touch event. However, it also keeps
216 // track of all the original touch-events that were coalesced into a single
217 // event. The coalesced event is forwarded to the renderer, while the original
218 // touch-events are sent to the Client (on ACK for the coalesced event) so that
219 // the Client receives the event with their original timestamp.
220 class CoalescedWebTouchEvent {
221  public:
222   CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
223                          bool ignore_ack)
224       : coalesced_event_(event),
225         ignore_ack_(ignore_ack) {
226     events_.push_back(event);
227     TRACE_EVENT_ASYNC_BEGIN0(
228         "input", "TouchEventQueue::QueueEvent", this);
229   }
230
231   ~CoalescedWebTouchEvent() {
232     TRACE_EVENT_ASYNC_END0(
233         "input", "TouchEventQueue::QueueEvent", this);
234   }
235
236   // Coalesces the event with the existing event if possible. Returns whether
237   // the event was coalesced.
238   bool CoalesceEventIfPossible(
239       const TouchEventWithLatencyInfo& event_with_latency) {
240     if (ignore_ack_)
241       return false;
242
243     if (!coalesced_event_.CanCoalesceWith(event_with_latency))
244       return false;
245
246     TRACE_EVENT_INSTANT0(
247         "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
248     coalesced_event_.CoalesceWith(event_with_latency);
249     events_.push_back(event_with_latency);
250     return true;
251   }
252
253   const TouchEventWithLatencyInfo& coalesced_event() const {
254     return coalesced_event_;
255   }
256
257   WebTouchEventWithLatencyList::iterator begin() {
258     return events_.begin();
259   }
260
261   WebTouchEventWithLatencyList::iterator end() {
262     return events_.end();
263   }
264
265   size_t size() const { return events_.size(); }
266
267   bool ignore_ack() const { return ignore_ack_; }
268
269  private:
270   // This is the event that is forwarded to the renderer.
271   TouchEventWithLatencyInfo coalesced_event_;
272
273   // This is the list of the original events that were coalesced.
274   WebTouchEventWithLatencyList events_;
275
276   // If |ignore_ack_| is true, don't send this touch event to client
277   // when the event is acked.
278   bool ignore_ack_;
279
280   DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
281 };
282
283 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
284                                  TouchScrollingMode mode,
285                                  double touchmove_suppression_length_dips)
286     : client_(client),
287       dispatching_touch_ack_(NULL),
288       dispatching_touch_(false),
289       touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT),
290       ack_timeout_enabled_(false),
291       touchmove_slop_suppressor_(
292           new TouchMoveSlopSuppressor(touchmove_suppression_length_dips)),
293       absorbing_touch_moves_(false),
294       touch_scrolling_mode_(mode) {
295   DCHECK(client);
296 }
297
298 TouchEventQueue::~TouchEventQueue() {
299   if (!touch_queue_.empty())
300     STLDeleteElements(&touch_queue_);
301 }
302
303 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
304   TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
305
306   // If the queueing of |event| was triggered by an ack dispatch, defer
307   // processing the event until the dispatch has finished.
308   if (touch_queue_.empty() && !dispatching_touch_ack_) {
309     // Optimization of the case without touch handlers.  Removing this path
310     // yields identical results, but this avoids unnecessary allocations.
311     if (touch_filtering_state_ == DROP_ALL_TOUCHES ||
312         (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE &&
313          !WebTouchEventTraits::IsTouchSequenceStart(event.event))) {
314       client_->OnTouchEventAck(event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
315       return;
316     }
317
318     // There is no touch event in the queue. Forward it to the renderer
319     // immediately.
320     touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
321     TryForwardNextEventToRenderer();
322     return;
323   }
324
325   // If the last queued touch-event was a touch-move, and the current event is
326   // also a touch-move, then the events can be coalesced into a single event.
327   if (touch_queue_.size() > 1) {
328     CoalescedWebTouchEvent* last_event = touch_queue_.back();
329     if (last_event->CoalesceEventIfPossible(event))
330       return;
331   }
332   touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
333 }
334
335 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
336                                       const LatencyInfo& latency_info) {
337   TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
338
339   DCHECK(!dispatching_touch_ack_);
340   dispatching_touch_ = false;
341
342   if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
343     return;
344
345   touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result);
346
347   if (touch_queue_.empty())
348     return;
349
350   const WebTouchEvent& acked_event =
351       touch_queue_.front()->coalesced_event().event;
352
353   if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED &&
354       touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) {
355     touch_filtering_state_ = FORWARD_ALL_TOUCHES;
356   }
357
358   if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS &&
359       touch_filtering_state_ != DROP_ALL_TOUCHES &&
360       WebTouchEventTraits::IsTouchSequenceStart(acked_event)) {
361     touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
362   }
363
364   UpdateTouchAckStates(acked_event, ack_result);
365   PopTouchEventToClient(ack_result, latency_info);
366   TryForwardNextEventToRenderer();
367 }
368
369 void TouchEventQueue::TryForwardNextEventToRenderer() {
370   DCHECK(!dispatching_touch_ack_);
371   // If there are queued touch events, then try to forward them to the renderer
372   // immediately, or ACK the events back to the client if appropriate.
373   while (!touch_queue_.empty()) {
374     const TouchEventWithLatencyInfo& touch =
375         touch_queue_.front()->coalesced_event();
376     PreFilterResult result = FilterBeforeForwarding(touch.event);
377     switch (result) {
378       case ACK_WITH_NO_CONSUMER_EXISTS:
379         PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
380                               LatencyInfo());
381         break;
382       case ACK_WITH_NOT_CONSUMED:
383         PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
384                               LatencyInfo());
385         break;
386       case FORWARD_TO_RENDERER:
387         ForwardToRenderer(touch);
388         return;
389     }
390   }
391 }
392
393 void TouchEventQueue::ForwardToRenderer(
394     const TouchEventWithLatencyInfo& touch) {
395   TRACE_EVENT0("input", "TouchEventQueue::ForwardToRenderer");
396
397   DCHECK(!dispatching_touch_);
398   DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES);
399
400   if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) {
401     touch_filtering_state_ =
402         ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT
403                              : FORWARD_ALL_TOUCHES;
404     touch_ack_states_.clear();
405     absorbing_touch_moves_ = false;
406   }
407
408   // A synchronous ack will reset |dispatching_touch_|, in which case
409   // the touch timeout should not be started.
410   base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
411   client_->SendTouchEventImmediately(touch);
412   if (dispatching_touch_ &&
413       touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT &&
414       ShouldTouchTypeTriggerTimeout(touch.event.type)) {
415     DCHECK(timeout_handler_);
416     timeout_handler_->Start(touch);
417   }
418 }
419
420 void TouchEventQueue::OnGestureScrollEvent(
421     const GestureEventWithLatencyInfo& gesture_event) {
422   if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin)
423     return;
424
425   if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE)
426     absorbing_touch_moves_ = true;
427
428   if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL)
429     return;
430
431   // We assume that scroll events are generated synchronously from
432   // dispatching a touch event ack. This allows us to generate a synthetic
433   // cancel event that has the same touch ids as the touch event that
434   // is being acked. Otherwise, we don't perform the touch-cancel optimization.
435   if (!dispatching_touch_ack_)
436     return;
437
438   if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE)
439     return;
440
441   touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
442
443   // Fake a TouchCancel to cancel the touch points of the touch event
444   // that is currently being acked.
445   // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
446   // are in the scope of PopTouchEventToClient() and that no touch event
447   // in the queue is waiting for ack from renderer. So we can just insert
448   // the touch cancel at the beginning of the queue.
449   touch_queue_.push_front(new CoalescedWebTouchEvent(
450       ObtainCancelEventForTouchEvent(
451           dispatching_touch_ack_->coalesced_event()), true));
452 }
453
454 void TouchEventQueue::OnGestureEventAck(
455     const GestureEventWithLatencyInfo& event,
456     InputEventAckState ack_result) {
457   if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE)
458     return;
459
460   if (event.event.type != blink::WebInputEvent::GestureScrollUpdate)
461     return;
462
463   // Suspend sending touchmove events as long as the scroll events are handled.
464   // Note that there's no guarantee that this ACK is for the most recent
465   // gesture event (or even part of the current sequence).  Worst case, the
466   // delay in updating the absorption state should only result in minor UI
467   // glitches.
468   absorbing_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
469 }
470
471 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
472   DCHECK(!dispatching_touch_ack_);
473   DCHECK(!dispatching_touch_);
474
475   if (has_handlers) {
476     if (touch_filtering_state_ == DROP_ALL_TOUCHES) {
477       // If no touch handler was previously registered, ensure that we don't
478       // send a partial touch sequence to the renderer.
479       DCHECK(touch_queue_.empty());
480       touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
481     }
482   } else {
483     // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch
484     // state tracking (e.g., if the touch handler was removed mid-sequence).
485     touch_filtering_state_ = DROP_ALL_TOUCHES;
486     if (timeout_handler_)
487       timeout_handler_->Reset();
488     if (!touch_queue_.empty())
489       ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo());
490     // As there is no touch handler, ack'ing the event should flush the queue.
491     DCHECK(touch_queue_.empty());
492   }
493 }
494
495 bool TouchEventQueue::IsPendingAckTouchStart() const {
496   DCHECK(!dispatching_touch_ack_);
497   if (touch_queue_.empty())
498     return false;
499
500   const blink::WebTouchEvent& event =
501       touch_queue_.front()->coalesced_event().event;
502   return (event.type == WebInputEvent::TouchStart);
503 }
504
505 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled,
506                                            base::TimeDelta ack_timeout_delay) {
507   if (!enabled) {
508     ack_timeout_enabled_ = false;
509     if (touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT)
510       touch_filtering_state_ = FORWARD_ALL_TOUCHES;
511     // Only reset the |timeout_handler_| if the timer is running and has not yet
512     // timed out. This ensures that an already timed out sequence is properly
513     // flushed by the handler.
514     if (timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning())
515       timeout_handler_->Reset();
516     return;
517   }
518
519   ack_timeout_enabled_ = true;
520   if (!timeout_handler_)
521     timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay));
522   else
523     timeout_handler_->set_timeout_delay(ack_timeout_delay);
524 }
525
526 bool TouchEventQueue::IsTimeoutRunningForTesting() const {
527   return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
528 }
529
530 const TouchEventWithLatencyInfo&
531 TouchEventQueue::GetLatestEventForTesting() const {
532   return touch_queue_.back()->coalesced_event();
533 }
534
535 void TouchEventQueue::FlushQueue() {
536   DCHECK(!dispatching_touch_ack_);
537   DCHECK(!dispatching_touch_);
538   if (touch_filtering_state_ != DROP_ALL_TOUCHES)
539     touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
540   while (!touch_queue_.empty()) {
541     PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
542                           LatencyInfo());
543   }
544 }
545
546 void TouchEventQueue::PopTouchEventToClient(
547     InputEventAckState ack_result,
548     const LatencyInfo& renderer_latency_info) {
549   DCHECK(!dispatching_touch_ack_);
550   if (touch_queue_.empty())
551     return;
552   scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front());
553   touch_queue_.pop_front();
554
555   if (acked_event->ignore_ack())
556     return;
557
558   // Note that acking the touch-event may result in multiple gestures being sent
559   // to the renderer, or touch-events being queued.
560   base::AutoReset<CoalescedWebTouchEvent*>
561       dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get());
562
563   for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(),
564        end = acked_event->end();
565        iter != end; ++iter) {
566     iter->latency.AddNewLatencyFrom(renderer_latency_info);
567     client_->OnTouchEventAck((*iter), ack_result);
568   }
569 }
570
571 TouchEventQueue::PreFilterResult
572 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
573   if (timeout_handler_ && timeout_handler_->FilterEvent(event))
574     return ACK_WITH_NO_CONSUMER_EXISTS;
575
576   if (touchmove_slop_suppressor_->FilterEvent(event))
577     return ACK_WITH_NOT_CONSUMED;
578
579   if (touch_filtering_state_ == DROP_ALL_TOUCHES)
580     return ACK_WITH_NO_CONSUMER_EXISTS;
581
582   if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE &&
583       event.type != WebInputEvent::TouchCancel) {
584     if (WebTouchEventTraits::IsTouchSequenceStart(event))
585       return FORWARD_TO_RENDERER;
586     return ACK_WITH_NOT_CONSUMED;
587   }
588
589   if (absorbing_touch_moves_ && event.type == WebInputEvent::TouchMove)
590     return ACK_WITH_NOT_CONSUMED;
591
592   // Touch press events should always be forwarded to the renderer.
593   if (event.type == WebInputEvent::TouchStart)
594     return FORWARD_TO_RENDERER;
595
596   for (unsigned int i = 0; i < event.touchesLength; ++i) {
597     const WebTouchPoint& point = event.touches[i];
598     // If a point has been stationary, then don't take it into account.
599     if (point.state == WebTouchPoint::StateStationary)
600       continue;
601
602     if (touch_ack_states_.count(point.id) > 0) {
603       if (touch_ack_states_.find(point.id)->second !=
604           INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
605         return FORWARD_TO_RENDERER;
606     } else {
607       // If the ACK status of a point is unknown, then the event should be
608       // forwarded to the renderer.
609       return FORWARD_TO_RENDERER;
610     }
611   }
612
613   return ACK_WITH_NO_CONSUMER_EXISTS;
614 }
615
616 void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
617                                            InputEventAckState ack_result) {
618   // Update the ACK status for each touch point in the ACKed event.
619   if (event.type == WebInputEvent::TouchEnd ||
620       event.type == WebInputEvent::TouchCancel) {
621     // The points have been released. Erase the ACK states.
622     for (unsigned i = 0; i < event.touchesLength; ++i) {
623       const WebTouchPoint& point = event.touches[i];
624       if (point.state == WebTouchPoint::StateReleased ||
625           point.state == WebTouchPoint::StateCancelled)
626         touch_ack_states_.erase(point.id);
627     }
628   } else if (event.type == WebInputEvent::TouchStart) {
629     for (unsigned i = 0; i < event.touchesLength; ++i) {
630       const WebTouchPoint& point = event.touches[i];
631       if (point.state == WebTouchPoint::StatePressed)
632         touch_ack_states_[point.id] = ack_result;
633     }
634   }
635 }
636
637 }  // namespace content