handoff: Fix the origin so that it correctly reflects the sender.
[chromium-blink-merge.git] / content / browser / renderer_host / input / touch_event_queue.cc
blobb766b3d1b51530ec4b49d9685cde78ebb302b525
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.
5 #include "content/browser/renderer_host/input/touch_event_queue.h"
7 #include "base/auto_reset.h"
8 #include "base/stl_util.h"
9 #include "base/trace_event/trace_event.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"
14 using blink::WebInputEvent;
15 using blink::WebTouchEvent;
16 using blink::WebTouchPoint;
17 using ui::LatencyInfo;
19 namespace content {
20 namespace {
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;
26 // A sanity check on touches received to ensure that touch movement outside
27 // the platform slop region will cause scrolling, as indicated by the event's
28 // |causesScrollingIfUncanceled| bit.
29 const double kMaxConceivablePlatformSlopRegionLengthDipsSquared = 60. * 60.;
31 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
32 const TouchEventWithLatencyInfo& event_to_cancel) {
33 TouchEventWithLatencyInfo event = event_to_cancel;
34 WebTouchEventTraits::ResetTypeAndTouchStates(
35 WebInputEvent::TouchCancel,
36 // TODO(rbyers): Shouldn't we use a fresh timestamp?
37 event.event.timeStampSeconds,
38 &event.event);
39 return event;
42 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
43 return (event.type == WebInputEvent::TouchStart ||
44 event.type == WebInputEvent::TouchMove) &&
45 !WebInputEventTraits::IgnoresAckDisposition(event);
48 // Compare all properties of touch points to determine the state.
49 bool HasPointChanged(const WebTouchPoint& point_1,
50 const WebTouchPoint& point_2) {
51 DCHECK_EQ(point_1.id, point_2.id);
52 if (point_1.screenPosition != point_2.screenPosition ||
53 point_1.position != point_2.position ||
54 point_1.radiusX != point_2.radiusX ||
55 point_1.radiusY != point_2.radiusY ||
56 point_1.rotationAngle != point_2.rotationAngle ||
57 point_1.force != point_2.force) {
58 return true;
60 return false;
63 } // namespace
66 // Cancels a touch sequence if a touchstart or touchmove ack response is
67 // sufficiently delayed.
68 class TouchEventQueue::TouchTimeoutHandler {
69 public:
70 TouchTimeoutHandler(TouchEventQueue* touch_queue,
71 base::TimeDelta timeout_delay)
72 : touch_queue_(touch_queue),
73 timeout_delay_(timeout_delay),
74 pending_ack_state_(PENDING_ACK_NONE),
75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
76 base::Unretained(this))),
77 enabled_(true),
78 enabled_for_current_sequence_(false) {
79 DCHECK(timeout_delay != base::TimeDelta());
82 ~TouchTimeoutHandler() {}
84 void StartIfNecessary(const TouchEventWithLatencyInfo& event) {
85 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
86 if (!enabled_)
87 return;
89 if (!ShouldTouchTriggerTimeout(event.event))
90 return;
92 if (WebTouchEventTraits::IsTouchSequenceStart(event.event))
93 enabled_for_current_sequence_ = true;
95 if (!enabled_for_current_sequence_)
96 return;
98 timeout_event_ = event;
99 timeout_monitor_.Restart(timeout_delay_);
102 bool ConfirmTouchEvent(InputEventAckState ack_result) {
103 switch (pending_ack_state_) {
104 case PENDING_ACK_NONE:
105 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
106 enabled_for_current_sequence_ = false;
107 timeout_monitor_.Stop();
108 return false;
109 case PENDING_ACK_ORIGINAL_EVENT:
110 if (AckedTimeoutEventRequiresCancel(ack_result)) {
111 SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
112 TouchEventWithLatencyInfo cancel_event =
113 ObtainCancelEventForTouchEvent(timeout_event_);
114 touch_queue_->SendTouchEventImmediately(&cancel_event);
115 } else {
116 SetPendingAckState(PENDING_ACK_NONE);
117 touch_queue_->UpdateTouchConsumerStates(timeout_event_.event,
118 ack_result);
120 return true;
121 case PENDING_ACK_CANCEL_EVENT:
122 SetPendingAckState(PENDING_ACK_NONE);
123 return true;
125 return false;
128 bool FilterEvent(const WebTouchEvent& event) {
129 return HasTimeoutEvent();
132 void SetEnabled(bool enabled) {
133 if (enabled_ == enabled)
134 return;
136 enabled_ = enabled;
138 if (enabled_)
139 return;
141 enabled_for_current_sequence_ = false;
142 // Only reset the |timeout_handler_| if the timer is running and has not
143 // yet timed out. This ensures that an already timed out sequence is
144 // properly flushed by the handler.
145 if (IsTimeoutTimerRunning()) {
146 pending_ack_state_ = PENDING_ACK_NONE;
147 timeout_monitor_.Stop();
151 bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); }
153 bool enabled() const { return enabled_; }
155 private:
156 enum PendingAckState {
157 PENDING_ACK_NONE,
158 PENDING_ACK_ORIGINAL_EVENT,
159 PENDING_ACK_CANCEL_EVENT,
162 void OnTimeOut() {
163 SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT);
164 touch_queue_->FlushQueue();
167 // Skip a cancel event if the timed-out event had no consumer and was the
168 // initial event in the gesture.
169 bool AckedTimeoutEventRequiresCancel(InputEventAckState ack_result) const {
170 DCHECK(HasTimeoutEvent());
171 if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
172 return true;
173 return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_.event);
176 void SetPendingAckState(PendingAckState new_pending_ack_state) {
177 DCHECK_NE(pending_ack_state_, new_pending_ack_state);
178 switch (new_pending_ack_state) {
179 case PENDING_ACK_ORIGINAL_EVENT:
180 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
181 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventTimeout", this);
182 break;
183 case PENDING_ACK_CANCEL_EVENT:
184 DCHECK_EQ(pending_ack_state_, PENDING_ACK_ORIGINAL_EVENT);
185 DCHECK(!timeout_monitor_.IsRunning());
186 DCHECK(touch_queue_->empty());
187 TRACE_EVENT_ASYNC_STEP_INTO0(
188 "input", "TouchEventTimeout", this, "CancelEvent");
189 break;
190 case PENDING_ACK_NONE:
191 DCHECK(!timeout_monitor_.IsRunning());
192 DCHECK(touch_queue_->empty());
193 TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
194 break;
196 pending_ack_state_ = new_pending_ack_state;
199 bool HasTimeoutEvent() const {
200 return pending_ack_state_ != PENDING_ACK_NONE;
204 TouchEventQueue* touch_queue_;
206 // How long to wait on a touch ack before cancelling the touch sequence.
207 base::TimeDelta timeout_delay_;
209 // The touch event source for which we expect the next ack.
210 PendingAckState pending_ack_state_;
212 // The event for which the ack timeout is triggered.
213 TouchEventWithLatencyInfo timeout_event_;
215 // Provides timeout-based callback behavior.
216 TimeoutMonitor timeout_monitor_;
218 bool enabled_;
219 bool enabled_for_current_sequence_;
222 // Provides touchmove slop suppression for a touch sequence until a
223 // (unprevented) touch will trigger immediate scrolling.
224 class TouchEventQueue::TouchMoveSlopSuppressor {
225 public:
226 TouchMoveSlopSuppressor() : suppressing_touchmoves_(false) {}
228 bool FilterEvent(const WebTouchEvent& event) {
229 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
230 suppressing_touchmoves_ = true;
231 touch_start_location_ = gfx::PointF(event.touches[0].position);
234 if (event.type == WebInputEvent::TouchEnd ||
235 event.type == WebInputEvent::TouchCancel)
236 suppressing_touchmoves_ = false;
238 if (event.type != WebInputEvent::TouchMove)
239 return false;
241 if (suppressing_touchmoves_) {
242 if (event.touchesLength > 1) {
243 suppressing_touchmoves_ = false;
244 } else if (event.causesScrollingIfUncanceled) {
245 suppressing_touchmoves_ = false;
246 } else {
247 // No sane slop region should be larger than 60 DIPs.
248 DCHECK_LT((gfx::PointF(event.touches[0].position) -
249 touch_start_location_).LengthSquared(),
250 kMaxConceivablePlatformSlopRegionLengthDipsSquared);
254 return suppressing_touchmoves_;
257 void ConfirmTouchEvent(InputEventAckState ack_result) {
258 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
259 suppressing_touchmoves_ = false;
262 bool suppressing_touchmoves() const { return suppressing_touchmoves_; }
264 private:
265 bool suppressing_touchmoves_;
267 // Sanity check that the upstream touch provider is properly reporting whether
268 // the touch sequence will cause scrolling.
269 gfx::PointF touch_start_location_;
271 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
274 // This class represents a single coalesced touch event. However, it also keeps
275 // track of all the original touch-events that were coalesced into a single
276 // event. The coalesced event is forwarded to the renderer, while the original
277 // touch-events are sent to the Client (on ACK for the coalesced event) so that
278 // the Client receives the event with their original timestamp.
279 class CoalescedWebTouchEvent {
280 public:
281 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
282 bool suppress_client_ack)
283 : coalesced_event_(event), suppress_client_ack_(suppress_client_ack) {
284 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
287 ~CoalescedWebTouchEvent() {
288 TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
291 // Coalesces the event with the existing event if possible. Returns whether
292 // the event was coalesced.
293 bool CoalesceEventIfPossible(
294 const TouchEventWithLatencyInfo& event_with_latency) {
295 if (suppress_client_ack_)
296 return false;
298 if (!coalesced_event_.CanCoalesceWith(event_with_latency))
299 return false;
301 // Addition of the first event to |uncoaleseced_events_to_ack_| is deferred
302 // until the first coalesced event, optimizing the (common) case where the
303 // event is not coalesced at all.
304 if (uncoaleseced_events_to_ack_.empty())
305 uncoaleseced_events_to_ack_.push_back(coalesced_event_);
307 TRACE_EVENT_INSTANT0(
308 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
309 coalesced_event_.CoalesceWith(event_with_latency);
310 uncoaleseced_events_to_ack_.push_back(event_with_latency);
311 DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
312 return true;
315 void DispatchAckToClient(InputEventAckState ack_result,
316 const ui::LatencyInfo* optional_latency_info,
317 TouchEventQueueClient* client) {
318 DCHECK(client);
319 if (suppress_client_ack_)
320 return;
322 if (uncoaleseced_events_to_ack_.empty()) {
323 if (optional_latency_info)
324 coalesced_event_.latency.AddNewLatencyFrom(*optional_latency_info);
325 client->OnTouchEventAck(coalesced_event_, ack_result);
326 return;
329 DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
330 for (WebTouchEventWithLatencyList::iterator
331 iter = uncoaleseced_events_to_ack_.begin(),
332 end = uncoaleseced_events_to_ack_.end();
333 iter != end;
334 ++iter) {
335 if (optional_latency_info)
336 iter->latency.AddNewLatencyFrom(*optional_latency_info);
337 client->OnTouchEventAck(*iter, ack_result);
341 const TouchEventWithLatencyInfo& coalesced_event() const {
342 return coalesced_event_;
345 private:
346 // This is the event that is forwarded to the renderer.
347 TouchEventWithLatencyInfo coalesced_event_;
349 // This is the list of the original events that were coalesced, each requiring
350 // future ack dispatch to the client.
351 // Note that this will be empty if no coalescing has occurred.
352 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
353 WebTouchEventWithLatencyList uncoaleseced_events_to_ack_;
355 bool suppress_client_ack_;
357 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
360 TouchEventQueue::Config::Config()
361 : touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
362 touch_ack_timeout_supported(false) {
365 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
366 const Config& config)
367 : client_(client),
368 dispatching_touch_ack_(false),
369 dispatching_touch_(false),
370 has_handlers_(true),
371 has_handler_for_current_sequence_(false),
372 drop_remaining_touches_in_sequence_(false),
373 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor),
374 send_touch_events_async_(false),
375 last_sent_touch_timestamp_sec_(0) {
376 DCHECK(client);
377 if (config.touch_ack_timeout_supported) {
378 timeout_handler_.reset(
379 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay));
383 TouchEventQueue::~TouchEventQueue() {
384 if (!touch_queue_.empty())
385 STLDeleteElements(&touch_queue_);
388 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
389 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
391 // If the queueing of |event| was triggered by an ack dispatch, defer
392 // processing the event until the dispatch has finished.
393 if (touch_queue_.empty() && !dispatching_touch_ack_) {
394 // Optimization of the case without touch handlers. Removing this path
395 // yields identical results, but this avoids unnecessary allocations.
396 PreFilterResult filter_result = FilterBeforeForwarding(event.event);
397 if (filter_result != FORWARD_TO_RENDERER) {
398 client_->OnTouchEventAck(event,
399 filter_result == ACK_WITH_NO_CONSUMER_EXISTS
400 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
401 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
402 return;
405 // There is no touch event in the queue. Forward it to the renderer
406 // immediately.
407 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
408 ForwardNextEventToRenderer();
409 return;
412 // If the last queued touch-event was a touch-move, and the current event is
413 // also a touch-move, then the events can be coalesced into a single event.
414 if (touch_queue_.size() > 1) {
415 CoalescedWebTouchEvent* last_event = touch_queue_.back();
416 if (last_event->CoalesceEventIfPossible(event))
417 return;
419 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
422 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
423 const LatencyInfo& latency_info) {
424 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
426 DCHECK(!dispatching_touch_ack_);
427 dispatching_touch_ = false;
429 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
430 return;
432 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result);
434 if (touch_queue_.empty())
435 return;
437 PopTouchEventToClient(ack_result, latency_info);
438 TryForwardNextEventToRenderer();
441 void TouchEventQueue::TryForwardNextEventToRenderer() {
442 DCHECK(!dispatching_touch_ack_);
443 // If there are queued touch events, then try to forward them to the renderer
444 // immediately, or ACK the events back to the client if appropriate.
445 while (!touch_queue_.empty()) {
446 PreFilterResult filter_result =
447 FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event);
448 switch (filter_result) {
449 case ACK_WITH_NO_CONSUMER_EXISTS:
450 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
451 break;
452 case ACK_WITH_NOT_CONSUMED:
453 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
454 break;
455 case FORWARD_TO_RENDERER:
456 ForwardNextEventToRenderer();
457 return;
462 void TouchEventQueue::ForwardNextEventToRenderer() {
463 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
465 DCHECK(!empty());
466 DCHECK(!dispatching_touch_);
467 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
469 if (send_touch_events_async_ &&
470 touch.event.type == WebInputEvent::TouchMove) {
471 // Throttling touchmove's in a continuous touchmove stream while scrolling
472 // reduces the risk of jank. However, it's still important that the web
473 // application be sent touches at key points in the gesture stream,
474 // e.g., when the application slop region is exceeded or touchmove
475 // coalescing fails because of different modifiers.
476 bool send_touchmove_now = size() > 1;
477 send_touchmove_now |= pending_async_touchmove_ &&
478 !pending_async_touchmove_->CanCoalesceWith(touch);
479 send_touchmove_now |=
480 touch.event.timeStampSeconds >=
481 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec;
483 if (!send_touchmove_now) {
484 if (!pending_async_touchmove_) {
485 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch));
486 } else {
487 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch));
488 pending_async_touchmove_->CoalesceWith(touch);
490 DCHECK_EQ(1U, size());
491 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
492 // It's possible (though unlikely) that ack'ing the current touch will
493 // trigger the queueing of another touch event (e.g., a touchcancel). As
494 // forwarding of the queued event will be deferred while the ack is being
495 // dispatched (see |OnTouchEvent()|), try forwarding it now.
496 TryForwardNextEventToRenderer();
497 return;
501 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds;
503 // Flush any pending async touch move. If it can be combined with the current
504 // (touchmove) event, great, otherwise send it immediately but separately. Its
505 // ack will trigger forwarding of the original |touch| event.
506 if (pending_async_touchmove_) {
507 if (pending_async_touchmove_->CanCoalesceWith(touch)) {
508 pending_async_touchmove_->CoalesceWith(touch);
509 pending_async_touchmove_->event.cancelable = !send_touch_events_async_;
510 touch = *pending_async_touchmove_;
511 pending_async_touchmove_.reset();
512 } else {
513 scoped_ptr<TouchEventWithLatencyInfo> async_move =
514 pending_async_touchmove_.Pass();
515 async_move->event.cancelable = false;
516 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true));
517 SendTouchEventImmediately(async_move.get());
518 return;
522 // Note: Touchstart events are marked cancelable to allow transitions between
523 // platform scrolling and JS pinching. Touchend events, however, remain
524 // uncancelable, mitigating the risk of jank when transitioning to a fling.
525 if (send_touch_events_async_ && touch.event.type != WebInputEvent::TouchStart)
526 touch.event.cancelable = false;
528 // A synchronous ack will reset |dispatching_touch_|, in which case
529 // the touch timeout should not be started.
530 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
531 SendTouchEventImmediately(&touch);
532 if (dispatching_touch_ && timeout_handler_)
533 timeout_handler_->StartIfNecessary(touch);
536 void TouchEventQueue::OnGestureScrollEvent(
537 const GestureEventWithLatencyInfo& gesture_event) {
538 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) {
539 if (has_handler_for_current_sequence_ &&
540 !drop_remaining_touches_in_sequence_) {
541 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
542 << "A touch handler should be offered a touchmove before scrolling.";
545 pending_async_touchmove_.reset();
547 return;
550 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollUpdate)
551 send_touch_events_async_ = true;
554 void TouchEventQueue::OnGestureEventAck(
555 const GestureEventWithLatencyInfo& event,
556 InputEventAckState ack_result) {
557 // Throttle sending touchmove events as long as the scroll events are handled.
558 // Note that there's no guarantee that this ACK is for the most recent
559 // gesture event (or even part of the current sequence). Worst case, the
560 // delay in updating the absorption state will result in minor UI glitches.
561 // A valid |pending_async_touchmove_| will be flushed when the next event is
562 // forwarded.
563 if (event.event.type == blink::WebInputEvent::GestureScrollUpdate)
564 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
567 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
568 DCHECK(!dispatching_touch_ack_);
569 DCHECK(!dispatching_touch_);
570 has_handlers_ = has_handlers;
573 bool TouchEventQueue::IsPendingAckTouchStart() const {
574 DCHECK(!dispatching_touch_ack_);
575 if (touch_queue_.empty())
576 return false;
578 const blink::WebTouchEvent& event =
579 touch_queue_.front()->coalesced_event().event;
580 return (event.type == WebInputEvent::TouchStart);
583 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
584 if (timeout_handler_)
585 timeout_handler_->SetEnabled(enabled);
588 bool TouchEventQueue::IsAckTimeoutEnabled() const {
589 return timeout_handler_ && timeout_handler_->enabled();
592 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
593 return pending_async_touchmove_;
596 bool TouchEventQueue::IsTimeoutRunningForTesting() const {
597 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
600 const TouchEventWithLatencyInfo&
601 TouchEventQueue::GetLatestEventForTesting() const {
602 return touch_queue_.back()->coalesced_event();
605 void TouchEventQueue::FlushQueue() {
606 DCHECK(!dispatching_touch_ack_);
607 DCHECK(!dispatching_touch_);
608 pending_async_touchmove_.reset();
609 drop_remaining_touches_in_sequence_ = true;
610 while (!touch_queue_.empty())
611 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
614 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
615 AckTouchEventToClient(ack_result, PopTouchEvent(), nullptr);
618 void TouchEventQueue::PopTouchEventToClient(
619 InputEventAckState ack_result,
620 const LatencyInfo& renderer_latency_info) {
621 AckTouchEventToClient(ack_result, PopTouchEvent(), &renderer_latency_info);
624 void TouchEventQueue::AckTouchEventToClient(
625 InputEventAckState ack_result,
626 scoped_ptr<CoalescedWebTouchEvent> acked_event,
627 const ui::LatencyInfo* optional_latency_info) {
628 DCHECK(acked_event);
629 DCHECK(!dispatching_touch_ack_);
630 UpdateTouchConsumerStates(acked_event->coalesced_event().event, ack_result);
632 // Note that acking the touch-event may result in multiple gestures being sent
633 // to the renderer, or touch-events being queued.
634 base::AutoReset<bool> dispatching_touch_ack(&dispatching_touch_ack_, true);
635 acked_event->DispatchAckToClient(ack_result, optional_latency_info, client_);
638 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() {
639 DCHECK(!touch_queue_.empty());
640 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front());
641 touch_queue_.pop_front();
642 return event.Pass();
645 void TouchEventQueue::SendTouchEventImmediately(
646 TouchEventWithLatencyInfo* touch) {
647 // For touchmove events, compare touch points position from current event
648 // to last sent event and update touch points state.
649 if (touch->event.type == WebInputEvent::TouchMove) {
650 CHECK(last_sent_touchevent_);
651 for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
652 const WebTouchPoint& last_touch_point =
653 last_sent_touchevent_->touches[i];
654 // Touches with same id may not have same index in Touches array.
655 for (unsigned int j = 0; j < touch->event.touchesLength; ++j) {
656 const WebTouchPoint& current_touchmove_point = touch->event.touches[j];
657 if (current_touchmove_point.id != last_touch_point.id)
658 continue;
660 if (!HasPointChanged(last_touch_point, current_touchmove_point))
661 touch->event.touches[j].state = WebTouchPoint::StateStationary;
663 break;
668 if (last_sent_touchevent_)
669 *last_sent_touchevent_ = touch->event;
670 else
671 last_sent_touchevent_.reset(new WebTouchEvent(touch->event));
673 client_->SendTouchEventImmediately(*touch);
676 TouchEventQueue::PreFilterResult
677 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
678 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
679 has_handler_for_current_sequence_ = false;
680 send_touch_events_async_ = false;
681 pending_async_touchmove_.reset();
682 last_sent_touchevent_.reset();
684 touch_sequence_start_position_ = gfx::PointF(event.touches[0].position);
685 drop_remaining_touches_in_sequence_ = false;
686 if (!has_handlers_) {
687 drop_remaining_touches_in_sequence_ = true;
688 return ACK_WITH_NO_CONSUMER_EXISTS;
692 if (timeout_handler_ && timeout_handler_->FilterEvent(event))
693 return ACK_WITH_NO_CONSUMER_EXISTS;
695 if (touchmove_slop_suppressor_->FilterEvent(event))
696 return ACK_WITH_NOT_CONSUMED;
698 if (drop_remaining_touches_in_sequence_ &&
699 event.type != WebInputEvent::TouchCancel) {
700 return ACK_WITH_NO_CONSUMER_EXISTS;
703 if (event.type == WebInputEvent::TouchStart) {
704 return (has_handlers_ || has_handler_for_current_sequence_)
705 ? FORWARD_TO_RENDERER
706 : ACK_WITH_NO_CONSUMER_EXISTS;
709 if (has_handler_for_current_sequence_) {
710 // Only forward a touch if it has a non-stationary pointer that is active
711 // in the current touch sequence.
712 for (size_t i = 0; i < event.touchesLength; ++i) {
713 const WebTouchPoint& point = event.touches[i];
714 if (point.state == WebTouchPoint::StateStationary)
715 continue;
717 // |last_sent_touchevent_| will be non-null as long as there is an
718 // active touch sequence being forwarded to the renderer.
719 if (!last_sent_touchevent_)
720 continue;
722 for (size_t j = 0; j < last_sent_touchevent_->touchesLength; ++j) {
723 if (point.id != last_sent_touchevent_->touches[j].id)
724 continue;
726 if (event.type != WebInputEvent::TouchMove)
727 return FORWARD_TO_RENDERER;
729 // All pointers in TouchMove events may have state as StateMoved,
730 // even though none of the pointers have not changed in real.
731 // Forward these events when at least one pointer has changed.
732 if (HasPointChanged(last_sent_touchevent_->touches[j], point))
733 return FORWARD_TO_RENDERER;
735 // This is a TouchMove event for which we have yet to find a
736 // non-stationary pointer. Continue checking the next pointers
737 // in the |event|.
738 break;
744 return ACK_WITH_NO_CONSUMER_EXISTS;
747 void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event,
748 InputEventAckState ack_result) {
749 if (event.type == WebInputEvent::TouchStart) {
750 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
751 send_touch_events_async_ = false;
752 has_handler_for_current_sequence_ |=
753 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
754 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) {
755 has_handler_for_current_sequence_ = false;
759 } // namespace content