Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(404)

Side by Side Diff: content/browser/renderer_host/input/touch_event_queue.cc

Issue 130993003: Prevent partial touch sequences from reaching the renderer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Test Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/input/touch_event_queue.h" 5 #include "content/browser/renderer_host/input/touch_event_queue.h"
6 6
7 #include "base/auto_reset.h" 7 #include "base/auto_reset.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
10 #include "base/stl_util.h" 10 #include "base/stl_util.h"
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 bool ConfirmTouchEvent(InputEventAckState ack_result) { 73 bool ConfirmTouchEvent(InputEventAckState ack_result) {
74 switch (pending_ack_state_) { 74 switch (pending_ack_state_) {
75 case PENDING_ACK_NONE: 75 case PENDING_ACK_NONE:
76 timeout_monitor_.Stop(); 76 timeout_monitor_.Stop();
77 return false; 77 return false;
78 case PENDING_ACK_ORIGINAL_EVENT: 78 case PENDING_ACK_ORIGINAL_EVENT:
79 if (AckedTimeoutEventRequiresCancel(ack_result)) { 79 if (AckedTimeoutEventRequiresCancel(ack_result)) {
80 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); 80 SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
81 TouchEventWithLatencyInfo cancel_event = 81 TouchEventWithLatencyInfo cancel_event =
82 ObtainCancelEventForTouchEvent(timeout_event_); 82 ObtainCancelEventForTouchEvent(timeout_event_);
83 touch_queue_->UpdateTouchAckStates(
84 cancel_event.event, kDefaultNotForwardedAck);
85 touch_queue_->client_->SendTouchEventImmediately(cancel_event); 83 touch_queue_->client_->SendTouchEventImmediately(cancel_event);
86 } else { 84 } else {
87 SetPendingAckState(PENDING_ACK_NONE); 85 SetPendingAckState(PENDING_ACK_NONE);
88 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); 86 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result);
89 } 87 }
90 return true; 88 return true;
91 case PENDING_ACK_CANCEL_EVENT: 89 case PENDING_ACK_CANCEL_EVENT:
92 SetPendingAckState(PENDING_ACK_NONE); 90 SetPendingAckState(PENDING_ACK_NONE);
93 return true; 91 return true;
94 } 92 }
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 // when the event is acked. 232 // when the event is acked.
235 bool ignore_ack_; 233 bool ignore_ack_;
236 234
237 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); 235 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
238 }; 236 };
239 237
240 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) 238 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client)
241 : client_(client), 239 : client_(client),
242 dispatching_touch_ack_(NULL), 240 dispatching_touch_ack_(NULL),
243 dispatching_touch_(false), 241 dispatching_touch_(false),
244 has_handlers_(false), 242 touch_handling_state_(TOUCH_HANDLING_STATE_DEFAULT),
245 scroll_in_progress_(false),
246 renderer_is_consuming_touch_gesture_(false),
247 ack_timeout_enabled_(false) { 243 ack_timeout_enabled_(false) {
248 DCHECK(client); 244 DCHECK(client);
249 } 245 }
250 246
251 TouchEventQueue::~TouchEventQueue() { 247 TouchEventQueue::~TouchEventQueue() {
252 if (!touch_queue_.empty()) 248 if (!touch_queue_.empty())
253 STLDeleteElements(&touch_queue_); 249 STLDeleteElements(&touch_queue_);
254 } 250 }
255 251
256 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { 252 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
257 // Optimization of the case without touch handlers. Removing this path 253 // Optimization of the case without touch handlers. Removing this path
258 // yields identical results, but this avoids unnecessary allocations. 254 // yields identical results, but this avoids unnecessary allocations.
259 if (!has_handlers_) { 255 if (touch_handling_state_ == NO_TOUCH_HANDLER) {
260 DCHECK(touch_queue_.empty()); 256 DCHECK(touch_queue_.empty());
261 client_->OnTouchEventAck(event, kDefaultNotForwardedAck); 257 client_->OnTouchEventAck(event, kDefaultNotForwardedAck);
262 return; 258 return;
263 } 259 }
264 260
265 // If the queueing of |event| was triggered by an ack dispatch, defer 261 // If the queueing of |event| was triggered by an ack dispatch, defer
266 // processing the event until the dispatch has finished. 262 // processing the event until the dispatch has finished.
267 if (touch_queue_.empty() && !dispatching_touch_ack_) { 263 if (touch_queue_.empty() && !dispatching_touch_ack_) {
268 // There is no touch event in the queue. Forward it to the renderer 264 // There is no touch event in the queue. Forward it to the renderer
269 // immediately. 265 // immediately.
(...skipping 17 matching lines...) Expand all
287 DCHECK(!dispatching_touch_ack_); 283 DCHECK(!dispatching_touch_ack_);
288 dispatching_touch_ = false; 284 dispatching_touch_ = false;
289 285
290 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) 286 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
291 return; 287 return;
292 288
293 if (touch_queue_.empty()) 289 if (touch_queue_.empty())
294 return; 290 return;
295 291
296 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) 292 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
297 renderer_is_consuming_touch_gesture_ = true; 293 touch_handling_state_ = RENDERER_CONSUMING_GESTURE;
298 294
299 const WebTouchEvent& acked_event = 295 const WebTouchEvent& acked_event =
300 touch_queue_.front()->coalesced_event().event; 296 touch_queue_.front()->coalesced_event().event;
301 UpdateTouchAckStates(acked_event, ack_result); 297 UpdateTouchAckStates(acked_event, ack_result);
302 PopTouchEventToClient(ack_result, latency_info); 298 PopTouchEventToClient(ack_result, latency_info);
303 TryForwardNextEventToRenderer(); 299 TryForwardNextEventToRenderer();
304 } 300 }
305 301
306 void TouchEventQueue::TryForwardNextEventToRenderer() { 302 void TouchEventQueue::TryForwardNextEventToRenderer() {
307 DCHECK(!dispatching_touch_ack_); 303 DCHECK(!dispatching_touch_ack_);
308 // If there are queued touch events, then try to forward them to the renderer 304 // If there are queued touch events, then try to forward them to the renderer
309 // immediately, or ACK the events back to the client if appropriate. 305 // immediately, or ACK the events back to the client if appropriate.
310 while (!touch_queue_.empty()) { 306 while (!touch_queue_.empty()) {
311 const TouchEventWithLatencyInfo& touch = 307 const TouchEventWithLatencyInfo& touch =
312 touch_queue_.front()->coalesced_event(); 308 touch_queue_.front()->coalesced_event();
313 if (IsNewTouchGesture(touch.event)) {
314 touch_ack_states_.clear();
315 renderer_is_consuming_touch_gesture_ = false;
316 }
317 if (ShouldForwardToRenderer(touch.event)) { 309 if (ShouldForwardToRenderer(touch.event)) {
318 ForwardToRenderer(touch); 310 ForwardToRenderer(touch);
319 break; 311 break;
320 } 312 }
321 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); 313 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
322 } 314 }
323 } 315 }
324 316
325 void TouchEventQueue::ForwardToRenderer( 317 void TouchEventQueue::ForwardToRenderer(
326 const TouchEventWithLatencyInfo& touch) { 318 const TouchEventWithLatencyInfo& touch) {
327 DCHECK(!dispatching_touch_); 319 DCHECK(!dispatching_touch_);
320 DCHECK_NE(touch_handling_state_, NO_TOUCH_HANDLER);
321
322 if (IsNewTouchGesture(touch.event)) {
323 touch_handling_state_ = HAS_TOUCH_HANDLER;
324 touch_ack_states_.clear();
325 }
326
328 // A synchronous ack will reset |dispatching_touch_|, in which case 327 // A synchronous ack will reset |dispatching_touch_|, in which case
329 // the touch timeout should not be started. 328 // the touch timeout should not be started.
330 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); 329 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
331 client_->SendTouchEventImmediately(touch); 330 client_->SendTouchEventImmediately(touch);
332 if (ack_timeout_enabled_ && 331 if (ack_timeout_enabled_ &&
333 dispatching_touch_ && 332 dispatching_touch_ &&
334 !renderer_is_consuming_touch_gesture_ && 333 touch_handling_state_ != RENDERER_CONSUMING_GESTURE &&
335 ShouldTouchTypeTriggerTimeout(touch.event.type)) { 334 ShouldTouchTypeTriggerTimeout(touch.event.type)) {
336 DCHECK(timeout_handler_); 335 DCHECK(timeout_handler_);
337 timeout_handler_->Start(touch); 336 timeout_handler_->Start(touch);
338 } 337 }
339 } 338 }
340 339
341 void TouchEventQueue::OnGestureScrollEvent( 340 void TouchEventQueue::OnGestureScrollEvent(
342 const GestureEventWithLatencyInfo& gesture_event) { 341 const GestureEventWithLatencyInfo& gesture_event) {
343 blink::WebInputEvent::Type type = gesture_event.event.type; 342 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin)
344 if (type == blink::WebInputEvent::GestureScrollBegin) { 343 return;
345 // We assume the scroll event are generated synchronously from
346 // dispatching a touch event ack, so that we can fake a cancel
347 // event that has the correct touch ids as the touch event that
348 // is being acked. If not, we don't do the touch-cancel optimization.
349 if (scroll_in_progress_ || !dispatching_touch_ack_)
350 return;
351 scroll_in_progress_ = true;
352 344
353 // If we have a timeout event, a cancel has already been dispatched 345 // We assume the scroll event are generated synchronously from
tdresser 2014/01/17 14:33:40 While you're here, wouldn't hurt to fix "scroll ev
jdduke (slow) 2014/01/17 18:29:07 Done.
354 // for the current touch stream. 346 // dispatching a touch event ack, so that we can fake a cancel
355 if (HasTimeoutEvent()) 347 // event that has the correct touch ids as the touch event that
356 return; 348 // is being acked. If not, we don't do the touch-cancel optimization.
349 if (!dispatching_touch_ack_)
350 return;
357 351
358 // Fake a TouchCancel to cancel the touch points of the touch event 352 if (touch_handling_state_ == NO_TOUCH_HANDLER_FOR_GESTURE)
359 // that is currently being acked. 353 return;
360 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we 354
361 // are in the scope of PopTouchEventToClient() and that no touch event 355 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE;
362 // in the queue is waiting for ack from renderer. So we can just insert 356
363 // the touch cancel at the beginning of the queue. 357 // If we have a timeout event, a cancel has already been dispatched
364 touch_queue_.push_front(new CoalescedWebTouchEvent( 358 // for the current touch stream.
365 ObtainCancelEventForTouchEvent( 359 if (HasTimeoutEvent())
366 dispatching_touch_ack_->coalesced_event()), true)); 360 return;
367 } else if (type == blink::WebInputEvent::GestureScrollEnd || 361
368 type == blink::WebInputEvent::GestureFlingStart) { 362 // Fake a TouchCancel to cancel the touch points of the touch event
369 scroll_in_progress_ = false; 363 // that is currently being acked.
370 } 364 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
365 // are in the scope of PopTouchEventToClient() and that no touch event
366 // in the queue is waiting for ack from renderer. So we can just insert
367 // the touch cancel at the beginning of the queue.
368 touch_queue_.push_front(new CoalescedWebTouchEvent(
369 ObtainCancelEventForTouchEvent(
370 dispatching_touch_ack_->coalesced_event()), true));
371 } 371 }
372 372
373 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { 373 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
374 DCHECK(!dispatching_touch_ack_); 374 DCHECK(!dispatching_touch_ack_);
375 DCHECK(!dispatching_touch_); 375 DCHECK(!dispatching_touch_);
376 if (has_handlers_ == has_handlers)
377 return;
378 376
379 has_handlers_ = has_handlers; 377 if (has_handlers) {
380 378 if (touch_handling_state_ == NO_TOUCH_HANDLER) {
381 if (!has_handlers_) { 379 // If no touch handler was previously registered, ensure that we don't
382 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch 380 // send a partial gesture to the renderer.
383 // state tracking. 381 DCHECK(touch_queue_.empty());
382 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE;
383 }
384 } else {
385 touch_handling_state_ = NO_TOUCH_HANDLER;
384 if (timeout_handler_) 386 if (timeout_handler_)
385 timeout_handler_->Reset(); 387 timeout_handler_->Reset();
386 if (!touch_queue_.empty()) 388 if (!touch_queue_.empty())
387 ProcessTouchAck(kDefaultNotForwardedAck, ui::LatencyInfo()); 389 ProcessTouchAck(kDefaultNotForwardedAck, ui::LatencyInfo());
388 // As there is no touch handler, ack'ing the event should flush the queue. 390 // As there is no touch handler, ack'ing the event should flush the queue.
389 DCHECK(touch_queue_.empty()); 391 DCHECK(touch_queue_.empty());
390 } else {
391 DCHECK(touch_queue_.empty());
392 // Prevent a partial sequence from being sent to the renderer.
393 TouchPointAckStates::iterator ack_it = touch_ack_states_.begin();
394 for (; ack_it != touch_ack_states_.end(); ++ack_it)
395 ack_it->second = kDefaultNotForwardedAck;
396 } 392 }
397 } 393 }
398 394
399 bool TouchEventQueue::IsPendingAckTouchStart() const { 395 bool TouchEventQueue::IsPendingAckTouchStart() const {
400 DCHECK(!dispatching_touch_ack_); 396 DCHECK(!dispatching_touch_ack_);
401 if (touch_queue_.empty()) 397 if (touch_queue_.empty())
402 return false; 398 return false;
403 399
404 const blink::WebTouchEvent& event = 400 const blink::WebTouchEvent& event =
405 touch_queue_.front()->coalesced_event().event; 401 touch_queue_.front()->coalesced_event().event;
(...skipping 23 matching lines...) Expand all
429 } 425 }
430 426
431 const TouchEventWithLatencyInfo& 427 const TouchEventWithLatencyInfo&
432 TouchEventQueue::GetLatestEventForTesting() const { 428 TouchEventQueue::GetLatestEventForTesting() const {
433 return touch_queue_.back()->coalesced_event(); 429 return touch_queue_.back()->coalesced_event();
434 } 430 }
435 431
436 void TouchEventQueue::FlushQueue() { 432 void TouchEventQueue::FlushQueue() {
437 DCHECK(!dispatching_touch_ack_); 433 DCHECK(!dispatching_touch_ack_);
438 DCHECK(!dispatching_touch_); 434 DCHECK(!dispatching_touch_);
435 DCHECK_NE(RENDERER_CONSUMING_GESTURE, touch_handling_state_);
436 if (touch_handling_state_ != NO_TOUCH_HANDLER)
437 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE;
439 while (!touch_queue_.empty()) 438 while (!touch_queue_.empty())
440 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); 439 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
441 } 440 }
442 441
443 void TouchEventQueue::PopTouchEventToClient( 442 void TouchEventQueue::PopTouchEventToClient(
444 InputEventAckState ack_result, 443 InputEventAckState ack_result,
445 const ui::LatencyInfo& renderer_latency_info) { 444 const ui::LatencyInfo& renderer_latency_info) {
446 DCHECK(!dispatching_touch_ack_); 445 DCHECK(!dispatching_touch_ack_);
447 if (touch_queue_.empty()) 446 if (touch_queue_.empty())
448 return; 447 return;
(...skipping 14 matching lines...) Expand all
463 iter->latency.AddNewLatencyFrom(renderer_latency_info); 462 iter->latency.AddNewLatencyFrom(renderer_latency_info);
464 client_->OnTouchEventAck((*iter), ack_result); 463 client_->OnTouchEventAck((*iter), ack_result);
465 } 464 }
466 } 465 }
467 466
468 bool TouchEventQueue::ShouldForwardToRenderer( 467 bool TouchEventQueue::ShouldForwardToRenderer(
469 const WebTouchEvent& event) const { 468 const WebTouchEvent& event) const {
470 if (HasTimeoutEvent()) 469 if (HasTimeoutEvent())
471 return false; 470 return false;
472 471
473 if (!has_handlers_) 472 if (touch_handling_state_ == NO_TOUCH_HANDLER)
474 return false; 473 return false;
475 474
476 if (scroll_in_progress_ && 475 if (touch_handling_state_ == NO_TOUCH_HANDLER_FOR_GESTURE) {
477 event.type != blink::WebInputEvent::TouchCancel) 476 if (IsNewTouchGesture(event))
477 return true;
478 if (event.type == WebInputEvent::TouchCancel)
479 return true;
478 return false; 480 return false;
481 }
479 482
480 // Touch press events should always be forwarded to the renderer. 483 // Touch press events should always be forwarded to the renderer.
481 if (event.type == WebInputEvent::TouchStart) 484 if (event.type == WebInputEvent::TouchStart)
482 return true; 485 return true;
483 486
484 for (unsigned int i = 0; i < event.touchesLength; ++i) { 487 for (unsigned int i = 0; i < event.touchesLength; ++i) {
485 const WebTouchPoint& point = event.touches[i]; 488 const WebTouchPoint& point = event.touches[i];
486 // If a point has been stationary, then don't take it into account. 489 // If a point has been stationary, then don't take it into account.
487 if (point.state == WebTouchPoint::StateStationary) 490 if (point.state == WebTouchPoint::StateStationary)
488 continue; 491 continue;
(...skipping 27 matching lines...) Expand all
516 } else if (event.type == WebInputEvent::TouchStart) { 519 } else if (event.type == WebInputEvent::TouchStart) {
517 for (unsigned i = 0; i < event.touchesLength; ++i) { 520 for (unsigned i = 0; i < event.touchesLength; ++i) {
518 const WebTouchPoint& point = event.touches[i]; 521 const WebTouchPoint& point = event.touches[i];
519 if (point.state == WebTouchPoint::StatePressed) 522 if (point.state == WebTouchPoint::StatePressed)
520 touch_ack_states_[point.id] = ack_result; 523 touch_ack_states_[point.id] = ack_result;
521 } 524 }
522 } 525 }
523 } 526 }
524 527
525 } // namespace content 528 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698