1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/input/input_router_impl.h" 6 7 #include <math.h> 8 9 #include "base/auto_reset.h" 10 #include "base/command_line.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "content/browser/renderer_host/input/gesture_event_queue.h" 14 #include "content/browser/renderer_host/input/input_ack_handler.h" 15 #include "content/browser/renderer_host/input/input_router_client.h" 16 #include "content/browser/renderer_host/input/touch_event_queue.h" 17 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" 18 #include "content/common/content_constants_internal.h" 19 #include "content/common/edit_command.h" 20 #include "content/common/input/input_event_ack_state.h" 21 #include "content/common/input/touch_action.h" 22 #include "content/common/input/web_touch_event_traits.h" 23 #include "content/common/input_messages.h" 24 #include "content/common/view_messages.h" 25 #include "content/public/browser/notification_service.h" 26 #include "content/public/browser/notification_types.h" 27 #include "content/public/browser/user_metrics.h" 28 #include "content/public/common/content_switches.h" 29 #include "ipc/ipc_sender.h" 30 #include "ui/events/event.h" 31 #include "ui/events/keycodes/keyboard_codes.h" 32 33 using base::Time; 34 using base::TimeDelta; 35 using base::TimeTicks; 36 using blink::WebGestureEvent; 37 using blink::WebInputEvent; 38 using blink::WebKeyboardEvent; 39 using blink::WebMouseEvent; 40 using blink::WebMouseWheelEvent; 41 42 namespace content { 43 namespace { 44 45 const char* GetEventAckName(InputEventAckState ack_result) { 46 switch(ack_result) { 47 case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN"; 48 case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED"; 49 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED"; 50 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS"; 51 case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED"; 52 } 53 DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName."; 54 return ""; 55 } 56 57 } // namespace 58 59 InputRouterImpl::Config::Config() { 60 } 61 62 InputRouterImpl::InputRouterImpl(IPC::Sender* sender, 63 InputRouterClient* client, 64 InputAckHandler* ack_handler, 65 int routing_id, 66 const Config& config) 67 : sender_(sender), 68 client_(client), 69 ack_handler_(ack_handler), 70 routing_id_(routing_id), 71 select_range_pending_(false), 72 move_caret_pending_(false), 73 mouse_move_pending_(false), 74 mouse_wheel_pending_(false), 75 current_view_flags_(0), 76 current_ack_source_(ACK_SOURCE_NONE), 77 flush_requested_(false), 78 touch_event_queue_(this, config.touch_config), 79 gesture_event_queue_(this, this, config.gesture_config) { 80 DCHECK(sender); 81 DCHECK(client); 82 DCHECK(ack_handler); 83 UpdateTouchAckTimeoutEnabled(); 84 } 85 86 InputRouterImpl::~InputRouterImpl() {} 87 88 void InputRouterImpl::Flush() { 89 flush_requested_ = true; 90 SignalFlushedIfNecessary(); 91 } 92 93 bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) { 94 DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); 95 switch (message->type()) { 96 // Check for types that require an ACK. 97 case InputMsg_SelectRange::ID: 98 return SendSelectRange(message.Pass()); 99 case InputMsg_MoveCaret::ID: 100 return SendMoveCaret(message.Pass()); 101 case InputMsg_HandleInputEvent::ID: 102 NOTREACHED() << "WebInputEvents should never be sent via SendInput."; 103 return false; 104 default: 105 return Send(message.release()); 106 } 107 } 108 109 void InputRouterImpl::SendMouseEvent( 110 const MouseEventWithLatencyInfo& mouse_event) { 111 if (mouse_event.event.type == WebInputEvent::MouseDown && 112 gesture_event_queue_.GetTouchpadTapSuppressionController()-> 113 ShouldDeferMouseDown(mouse_event)) 114 return; 115 if (mouse_event.event.type == WebInputEvent::MouseUp && 116 gesture_event_queue_.GetTouchpadTapSuppressionController()-> 117 ShouldSuppressMouseUp()) 118 return; 119 120 SendMouseEventImmediately(mouse_event); 121 } 122 123 void InputRouterImpl::SendWheelEvent( 124 const MouseWheelEventWithLatencyInfo& wheel_event) { 125 SendWheelEvent(QueuedWheelEvent(wheel_event, false)); 126 } 127 128 void InputRouterImpl::SendWheelEvent(const QueuedWheelEvent& wheel_event) { 129 if (mouse_wheel_pending_) { 130 // If there's already a mouse wheel event waiting to be sent to the 131 // renderer, add the new deltas to that event. Not doing so (e.g., by 132 // dropping the old event, as for mouse moves) results in very slow 133 // scrolling on the Mac (on which many, very small wheel events are sent). 134 // Note that we can't coalesce wheel events for pinches because the GEQ 135 // expects one ACK for each (but it's fine to coalesce non-pinch wheels 136 // into a pinch one). Note that the GestureEventQueue ensures we only 137 // ever have a single pinch event queued here. 138 if (coalesced_mouse_wheel_events_.empty() || 139 wheel_event.synthesized_from_pinch || 140 !coalesced_mouse_wheel_events_.back().event.CanCoalesceWith( 141 wheel_event.event)) { 142 coalesced_mouse_wheel_events_.push_back(wheel_event); 143 } else { 144 coalesced_mouse_wheel_events_.back().event.CoalesceWith( 145 wheel_event.event); 146 } 147 return; 148 } 149 150 mouse_wheel_pending_ = true; 151 current_wheel_event_ = wheel_event; 152 153 HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", 154 coalesced_mouse_wheel_events_.size()); 155 156 FilterAndSendWebInputEvent( 157 wheel_event.event.event, wheel_event.event.latency, false); 158 } 159 160 void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, 161 const ui::LatencyInfo& latency_info, 162 bool is_keyboard_shortcut) { 163 // Put all WebKeyboardEvent objects in a queue since we can't trust the 164 // renderer and we need to give something to the HandleKeyboardEvent 165 // handler. 166 key_queue_.push_back(key_event); 167 HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); 168 169 gesture_event_queue_.FlingHasBeenHalted(); 170 171 // Only forward the non-native portions of our event. 172 FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut); 173 } 174 175 void InputRouterImpl::SendGestureEvent( 176 const GestureEventWithLatencyInfo& original_gesture_event) { 177 input_stream_validator_.Validate(original_gesture_event.event); 178 179 GestureEventWithLatencyInfo gesture_event(original_gesture_event); 180 181 if (touch_action_filter_.FilterGestureEvent(&gesture_event.event)) 182 return; 183 184 if (gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchscreen) 185 touch_event_queue_.OnGestureScrollEvent(gesture_event); 186 187 if (!gesture_event_queue_.ShouldForward(gesture_event)) 188 return; 189 190 SendGestureEventImmediately(gesture_event); 191 } 192 193 void InputRouterImpl::SendTouchEvent( 194 const TouchEventWithLatencyInfo& touch_event) { 195 input_stream_validator_.Validate(touch_event.event); 196 touch_event_queue_.QueueEvent(touch_event); 197 } 198 199 // Forwards MouseEvent without passing it through 200 // TouchpadTapSuppressionController. 201 void InputRouterImpl::SendMouseEventImmediately( 202 const MouseEventWithLatencyInfo& mouse_event) { 203 // Avoid spamming the renderer with mouse move events. It is important 204 // to note that WM_MOUSEMOVE events are anyways synthetic, but since our 205 // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way 206 // more WM_MOUSEMOVE events than we wish to send to the renderer. 207 if (mouse_event.event.type == WebInputEvent::MouseMove) { 208 if (mouse_move_pending_) { 209 if (!next_mouse_move_) 210 next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event)); 211 else 212 next_mouse_move_->CoalesceWith(mouse_event); 213 return; 214 } 215 mouse_move_pending_ = true; 216 } 217 218 FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false); 219 } 220 221 void InputRouterImpl::SendTouchEventImmediately( 222 const TouchEventWithLatencyInfo& touch_event) { 223 if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) { 224 touch_action_filter_.ResetTouchAction(); 225 // Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling 226 // the timeout here will not take effect until the *following* touch 227 // sequence. This is a desirable side-effect, giving the renderer a chance 228 // to send a touch-action response without racing against the ack timeout. 229 UpdateTouchAckTimeoutEnabled(); 230 } 231 232 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); 233 } 234 235 void InputRouterImpl::SendGestureEventImmediately( 236 const GestureEventWithLatencyInfo& gesture_event) { 237 if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate && 238 gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchpad) { 239 SendSyntheticWheelEventForPinch(gesture_event); 240 return; 241 } 242 243 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 244 } 245 246 const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const { 247 if (key_queue_.empty()) 248 return NULL; 249 return &key_queue_.front(); 250 } 251 252 bool InputRouterImpl::ShouldForwardTouchEvent() const { 253 // Always send a touch event if the renderer has a touch-event handler or 254 // there are pending touch events. 255 return touch_event_queue_.has_handlers() || !touch_event_queue_.empty(); 256 } 257 258 void InputRouterImpl::OnViewUpdated(int view_flags) { 259 current_view_flags_ = view_flags; 260 261 // A fixed page scale or mobile viewport should disable the touch ack timeout. 262 UpdateTouchAckTimeoutEnabled(); 263 } 264 265 bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) { 266 bool handled = true; 267 IPC_BEGIN_MESSAGE_MAP(InputRouterImpl, message) 268 IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck) 269 IPC_MESSAGE_HANDLER(InputHostMsg_DidOverscroll, OnDidOverscroll) 270 IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck) 271 IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck) 272 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, 273 OnHasTouchEventHandlers) 274 IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction, 275 OnSetTouchAction) 276 IPC_MESSAGE_UNHANDLED(handled = false) 277 IPC_END_MESSAGE_MAP() 278 279 return handled; 280 } 281 282 void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event, 283 InputEventAckState ack_result) { 284 // Touchstart events sent to the renderer indicate a new touch sequence, but 285 // in some cases we may filter out sending the touchstart - catch those here. 286 if (WebTouchEventTraits::IsTouchSequenceStart(event.event) && 287 ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) { 288 touch_action_filter_.ResetTouchAction(); 289 UpdateTouchAckTimeoutEnabled(); 290 } 291 ack_handler_->OnTouchEventAck(event, ack_result); 292 } 293 294 void InputRouterImpl::OnGestureEventAck( 295 const GestureEventWithLatencyInfo& event, 296 InputEventAckState ack_result) { 297 touch_event_queue_.OnGestureEventAck(event, ack_result); 298 ack_handler_->OnGestureEventAck(event, ack_result); 299 } 300 301 bool InputRouterImpl::SendSelectRange(scoped_ptr<IPC::Message> message) { 302 DCHECK(message->type() == InputMsg_SelectRange::ID); 303 if (select_range_pending_) { 304 next_selection_range_ = message.Pass(); 305 return true; 306 } 307 308 select_range_pending_ = true; 309 return Send(message.release()); 310 } 311 312 bool InputRouterImpl::SendMoveCaret(scoped_ptr<IPC::Message> message) { 313 DCHECK(message->type() == InputMsg_MoveCaret::ID); 314 if (move_caret_pending_) { 315 next_move_caret_ = message.Pass(); 316 return true; 317 } 318 319 move_caret_pending_ = true; 320 return Send(message.release()); 321 } 322 323 bool InputRouterImpl::Send(IPC::Message* message) { 324 return sender_->Send(message); 325 } 326 327 void InputRouterImpl::FilterAndSendWebInputEvent( 328 const WebInputEvent& input_event, 329 const ui::LatencyInfo& latency_info, 330 bool is_keyboard_shortcut) { 331 TRACE_EVENT1("input", 332 "InputRouterImpl::FilterAndSendWebInputEvent", 333 "type", 334 WebInputEventTraits::GetName(input_event.type)); 335 336 // Any input event cancels a pending mouse move event. 337 next_mouse_move_.reset(); 338 339 OfferToHandlers(input_event, latency_info, is_keyboard_shortcut); 340 } 341 342 void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, 343 const ui::LatencyInfo& latency_info, 344 bool is_keyboard_shortcut) { 345 output_stream_validator_.Validate(input_event); 346 347 if (OfferToClient(input_event, latency_info)) 348 return; 349 350 OfferToRenderer(input_event, latency_info, is_keyboard_shortcut); 351 352 // Touch events should always indicate in the event whether they are 353 // cancelable (respect ACK disposition) or not. 354 bool ignores_ack = WebInputEventTraits::IgnoresAckDisposition(input_event); 355 if (WebInputEvent::isTouchEventType(input_event.type)) { 356 DCHECK(!ignores_ack == 357 static_cast<const blink::WebTouchEvent&>(input_event).cancelable); 358 } 359 360 // If we don't care about the ack disposition, send the ack immediately. 361 if (ignores_ack) { 362 ProcessInputEventAck(input_event.type, 363 INPUT_EVENT_ACK_STATE_IGNORED, 364 latency_info, 365 IGNORING_DISPOSITION); 366 } 367 } 368 369 bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event, 370 const ui::LatencyInfo& latency_info) { 371 bool consumed = false; 372 373 InputEventAckState filter_ack = 374 client_->FilterInputEvent(input_event, latency_info); 375 switch (filter_ack) { 376 case INPUT_EVENT_ACK_STATE_CONSUMED: 377 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: 378 // Send the ACK and early exit. 379 next_mouse_move_.reset(); 380 ProcessInputEventAck(input_event.type, filter_ack, latency_info, CLIENT); 381 // WARNING: |this| may be deleted at this point. 382 consumed = true; 383 break; 384 case INPUT_EVENT_ACK_STATE_UNKNOWN: 385 // Simply drop the event. 386 consumed = true; 387 break; 388 default: 389 break; 390 } 391 392 return consumed; 393 } 394 395 bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, 396 const ui::LatencyInfo& latency_info, 397 bool is_keyboard_shortcut) { 398 if (Send(new InputMsg_HandleInputEvent( 399 routing_id(), &input_event, latency_info, is_keyboard_shortcut))) { 400 // Ack messages for ignored ack event types should never be sent by the 401 // renderer. Consequently, such event types should not affect event time 402 // or in-flight event count metrics. 403 if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) { 404 input_event_start_time_ = TimeTicks::Now(); 405 client_->IncrementInFlightEventCount(); 406 } 407 return true; 408 } 409 return false; 410 } 411 412 void InputRouterImpl::SendSyntheticWheelEventForPinch( 413 const GestureEventWithLatencyInfo& pinch_event) { 414 // We match typical trackpad behavior on Windows by sending fake wheel events 415 // with the ctrl modifier set when we see trackpad pinch gestures. Ideally 416 // we'd someday get a standard 'pinch' event and send that instead. 417 418 WebMouseWheelEvent wheelEvent; 419 wheelEvent.type = WebInputEvent::MouseWheel; 420 wheelEvent.timeStampSeconds = pinch_event.event.timeStampSeconds; 421 wheelEvent.windowX = wheelEvent.x = pinch_event.event.x; 422 wheelEvent.windowY = wheelEvent.y = pinch_event.event.y; 423 wheelEvent.globalX = pinch_event.event.globalX; 424 wheelEvent.globalY = pinch_event.event.globalY; 425 wheelEvent.modifiers = 426 pinch_event.event.modifiers | WebInputEvent::ControlKey; 427 wheelEvent.deltaX = 0; 428 // The function to convert scales to deltaY values is designed to be 429 // compatible with websites existing use of wheel events, and with existing 430 // Windows trackpad behavior. In particular, we want: 431 // - deltas should accumulate via addition: f(s1*s2)==f(s1)+f(s2) 432 // - deltas should invert via negation: f(1/s) == -f(s) 433 // - zoom in should be positive: f(s) > 0 iff s > 1 434 // - magnitude roughly matches wheels: f(2) > 25 && f(2) < 100 435 // - a formula that's relatively easy to use from JavaScript 436 // Note that 'wheel' event deltaY values have their sign inverted. So to 437 // convert a wheel deltaY back to a scale use Math.exp(-deltaY/100). 438 DCHECK_GT(pinch_event.event.data.pinchUpdate.scale, 0); 439 wheelEvent.deltaY = 100.0f * log(pinch_event.event.data.pinchUpdate.scale); 440 wheelEvent.hasPreciseScrollingDeltas = true; 441 wheelEvent.wheelTicksX = 0; 442 wheelEvent.wheelTicksY = 443 pinch_event.event.data.pinchUpdate.scale > 1 ? 1 : -1; 444 445 SendWheelEvent(QueuedWheelEvent( 446 MouseWheelEventWithLatencyInfo(wheelEvent, pinch_event.latency), true)); 447 } 448 449 void InputRouterImpl::OnInputEventAck( 450 const InputHostMsg_HandleInputEvent_ACK_Params& ack) { 451 client_->DecrementInFlightEventCount(); 452 453 // Log the time delta for processing an input event. 454 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; 455 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); 456 457 if (ack.overscroll) { 458 DCHECK(ack.type == WebInputEvent::MouseWheel || 459 ack.type == WebInputEvent::GestureScrollUpdate); 460 OnDidOverscroll(*ack.overscroll); 461 } 462 463 ProcessInputEventAck(ack.type, ack.state, ack.latency, RENDERER); 464 // WARNING: |this| may be deleted at this point. 465 466 // This is used only for testing, and the other end does not use the 467 // source object. On linux, specifying 468 // Source<RenderWidgetHost> results in a very strange 469 // runtime error in the epilogue of the enclosing 470 // (ProcessInputEventAck) method, but not on other platforms; using 471 // 'void' instead is just as safe (since NotificationSource 472 // is not actually typesafe) and avoids this error. 473 int type = static_cast<int>(ack.type); 474 NotificationService::current()->Notify( 475 NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK, 476 Source<void>(this), 477 Details<int>(&type)); 478 } 479 480 void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) { 481 client_->DidOverscroll(params); 482 } 483 484 void InputRouterImpl::OnMsgMoveCaretAck() { 485 move_caret_pending_ = false; 486 if (next_move_caret_) 487 SendMoveCaret(next_move_caret_.Pass()); 488 } 489 490 void InputRouterImpl::OnSelectRangeAck() { 491 select_range_pending_ = false; 492 if (next_selection_range_) 493 SendSelectRange(next_selection_range_.Pass()); 494 } 495 496 void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) { 497 TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers", 498 "has_handlers", has_handlers); 499 500 // Lack of a touch handler indicates that the page either has no touch-action 501 // modifiers or that all its touch-action modifiers are auto. Resetting the 502 // touch-action here allows forwarding of subsequent gestures even if the 503 // underlying touches never reach the router. 504 // TODO(jdduke): Reset touch-action only at the end of a touch sequence to 505 // prevent potentially strange mid-sequence behavior, crbug.com/375940. 506 if (!has_handlers) 507 touch_action_filter_.ResetTouchAction(); 508 509 touch_event_queue_.OnHasTouchEventHandlers(has_handlers); 510 client_->OnHasTouchEventHandlers(has_handlers); 511 } 512 513 void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) { 514 // Synthetic touchstart events should get filtered out in RenderWidget. 515 DCHECK(touch_event_queue_.IsPendingAckTouchStart()); 516 TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction", 517 "action", touch_action); 518 519 touch_action_filter_.OnSetTouchAction(touch_action); 520 521 // TOUCH_ACTION_NONE should disable the touch ack timeout. 522 UpdateTouchAckTimeoutEnabled(); 523 } 524 525 void InputRouterImpl::ProcessInputEventAck( 526 WebInputEvent::Type event_type, 527 InputEventAckState ack_result, 528 const ui::LatencyInfo& latency_info, 529 AckSource ack_source) { 530 TRACE_EVENT2("input", "InputRouterImpl::ProcessInputEventAck", 531 "type", WebInputEventTraits::GetName(event_type), 532 "ack", GetEventAckName(ack_result)); 533 534 // Note: The keyboard ack must be treated carefully, as it may result in 535 // synchronous destruction of |this|. Handling immediately guards against 536 // future references to |this|, as with |auto_reset_current_ack_source| below. 537 if (WebInputEvent::isKeyboardEventType(event_type)) { 538 ProcessKeyboardAck(event_type, ack_result); 539 // WARNING: |this| may be deleted at this point. 540 return; 541 } 542 543 base::AutoReset<AckSource> auto_reset_current_ack_source( 544 ¤t_ack_source_, ack_source); 545 546 if (WebInputEvent::isMouseEventType(event_type)) { 547 ProcessMouseAck(event_type, ack_result); 548 } else if (event_type == WebInputEvent::MouseWheel) { 549 ProcessWheelAck(ack_result, latency_info); 550 } else if (WebInputEvent::isTouchEventType(event_type)) { 551 ProcessTouchAck(ack_result, latency_info); 552 } else if (WebInputEvent::isGestureEventType(event_type)) { 553 ProcessGestureAck(event_type, ack_result, latency_info); 554 } else if (event_type != WebInputEvent::Undefined) { 555 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); 556 } 557 558 SignalFlushedIfNecessary(); 559 } 560 561 void InputRouterImpl::ProcessKeyboardAck(blink::WebInputEvent::Type type, 562 InputEventAckState ack_result) { 563 if (key_queue_.empty()) { 564 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); 565 } else if (key_queue_.front().type != type) { 566 // Something must be wrong. Clear the |key_queue_| and char event 567 // suppression so that we can resume from the error. 568 key_queue_.clear(); 569 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE); 570 } else { 571 NativeWebKeyboardEvent front_item = key_queue_.front(); 572 key_queue_.pop_front(); 573 574 ack_handler_->OnKeyboardEventAck(front_item, ack_result); 575 // WARNING: This InputRouterImpl can be deallocated at this point 576 // (i.e. in the case of Ctrl+W, where the call to 577 // HandleKeyboardEvent destroys this InputRouterImpl). 578 // TODO(jdduke): crbug.com/274029 - Make ack-triggered shutdown async. 579 } 580 } 581 582 void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type, 583 InputEventAckState ack_result) { 584 if (type != WebInputEvent::MouseMove) 585 return; 586 587 DCHECK(mouse_move_pending_); 588 mouse_move_pending_ = false; 589 590 if (next_mouse_move_) { 591 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); 592 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move 593 = next_mouse_move_.Pass(); 594 SendMouseEvent(*next_mouse_move); 595 } 596 } 597 598 void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result, 599 const ui::LatencyInfo& latency) { 600 // TODO(miletus): Add renderer side latency to each uncoalesced mouse 601 // wheel event and add terminal component to each of them. 602 current_wheel_event_.event.latency.AddNewLatencyFrom(latency); 603 604 if (current_wheel_event_.synthesized_from_pinch) { 605 // Ack the GesturePinchUpdate event that generated this wheel event. 606 ProcessInputEventAck(WebInputEvent::GesturePinchUpdate, 607 ack_result, 608 current_wheel_event_.event.latency, 609 current_ack_source_); 610 } else { 611 // Process the unhandled wheel event here before calling SendWheelEvent() 612 // since it will mutate current_wheel_event_. 613 ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result); 614 } 615 616 // Mark the wheel event complete only after the ACKs have been handled above. 617 // For example, ACKing the GesturePinchUpdate could cause another 618 // GesturePinchUpdate to be sent, which should queue a wheel event rather than 619 // send it immediately. 620 mouse_wheel_pending_ = false; 621 622 // Send the next (coalesced or synthetic) mouse wheel event. 623 if (!coalesced_mouse_wheel_events_.empty()) { 624 QueuedWheelEvent next_wheel_event = coalesced_mouse_wheel_events_.front(); 625 coalesced_mouse_wheel_events_.pop_front(); 626 SendWheelEvent(next_wheel_event); 627 } 628 } 629 630 void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type, 631 InputEventAckState ack_result, 632 const ui::LatencyInfo& latency) { 633 if (!gesture_event_queue_.ExpectingGestureAck()) 634 return; 635 636 // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate. 637 gesture_event_queue_.ProcessGestureAck(ack_result, type, latency); 638 } 639 640 void InputRouterImpl::ProcessTouchAck( 641 InputEventAckState ack_result, 642 const ui::LatencyInfo& latency) { 643 // |touch_event_queue_| will forward to OnTouchEventAck when appropriate. 644 touch_event_queue_.ProcessTouchAck(ack_result, latency); 645 } 646 647 void InputRouterImpl::UpdateTouchAckTimeoutEnabled() { 648 // Mobile sites tend to be well-behaved with respect to touch handling, so 649 // they have less need for the touch timeout fallback. 650 const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0; 651 const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0; 652 653 // TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves 654 // little purpose. It's also a strong signal that touch handling is critical 655 // to page functionality, so the timeout could do more harm than good. 656 const bool touch_action_none = 657 touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE; 658 659 const bool touch_ack_timeout_enabled = !fixed_page_scale && 660 !mobile_viewport && 661 !touch_action_none; 662 touch_event_queue_.SetAckTimeoutEnabled(touch_ack_timeout_enabled); 663 } 664 665 void InputRouterImpl::SignalFlushedIfNecessary() { 666 if (!flush_requested_) 667 return; 668 669 if (HasPendingEvents()) 670 return; 671 672 flush_requested_ = false; 673 client_->DidFlush(); 674 } 675 676 bool InputRouterImpl::HasPendingEvents() const { 677 return !touch_event_queue_.empty() || 678 !gesture_event_queue_.empty() || 679 !key_queue_.empty() || 680 mouse_move_pending_ || 681 mouse_wheel_pending_ || 682 select_range_pending_ || 683 move_caret_pending_; 684 } 685 686 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent() 687 : synthesized_from_pinch(false) { 688 } 689 690 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent( 691 const MouseWheelEventWithLatencyInfo& event, 692 bool synthesized_from_pinch) 693 : event(event), synthesized_from_pinch(synthesized_from_pinch) { 694 } 695 696 InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() { 697 } 698 699 } // namespace content 700