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
;
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
,
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
) {
66 // Cancels a touch sequence if a touchstart or touchmove ack response is
67 // sufficiently delayed.
68 class TouchEventQueue::TouchTimeoutHandler
{
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))),
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
);
89 if (!ShouldTouchTriggerTimeout(event
.event
))
92 if (WebTouchEventTraits::IsTouchSequenceStart(event
.event
))
93 enabled_for_current_sequence_
= true;
95 if (!enabled_for_current_sequence_
)
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();
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
);
116 SetPendingAckState(PENDING_ACK_NONE
);
117 touch_queue_
->UpdateTouchConsumerStates(timeout_event_
.event
,
121 case PENDING_ACK_CANCEL_EVENT
:
122 SetPendingAckState(PENDING_ACK_NONE
);
128 bool FilterEvent(const WebTouchEvent
& event
) {
129 return HasTimeoutEvent();
132 void SetEnabled(bool enabled
) {
133 if (enabled_
== enabled
)
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_
; }
156 enum PendingAckState
{
158 PENDING_ACK_ORIGINAL_EVENT
,
159 PENDING_ACK_CANCEL_EVENT
,
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
)
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);
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");
190 case PENDING_ACK_NONE
:
191 DCHECK(!timeout_monitor_
.IsRunning());
192 DCHECK(touch_queue_
->empty());
193 TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
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_
;
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
{
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
)
241 if (suppressing_touchmoves_
) {
242 if (event
.touchesLength
> 1) {
243 suppressing_touchmoves_
= false;
244 } else if (event
.causesScrollingIfUncanceled
) {
245 suppressing_touchmoves_
= false;
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_
; }
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
{
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_
)
298 if (!coalesced_event_
.CanCoalesceWith(event_with_latency
))
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);
315 void DispatchAckToClient(InputEventAckState ack_result
,
316 const ui::LatencyInfo
* optional_latency_info
,
317 TouchEventQueueClient
* client
) {
319 if (suppress_client_ack_
)
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
);
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();
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_
;
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
)
368 dispatching_touch_ack_(false),
369 dispatching_touch_(false),
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) {
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
);
405 // There is no touch event in the queue. Forward it to the renderer
407 touch_queue_
.push_back(new CoalescedWebTouchEvent(event
, false));
408 ForwardNextEventToRenderer();
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
))
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
))
432 touchmove_slop_suppressor_
->ConfirmTouchEvent(ack_result
);
434 if (touch_queue_
.empty())
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
);
452 case ACK_WITH_NOT_CONSUMED
:
453 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED
);
455 case FORWARD_TO_RENDERER
:
456 ForwardNextEventToRenderer();
462 void TouchEventQueue::ForwardNextEventToRenderer() {
463 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
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
));
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();
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();
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());
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();
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
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())
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
) {
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();
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
)
660 if (!HasPointChanged(last_touch_point
, current_touchmove_point
))
661 touch
->event
.touches
[j
].state
= WebTouchPoint::StateStationary
;
668 if (last_sent_touchevent_
)
669 *last_sent_touchevent_
= touch
->event
;
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
)
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_
)
722 for (size_t j
= 0; j
< last_sent_touchevent_
->touchesLength
; ++j
) {
723 if (point
.id
!= last_sent_touchevent_
->touches
[j
].id
)
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
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