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/metrics/histogram_macros.h"
9 #include "base/stl_util.h"
10 #include "base/trace_event/trace_event.h"
11 #include "content/browser/renderer_host/input/timeout_monitor.h"
12 #include "content/common/input/web_touch_event_traits.h"
13 #include "ui/gfx/geometry/point_f.h"
15 using blink::WebInputEvent
;
16 using blink::WebTouchEvent
;
17 using blink::WebTouchPoint
;
18 using ui::LatencyInfo
;
23 // Time interval at which touchmove events will be forwarded to the client while
24 // scrolling is active and possible.
25 const double kAsyncTouchMoveIntervalSec
= .2;
27 // A sanity check on touches received to ensure that touch movement outside
28 // the platform slop region will cause scrolling, as indicated by the event's
29 // |causesScrollingIfUncanceled| bit.
30 const double kMaxConceivablePlatformSlopRegionLengthDipsSquared
= 60. * 60.;
32 TouchEventWithLatencyInfo
ObtainCancelEventForTouchEvent(
33 const TouchEventWithLatencyInfo
& event_to_cancel
) {
34 TouchEventWithLatencyInfo event
= event_to_cancel
;
35 WebTouchEventTraits::ResetTypeAndTouchStates(
36 WebInputEvent::TouchCancel
,
37 // TODO(rbyers): Shouldn't we use a fresh timestamp?
38 event
.event
.timeStampSeconds
,
43 bool ShouldTouchTriggerTimeout(const WebTouchEvent
& event
) {
44 return (event
.type
== WebInputEvent::TouchStart
||
45 event
.type
== WebInputEvent::TouchMove
) &&
46 WebInputEventTraits::WillReceiveAckFromRenderer(event
) &&
50 // Compare all properties of touch points to determine the state.
51 bool HasPointChanged(const WebTouchPoint
& point_1
,
52 const WebTouchPoint
& point_2
) {
53 DCHECK_EQ(point_1
.id
, point_2
.id
);
54 if (point_1
.screenPosition
!= point_2
.screenPosition
||
55 point_1
.position
!= point_2
.position
||
56 point_1
.radiusX
!= point_2
.radiusX
||
57 point_1
.radiusY
!= point_2
.radiusY
||
58 point_1
.rotationAngle
!= point_2
.rotationAngle
||
59 point_1
.force
!= point_2
.force
) {
68 // Cancels a touch sequence if a touchstart or touchmove ack response is
69 // sufficiently delayed.
70 class TouchEventQueue::TouchTimeoutHandler
{
72 TouchTimeoutHandler(TouchEventQueue
* touch_queue
,
73 base::TimeDelta desktop_timeout_delay
,
74 base::TimeDelta mobile_timeout_delay
)
75 : touch_queue_(touch_queue
),
76 desktop_timeout_delay_(desktop_timeout_delay
),
77 mobile_timeout_delay_(mobile_timeout_delay
),
78 use_mobile_timeout_(false),
79 pending_ack_state_(PENDING_ACK_NONE
),
80 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut
,
81 base::Unretained(this))),
83 enabled_for_current_sequence_(false),
84 sequence_awaiting_uma_update_(false),
85 sequence_using_mobile_timeout_(false) {
86 SetUseMobileTimeout(false);
89 ~TouchTimeoutHandler() {
90 LogSequenceEndForUMAIfNecessary(false);
93 void StartIfNecessary(const TouchEventWithLatencyInfo
& event
) {
94 if (pending_ack_state_
!= PENDING_ACK_NONE
)
100 const base::TimeDelta timeout_delay
= GetTimeoutDelay();
101 if (timeout_delay
.is_zero())
104 if (!ShouldTouchTriggerTimeout(event
.event
))
107 if (WebTouchEventTraits::IsTouchSequenceStart(event
.event
)) {
108 LogSequenceStartForUMA();
109 enabled_for_current_sequence_
= true;
112 if (!enabled_for_current_sequence_
)
115 timeout_event_
= event
;
116 timeout_monitor_
.Restart(timeout_delay
);
119 bool ConfirmTouchEvent(InputEventAckState ack_result
) {
120 switch (pending_ack_state_
) {
121 case PENDING_ACK_NONE
:
122 if (ack_result
== INPUT_EVENT_ACK_STATE_CONSUMED
)
123 enabled_for_current_sequence_
= false;
124 timeout_monitor_
.Stop();
126 case PENDING_ACK_ORIGINAL_EVENT
:
127 if (AckedTimeoutEventRequiresCancel(ack_result
)) {
128 SetPendingAckState(PENDING_ACK_CANCEL_EVENT
);
129 TouchEventWithLatencyInfo cancel_event
=
130 ObtainCancelEventForTouchEvent(timeout_event_
);
131 touch_queue_
->SendTouchEventImmediately(&cancel_event
);
133 SetPendingAckState(PENDING_ACK_NONE
);
134 touch_queue_
->UpdateTouchConsumerStates(timeout_event_
.event
,
138 case PENDING_ACK_CANCEL_EVENT
:
139 SetPendingAckState(PENDING_ACK_NONE
);
145 bool FilterEvent(const WebTouchEvent
& event
) {
146 if (!HasTimeoutEvent())
149 if (WebTouchEventTraits::IsTouchSequenceStart(event
)) {
150 // If a new sequence is observed while we're still waiting on the
151 // timed-out sequence response, also count the new sequence as timed-out.
152 LogSequenceStartForUMA();
153 LogSequenceEndForUMAIfNecessary(true);
159 void SetEnabled(bool enabled
) {
160 if (enabled_
== enabled
)
168 enabled_for_current_sequence_
= false;
169 // Only reset the |timeout_handler_| if the timer is running and has not
170 // yet timed out. This ensures that an already timed out sequence is
171 // properly flushed by the handler.
172 if (IsTimeoutTimerRunning()) {
173 pending_ack_state_
= PENDING_ACK_NONE
;
174 timeout_monitor_
.Stop();
178 void SetUseMobileTimeout(bool use_mobile_timeout
) {
179 use_mobile_timeout_
= use_mobile_timeout
;
182 bool IsTimeoutTimerRunning() const { return timeout_monitor_
.IsRunning(); }
184 bool IsEnabled() const {
185 return enabled_
&& !GetTimeoutDelay().is_zero();
189 enum PendingAckState
{
191 PENDING_ACK_ORIGINAL_EVENT
,
192 PENDING_ACK_CANCEL_EVENT
,
196 LogSequenceEndForUMAIfNecessary(true);
197 SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT
);
198 touch_queue_
->FlushQueue();
201 // Skip a cancel event if the timed-out event had no consumer and was the
202 // initial event in the gesture.
203 bool AckedTimeoutEventRequiresCancel(InputEventAckState ack_result
) const {
204 DCHECK(HasTimeoutEvent());
205 if (ack_result
!= INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
)
207 return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_
.event
);
210 void SetPendingAckState(PendingAckState new_pending_ack_state
) {
211 DCHECK_NE(pending_ack_state_
, new_pending_ack_state
);
212 switch (new_pending_ack_state
) {
213 case PENDING_ACK_ORIGINAL_EVENT
:
214 DCHECK_EQ(pending_ack_state_
, PENDING_ACK_NONE
);
215 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventTimeout", this);
217 case PENDING_ACK_CANCEL_EVENT
:
218 DCHECK_EQ(pending_ack_state_
, PENDING_ACK_ORIGINAL_EVENT
);
219 DCHECK(!timeout_monitor_
.IsRunning());
220 DCHECK(touch_queue_
->empty());
221 TRACE_EVENT_ASYNC_STEP_INTO0(
222 "input", "TouchEventTimeout", this, "CancelEvent");
224 case PENDING_ACK_NONE
:
225 DCHECK(!timeout_monitor_
.IsRunning());
226 DCHECK(touch_queue_
->empty());
227 TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
230 pending_ack_state_
= new_pending_ack_state
;
233 void LogSequenceStartForUMA() {
234 // Always flush any unlogged entries before starting a new one.
235 LogSequenceEndForUMAIfNecessary(false);
236 sequence_awaiting_uma_update_
= true;
237 sequence_using_mobile_timeout_
= use_mobile_timeout_
;
240 void LogSequenceEndForUMAIfNecessary(bool timed_out
) {
241 if (!sequence_awaiting_uma_update_
)
244 sequence_awaiting_uma_update_
= false;
246 if (sequence_using_mobile_timeout_
) {
247 UMA_HISTOGRAM_BOOLEAN("Event.Touch.TimedOutOnMobileSite", timed_out
);
249 UMA_HISTOGRAM_BOOLEAN("Event.Touch.TimedOutOnDesktopSite", timed_out
);
253 base::TimeDelta
GetTimeoutDelay() const {
254 return use_mobile_timeout_
? mobile_timeout_delay_
: desktop_timeout_delay_
;
257 bool HasTimeoutEvent() const {
258 return pending_ack_state_
!= PENDING_ACK_NONE
;
262 TouchEventQueue
* touch_queue_
;
264 // How long to wait on a touch ack before cancelling the touch sequence.
265 const base::TimeDelta desktop_timeout_delay_
;
266 const base::TimeDelta mobile_timeout_delay_
;
267 bool use_mobile_timeout_
;
269 // The touch event source for which we expect the next ack.
270 PendingAckState pending_ack_state_
;
272 // The event for which the ack timeout is triggered.
273 TouchEventWithLatencyInfo timeout_event_
;
275 // Provides timeout-based callback behavior.
276 TimeoutMonitor timeout_monitor_
;
279 bool enabled_for_current_sequence_
;
281 // Bookkeeping to classify and log whether a touch sequence times out.
282 bool sequence_awaiting_uma_update_
;
283 bool sequence_using_mobile_timeout_
;
286 // Provides touchmove slop suppression for a touch sequence until a
287 // (unprevented) touch will trigger immediate scrolling.
288 class TouchEventQueue::TouchMoveSlopSuppressor
{
290 TouchMoveSlopSuppressor() : suppressing_touchmoves_(false) {}
292 bool FilterEvent(const WebTouchEvent
& event
) {
293 if (WebTouchEventTraits::IsTouchSequenceStart(event
)) {
294 suppressing_touchmoves_
= true;
295 touch_start_location_
= gfx::PointF(event
.touches
[0].position
);
298 if (event
.type
== WebInputEvent::TouchEnd
||
299 event
.type
== WebInputEvent::TouchCancel
)
300 suppressing_touchmoves_
= false;
302 if (event
.type
!= WebInputEvent::TouchMove
)
305 if (suppressing_touchmoves_
) {
306 if (event
.touchesLength
> 1) {
307 suppressing_touchmoves_
= false;
308 } else if (event
.causesScrollingIfUncanceled
) {
309 suppressing_touchmoves_
= false;
311 // No sane slop region should be larger than 60 DIPs.
312 DCHECK_LT((gfx::PointF(event
.touches
[0].position
) -
313 touch_start_location_
).LengthSquared(),
314 kMaxConceivablePlatformSlopRegionLengthDipsSquared
);
318 return suppressing_touchmoves_
;
321 void ConfirmTouchEvent(InputEventAckState ack_result
) {
322 if (ack_result
== INPUT_EVENT_ACK_STATE_CONSUMED
)
323 suppressing_touchmoves_
= false;
326 bool suppressing_touchmoves() const { return suppressing_touchmoves_
; }
329 bool suppressing_touchmoves_
;
331 // Sanity check that the upstream touch provider is properly reporting whether
332 // the touch sequence will cause scrolling.
333 gfx::PointF touch_start_location_
;
335 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor
);
338 // This class represents a single coalesced touch event. However, it also keeps
339 // track of all the original touch-events that were coalesced into a single
340 // event. The coalesced event is forwarded to the renderer, while the original
341 // touch-events are sent to the Client (on ACK for the coalesced event) so that
342 // the Client receives the event with their original timestamp.
343 class CoalescedWebTouchEvent
{
345 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo
& event
,
346 bool suppress_client_ack
)
347 : coalesced_event_(event
), suppress_client_ack_(suppress_client_ack
) {
348 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
351 ~CoalescedWebTouchEvent() {
352 TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
355 // Coalesces the event with the existing event if possible. Returns whether
356 // the event was coalesced.
357 bool CoalesceEventIfPossible(
358 const TouchEventWithLatencyInfo
& event_with_latency
) {
359 if (suppress_client_ack_
)
362 if (!coalesced_event_
.CanCoalesceWith(event_with_latency
))
365 // Addition of the first event to |uncoaleseced_events_to_ack_| is deferred
366 // until the first coalesced event, optimizing the (common) case where the
367 // event is not coalesced at all.
368 if (uncoaleseced_events_to_ack_
.empty())
369 uncoaleseced_events_to_ack_
.push_back(coalesced_event_
);
371 TRACE_EVENT_INSTANT0(
372 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD
);
373 coalesced_event_
.CoalesceWith(event_with_latency
);
374 uncoaleseced_events_to_ack_
.push_back(event_with_latency
);
375 DCHECK_GE(uncoaleseced_events_to_ack_
.size(), 2U);
379 void DispatchAckToClient(InputEventAckState ack_result
,
380 const ui::LatencyInfo
* optional_latency_info
,
381 TouchEventQueueClient
* client
) {
383 if (suppress_client_ack_
)
386 if (uncoaleseced_events_to_ack_
.empty()) {
387 if (optional_latency_info
)
388 coalesced_event_
.latency
.AddNewLatencyFrom(*optional_latency_info
);
389 client
->OnTouchEventAck(coalesced_event_
, ack_result
);
393 DCHECK_GE(uncoaleseced_events_to_ack_
.size(), 2U);
394 for (WebTouchEventWithLatencyList::iterator
395 iter
= uncoaleseced_events_to_ack_
.begin(),
396 end
= uncoaleseced_events_to_ack_
.end();
399 if (optional_latency_info
)
400 iter
->latency
.AddNewLatencyFrom(*optional_latency_info
);
401 client
->OnTouchEventAck(*iter
, ack_result
);
405 const TouchEventWithLatencyInfo
& coalesced_event() const {
406 return coalesced_event_
;
410 // This is the event that is forwarded to the renderer.
411 TouchEventWithLatencyInfo coalesced_event_
;
413 // This is the list of the original events that were coalesced, each requiring
414 // future ack dispatch to the client.
415 // Note that this will be empty if no coalescing has occurred.
416 typedef std::vector
<TouchEventWithLatencyInfo
> WebTouchEventWithLatencyList
;
417 WebTouchEventWithLatencyList uncoaleseced_events_to_ack_
;
419 bool suppress_client_ack_
;
421 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent
);
424 TouchEventQueue::Config::Config()
425 : desktop_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
426 mobile_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(1000)),
427 touch_ack_timeout_supported(false) {
430 TouchEventQueue::TouchEventQueue(TouchEventQueueClient
* client
,
431 const Config
& config
)
433 dispatching_touch_ack_(false),
434 dispatching_touch_(false),
436 has_handler_for_current_sequence_(false),
437 drop_remaining_touches_in_sequence_(false),
438 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor
),
439 send_touch_events_async_(false),
440 last_sent_touch_timestamp_sec_(0) {
442 if (config
.touch_ack_timeout_supported
) {
443 timeout_handler_
.reset(
444 new TouchTimeoutHandler(this,
445 config
.desktop_touch_ack_timeout_delay
,
446 config
.mobile_touch_ack_timeout_delay
));
450 TouchEventQueue::~TouchEventQueue() {
451 if (!touch_queue_
.empty())
452 STLDeleteElements(&touch_queue_
);
455 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo
& event
) {
456 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
458 // If the queueing of |event| was triggered by an ack dispatch, defer
459 // processing the event until the dispatch has finished.
460 if (touch_queue_
.empty() && !dispatching_touch_ack_
) {
461 // Optimization of the case without touch handlers. Removing this path
462 // yields identical results, but this avoids unnecessary allocations.
463 PreFilterResult filter_result
= FilterBeforeForwarding(event
.event
);
464 if (filter_result
!= FORWARD_TO_RENDERER
) {
465 client_
->OnTouchEventAck(event
,
466 filter_result
== ACK_WITH_NO_CONSUMER_EXISTS
467 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
468 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
472 // There is no touch event in the queue. Forward it to the renderer
474 touch_queue_
.push_back(new CoalescedWebTouchEvent(event
, false));
475 ForwardNextEventToRenderer();
479 // If the last queued touch-event was a touch-move, and the current event is
480 // also a touch-move, then the events can be coalesced into a single event.
481 if (touch_queue_
.size() > 1) {
482 CoalescedWebTouchEvent
* last_event
= touch_queue_
.back();
483 if (last_event
->CoalesceEventIfPossible(event
))
486 touch_queue_
.push_back(new CoalescedWebTouchEvent(event
, false));
489 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result
,
490 const LatencyInfo
& latency_info
,
491 const uint32 unique_touch_event_id
) {
492 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
494 // We receive an ack for async touchmove from render.
495 if (!ack_pending_async_touchmove_ids_
.empty() &&
496 ack_pending_async_touchmove_ids_
.front() == unique_touch_event_id
) {
497 // Remove the first touchmove from the ack_pending_async_touchmove queue.
498 ack_pending_async_touchmove_ids_
.pop_front();
499 // Send the next pending async touch move once we receive all acks back.
500 if (pending_async_touchmove_
&& ack_pending_async_touchmove_ids_
.empty()) {
501 DCHECK(touch_queue_
.empty());
503 // Dispatch the next pending async touch move when time expires.
504 if (pending_async_touchmove_
->event
.timeStampSeconds
>=
505 last_sent_touch_timestamp_sec_
+ kAsyncTouchMoveIntervalSec
) {
506 FlushPendingAsyncTouchmove();
512 DCHECK(!dispatching_touch_ack_
);
513 dispatching_touch_
= false;
515 if (timeout_handler_
&& timeout_handler_
->ConfirmTouchEvent(ack_result
))
518 touchmove_slop_suppressor_
->ConfirmTouchEvent(ack_result
);
520 if (touch_queue_
.empty())
523 DCHECK_EQ(touch_queue_
.front()->coalesced_event().event
.uniqueTouchEventId
,
524 unique_touch_event_id
);
526 PopTouchEventToClient(ack_result
, latency_info
);
527 TryForwardNextEventToRenderer();
530 void TouchEventQueue::TryForwardNextEventToRenderer() {
531 DCHECK(!dispatching_touch_ack_
);
532 // If there are queued touch events, then try to forward them to the renderer
533 // immediately, or ACK the events back to the client if appropriate.
534 while (!touch_queue_
.empty()) {
535 PreFilterResult filter_result
=
536 FilterBeforeForwarding(touch_queue_
.front()->coalesced_event().event
);
537 switch (filter_result
) {
538 case ACK_WITH_NO_CONSUMER_EXISTS
:
539 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
);
541 case ACK_WITH_NOT_CONSUMED
:
542 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
544 case FORWARD_TO_RENDERER
:
545 ForwardNextEventToRenderer();
551 void TouchEventQueue::ForwardNextEventToRenderer() {
552 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
555 DCHECK(!dispatching_touch_
);
556 TouchEventWithLatencyInfo touch
= touch_queue_
.front()->coalesced_event();
558 if (send_touch_events_async_
&&
559 touch
.event
.type
== WebInputEvent::TouchMove
) {
560 // Throttling touchmove's in a continuous touchmove stream while scrolling
561 // reduces the risk of jank. However, it's still important that the web
562 // application be sent touches at key points in the gesture stream,
563 // e.g., when the application slop region is exceeded or touchmove
564 // coalescing fails because of different modifiers.
565 bool send_touchmove_now
= size() > 1;
566 send_touchmove_now
|= pending_async_touchmove_
&&
567 !pending_async_touchmove_
->CanCoalesceWith(touch
);
568 send_touchmove_now
|=
569 ack_pending_async_touchmove_ids_
.empty() &&
570 (touch
.event
.timeStampSeconds
>=
571 last_sent_touch_timestamp_sec_
+ kAsyncTouchMoveIntervalSec
);
573 if (!send_touchmove_now
) {
574 if (!pending_async_touchmove_
) {
575 pending_async_touchmove_
.reset(new TouchEventWithLatencyInfo(touch
));
577 DCHECK(pending_async_touchmove_
->CanCoalesceWith(touch
));
578 pending_async_touchmove_
->CoalesceWith(touch
);
580 DCHECK_EQ(1U, size());
581 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
582 // It's possible (though unlikely) that ack'ing the current touch will
583 // trigger the queueing of another touch event (e.g., a touchcancel). As
584 // forwarding of the queued event will be deferred while the ack is being
585 // dispatched (see |OnTouchEvent()|), try forwarding it now.
586 TryForwardNextEventToRenderer();
591 last_sent_touch_timestamp_sec_
= touch
.event
.timeStampSeconds
;
593 // Flush any pending async touch move. If it can be combined with the current
594 // (touchmove) event, great, otherwise send it immediately but separately. Its
595 // ack will trigger forwarding of the original |touch| event.
596 if (pending_async_touchmove_
) {
597 if (pending_async_touchmove_
->CanCoalesceWith(touch
)) {
598 pending_async_touchmove_
->CoalesceWith(touch
);
599 pending_async_touchmove_
->event
.cancelable
= !send_touch_events_async_
;
600 touch
= *pending_async_touchmove_
;
601 pending_async_touchmove_
.reset();
603 FlushPendingAsyncTouchmove();
608 // Note: Touchstart events are marked cancelable to allow transitions between
609 // platform scrolling and JS pinching. Touchend events, however, remain
610 // uncancelable, mitigating the risk of jank when transitioning to a fling.
611 if (send_touch_events_async_
&& touch
.event
.type
!= WebInputEvent::TouchStart
)
612 touch
.event
.cancelable
= false;
614 SendTouchEventImmediately(&touch
);
617 void TouchEventQueue::FlushPendingAsyncTouchmove() {
618 DCHECK(!dispatching_touch_
);
619 scoped_ptr
<TouchEventWithLatencyInfo
> touch
= pending_async_touchmove_
.Pass();
620 touch
->event
.cancelable
= false;
621 touch_queue_
.push_front(new CoalescedWebTouchEvent(*touch
, true));
622 SendTouchEventImmediately(touch
.get());
625 void TouchEventQueue::OnGestureScrollEvent(
626 const GestureEventWithLatencyInfo
& gesture_event
) {
627 if (gesture_event
.event
.type
== blink::WebInputEvent::GestureScrollBegin
) {
628 if (has_handler_for_current_sequence_
&&
629 !drop_remaining_touches_in_sequence_
) {
630 DCHECK(!touchmove_slop_suppressor_
->suppressing_touchmoves())
631 << "A touch handler should be offered a touchmove before scrolling.";
634 pending_async_touchmove_
.reset();
639 if (gesture_event
.event
.type
== blink::WebInputEvent::GestureScrollUpdate
)
640 send_touch_events_async_
= true;
643 void TouchEventQueue::OnGestureEventAck(
644 const GestureEventWithLatencyInfo
& event
,
645 InputEventAckState ack_result
) {
646 // Throttle sending touchmove events as long as the scroll events are handled.
647 // Note that there's no guarantee that this ACK is for the most recent
648 // gesture event (or even part of the current sequence). Worst case, the
649 // delay in updating the absorption state will result in minor UI glitches.
650 // A valid |pending_async_touchmove_| will be flushed when the next event is
652 if (event
.event
.type
== blink::WebInputEvent::GestureScrollUpdate
)
653 send_touch_events_async_
= (ack_result
== INPUT_EVENT_ACK_STATE_CONSUMED
);
656 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers
) {
657 DCHECK(!dispatching_touch_ack_
);
658 DCHECK(!dispatching_touch_
);
659 has_handlers_
= has_handlers
;
662 bool TouchEventQueue::IsPendingAckTouchStart() const {
663 DCHECK(!dispatching_touch_ack_
);
664 if (touch_queue_
.empty())
667 const blink::WebTouchEvent
& event
=
668 touch_queue_
.front()->coalesced_event().event
;
669 return (event
.type
== WebInputEvent::TouchStart
);
672 void TouchEventQueue::SetAckTimeoutEnabled(bool enabled
) {
673 if (timeout_handler_
)
674 timeout_handler_
->SetEnabled(enabled
);
677 void TouchEventQueue::SetIsMobileOptimizedSite(bool mobile_optimized_site
) {
678 if (timeout_handler_
)
679 timeout_handler_
->SetUseMobileTimeout(mobile_optimized_site
);
682 bool TouchEventQueue::IsAckTimeoutEnabled() const {
683 return timeout_handler_
&& timeout_handler_
->IsEnabled();
686 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
687 return pending_async_touchmove_
;
690 bool TouchEventQueue::IsTimeoutRunningForTesting() const {
691 return timeout_handler_
&& timeout_handler_
->IsTimeoutTimerRunning();
694 const TouchEventWithLatencyInfo
&
695 TouchEventQueue::GetLatestEventForTesting() const {
696 return touch_queue_
.back()->coalesced_event();
699 void TouchEventQueue::FlushQueue() {
700 DCHECK(!dispatching_touch_ack_
);
701 DCHECK(!dispatching_touch_
);
702 pending_async_touchmove_
.reset();
703 drop_remaining_touches_in_sequence_
= true;
704 while (!touch_queue_
.empty())
705 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
);
708 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result
) {
709 AckTouchEventToClient(ack_result
, PopTouchEvent(), nullptr);
712 void TouchEventQueue::PopTouchEventToClient(
713 InputEventAckState ack_result
,
714 const LatencyInfo
& renderer_latency_info
) {
715 AckTouchEventToClient(ack_result
, PopTouchEvent(), &renderer_latency_info
);
718 void TouchEventQueue::AckTouchEventToClient(
719 InputEventAckState ack_result
,
720 scoped_ptr
<CoalescedWebTouchEvent
> acked_event
,
721 const ui::LatencyInfo
* optional_latency_info
) {
723 DCHECK(!dispatching_touch_ack_
);
724 UpdateTouchConsumerStates(acked_event
->coalesced_event().event
, ack_result
);
726 // Note that acking the touch-event may result in multiple gestures being sent
727 // to the renderer, or touch-events being queued.
728 base::AutoReset
<bool> dispatching_touch_ack(&dispatching_touch_ack_
, true);
729 acked_event
->DispatchAckToClient(ack_result
, optional_latency_info
, client_
);
732 scoped_ptr
<CoalescedWebTouchEvent
> TouchEventQueue::PopTouchEvent() {
733 DCHECK(!touch_queue_
.empty());
734 scoped_ptr
<CoalescedWebTouchEvent
> event(touch_queue_
.front());
735 touch_queue_
.pop_front();
739 void TouchEventQueue::SendTouchEventImmediately(
740 TouchEventWithLatencyInfo
* touch
) {
741 // For touchmove events, compare touch points position from current event
742 // to last sent event and update touch points state.
743 if (touch
->event
.type
== WebInputEvent::TouchMove
) {
744 CHECK(last_sent_touchevent_
);
745 for (unsigned int i
= 0; i
< last_sent_touchevent_
->touchesLength
; ++i
) {
746 const WebTouchPoint
& last_touch_point
=
747 last_sent_touchevent_
->touches
[i
];
748 // Touches with same id may not have same index in Touches array.
749 for (unsigned int j
= 0; j
< touch
->event
.touchesLength
; ++j
) {
750 const WebTouchPoint
& current_touchmove_point
= touch
->event
.touches
[j
];
751 if (current_touchmove_point
.id
!= last_touch_point
.id
)
754 if (!HasPointChanged(last_touch_point
, current_touchmove_point
))
755 touch
->event
.touches
[j
].state
= WebTouchPoint::StateStationary
;
762 if (last_sent_touchevent_
)
763 *last_sent_touchevent_
= touch
->event
;
765 last_sent_touchevent_
.reset(new WebTouchEvent(touch
->event
));
767 base::AutoReset
<bool> dispatching_touch(&dispatching_touch_
, true);
769 client_
->SendTouchEventImmediately(*touch
);
771 // A synchronous ack will reset |dispatching_touch_|, in which case the touch
772 // timeout should not be started and the count also should not be increased.
773 if (dispatching_touch_
) {
774 if (touch
->event
.type
== WebInputEvent::TouchMove
&&
775 !touch
->event
.cancelable
) {
776 // When we send out a uncancelable touch move, we increase the count and
777 // we do not process input event ack any more, we will just ack to client
778 // and wait for the ack from render. Also we will remove it from the front
780 ack_pending_async_touchmove_ids_
.push_back(
781 touch
->event
.uniqueTouchEventId
);
782 dispatching_touch_
= false;
783 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_IGNORED
);
784 TryForwardNextEventToRenderer();
788 if (timeout_handler_
)
789 timeout_handler_
->StartIfNecessary(*touch
);
793 TouchEventQueue::PreFilterResult
794 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent
& event
) {
795 if (WebTouchEventTraits::IsTouchSequenceStart(event
)) {
796 has_handler_for_current_sequence_
= false;
797 send_touch_events_async_
= false;
798 pending_async_touchmove_
.reset();
799 last_sent_touchevent_
.reset();
801 touch_sequence_start_position_
= gfx::PointF(event
.touches
[0].position
);
802 drop_remaining_touches_in_sequence_
= false;
803 if (!has_handlers_
) {
804 drop_remaining_touches_in_sequence_
= true;
805 return ACK_WITH_NO_CONSUMER_EXISTS
;
809 if (timeout_handler_
&& timeout_handler_
->FilterEvent(event
))
810 return ACK_WITH_NO_CONSUMER_EXISTS
;
812 if (touchmove_slop_suppressor_
->FilterEvent(event
))
813 return ACK_WITH_NOT_CONSUMED
;
815 if (drop_remaining_touches_in_sequence_
&&
816 event
.type
!= WebInputEvent::TouchCancel
) {
817 return ACK_WITH_NO_CONSUMER_EXISTS
;
820 if (event
.type
== WebInputEvent::TouchStart
) {
821 return (has_handlers_
|| has_handler_for_current_sequence_
)
822 ? FORWARD_TO_RENDERER
823 : ACK_WITH_NO_CONSUMER_EXISTS
;
826 if (has_handler_for_current_sequence_
) {
827 // Only forward a touch if it has a non-stationary pointer that is active
828 // in the current touch sequence.
829 for (size_t i
= 0; i
< event
.touchesLength
; ++i
) {
830 const WebTouchPoint
& point
= event
.touches
[i
];
831 if (point
.state
== WebTouchPoint::StateStationary
)
834 // |last_sent_touchevent_| will be non-null as long as there is an
835 // active touch sequence being forwarded to the renderer.
836 if (!last_sent_touchevent_
)
839 for (size_t j
= 0; j
< last_sent_touchevent_
->touchesLength
; ++j
) {
840 if (point
.id
!= last_sent_touchevent_
->touches
[j
].id
)
843 if (event
.type
!= WebInputEvent::TouchMove
)
844 return FORWARD_TO_RENDERER
;
846 // All pointers in TouchMove events may have state as StateMoved,
847 // even though none of the pointers have not changed in real.
848 // Forward these events when at least one pointer has changed.
849 if (HasPointChanged(last_sent_touchevent_
->touches
[j
], point
))
850 return FORWARD_TO_RENDERER
;
852 // This is a TouchMove event for which we have yet to find a
853 // non-stationary pointer. Continue checking the next pointers
861 return ACK_WITH_NO_CONSUMER_EXISTS
;
864 void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent
& event
,
865 InputEventAckState ack_result
) {
866 if (event
.type
== WebInputEvent::TouchStart
) {
867 if (ack_result
== INPUT_EVENT_ACK_STATE_CONSUMED
)
868 send_touch_events_async_
= false;
869 has_handler_for_current_sequence_
|=
870 ack_result
!= INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
;
871 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event
)) {
872 has_handler_for_current_sequence_
= false;
876 } // namespace content