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/immediate_input_router.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/histogram.h" 9 #include "content/browser/renderer_host/input/gesture_event_filter.h" 10 #include "content/browser/renderer_host/input/input_router_client.h" 11 #include "content/browser/renderer_host/input/touch_event_queue.h" 12 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" 13 #include "content/browser/renderer_host/render_process_host_impl.h" 14 #include "content/common/content_constants_internal.h" 15 #include "content/common/edit_command.h" 16 #include "content/common/input_messages.h" 17 #include "content/common/view_messages.h" 18 #include "content/port/common/input_event_ack_state.h" 19 #include "content/public/browser/notification_service.h" 20 #include "content/public/browser/notification_types.h" 21 #include "content/public/browser/user_metrics.h" 22 #include "content/public/common/content_switches.h" 23 #include "ui/base/events/event.h" 24 #include "ui/base/keycodes/keyboard_codes.h" 25 26 using base::Time; 27 using base::TimeDelta; 28 using base::TimeTicks; 29 using WebKit::WebGestureEvent; 30 using WebKit::WebInputEvent; 31 using WebKit::WebKeyboardEvent; 32 using WebKit::WebMouseEvent; 33 using WebKit::WebMouseWheelEvent; 34 35 namespace content { 36 namespace { 37 38 // Returns |true| if the two wheel events should be coalesced. 39 bool ShouldCoalesceMouseWheelEvents(const WebMouseWheelEvent& last_event, 40 const WebMouseWheelEvent& new_event) { 41 return last_event.modifiers == new_event.modifiers && 42 last_event.scrollByPage == new_event.scrollByPage && 43 last_event.hasPreciseScrollingDeltas 44 == new_event.hasPreciseScrollingDeltas && 45 last_event.phase == new_event.phase && 46 last_event.momentumPhase == new_event.momentumPhase; 47 } 48 49 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) { 50 return accelerated_delta * acceleration_ratio; 51 } 52 53 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) { 54 if (unaccelerated_delta == 0.f || accelerated_delta == 0.f) 55 return 1.f; 56 return unaccelerated_delta / accelerated_delta; 57 } 58 59 const char* GetEventAckName(InputEventAckState ack_result) { 60 switch(ack_result) { 61 case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN"; 62 case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED"; 63 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED"; 64 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS"; 65 default: 66 DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName.\n"; 67 break; 68 } 69 return ""; 70 } 71 72 } // namespace 73 74 ImmediateInputRouter::ImmediateInputRouter( 75 RenderProcessHost* process, 76 InputRouterClient* client, 77 int routing_id) 78 : process_(process), 79 client_(client), 80 routing_id_(routing_id), 81 select_range_pending_(false), 82 move_caret_pending_(false), 83 mouse_move_pending_(false), 84 mouse_wheel_pending_(false), 85 has_touch_handler_(false), 86 touch_event_queue_(new TouchEventQueue(this)), 87 gesture_event_filter_(new GestureEventFilter(this)) { 88 DCHECK(process); 89 DCHECK(client); 90 } 91 92 ImmediateInputRouter::~ImmediateInputRouter() { 93 } 94 95 bool ImmediateInputRouter::SendInput(IPC::Message* message) { 96 DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); 97 scoped_ptr<IPC::Message> scoped_message(message); 98 switch (scoped_message->type()) { 99 // Check for types that require an ACK. 100 case InputMsg_SelectRange::ID: 101 return SendSelectRange(scoped_message.release()); 102 case InputMsg_MoveCaret::ID: 103 return SendMoveCaret(scoped_message.release()); 104 case InputMsg_HandleInputEvent::ID: 105 NOTREACHED() << "WebInputEvents should never be sent via SendInput."; 106 return false; 107 default: 108 return Send(scoped_message.release()); 109 } 110 } 111 112 void ImmediateInputRouter::SendMouseEvent( 113 const MouseEventWithLatencyInfo& mouse_event) { 114 if (!client_->OnSendMouseEvent(mouse_event)) 115 return; 116 117 if (mouse_event.event.type == WebInputEvent::MouseDown && 118 gesture_event_filter_->GetTouchpadTapSuppressionController()-> 119 ShouldDeferMouseDown(mouse_event)) 120 return; 121 if (mouse_event.event.type == WebInputEvent::MouseUp && 122 gesture_event_filter_->GetTouchpadTapSuppressionController()-> 123 ShouldSuppressMouseUp()) 124 return; 125 126 SendMouseEventImmediately(mouse_event); 127 } 128 129 void ImmediateInputRouter::SendWheelEvent( 130 const MouseWheelEventWithLatencyInfo& wheel_event) { 131 if (!client_->OnSendWheelEvent(wheel_event)) 132 return; 133 134 // If there's already a mouse wheel event waiting to be sent to the renderer, 135 // add the new deltas to that event. Not doing so (e.g., by dropping the old 136 // event, as for mouse moves) results in very slow scrolling on the Mac (on 137 // which many, very small wheel events are sent). 138 if (mouse_wheel_pending_) { 139 if (coalesced_mouse_wheel_events_.empty() || 140 !ShouldCoalesceMouseWheelEvents( 141 coalesced_mouse_wheel_events_.back().event, wheel_event.event)) { 142 coalesced_mouse_wheel_events_.push_back(wheel_event); 143 } else { 144 MouseWheelEventWithLatencyInfo* last_wheel_event = 145 &coalesced_mouse_wheel_events_.back(); 146 float unaccelerated_x = 147 GetUnacceleratedDelta(last_wheel_event->event.deltaX, 148 last_wheel_event->event.accelerationRatioX) + 149 GetUnacceleratedDelta(wheel_event.event.deltaX, 150 wheel_event.event.accelerationRatioX); 151 float unaccelerated_y = 152 GetUnacceleratedDelta(last_wheel_event->event.deltaY, 153 last_wheel_event->event.accelerationRatioY) + 154 GetUnacceleratedDelta(wheel_event.event.deltaY, 155 wheel_event.event.accelerationRatioY); 156 last_wheel_event->event.deltaX += wheel_event.event.deltaX; 157 last_wheel_event->event.deltaY += wheel_event.event.deltaY; 158 last_wheel_event->event.wheelTicksX += wheel_event.event.wheelTicksX; 159 last_wheel_event->event.wheelTicksY += wheel_event.event.wheelTicksY; 160 last_wheel_event->event.accelerationRatioX = 161 GetAccelerationRatio(last_wheel_event->event.deltaX, unaccelerated_x); 162 last_wheel_event->event.accelerationRatioY = 163 GetAccelerationRatio(last_wheel_event->event.deltaY, unaccelerated_y); 164 DCHECK_GE(wheel_event.event.timeStampSeconds, 165 last_wheel_event->event.timeStampSeconds); 166 last_wheel_event->event.timeStampSeconds = 167 wheel_event.event.timeStampSeconds; 168 last_wheel_event->latency.MergeWith(wheel_event.latency); 169 } 170 return; 171 } 172 mouse_wheel_pending_ = true; 173 current_wheel_event_ = wheel_event; 174 175 HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", 176 coalesced_mouse_wheel_events_.size()); 177 178 FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false); 179 } 180 181 void ImmediateInputRouter::SendKeyboardEvent( 182 const NativeWebKeyboardEvent& key_event, 183 const ui::LatencyInfo& latency_info) { 184 bool is_shortcut = false; 185 if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut)) 186 return; 187 188 // Put all WebKeyboardEvent objects in a queue since we can't trust the 189 // renderer and we need to give something to the HandleKeyboardEvent 190 // handler. 191 key_queue_.push_back(key_event); 192 HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); 193 194 gesture_event_filter_->FlingHasBeenHalted(); 195 196 // Only forward the non-native portions of our event. 197 FilterAndSendWebInputEvent(key_event, latency_info, is_shortcut); 198 } 199 200 void ImmediateInputRouter::SendGestureEvent( 201 const GestureEventWithLatencyInfo& gesture_event) { 202 if (!client_->OnSendGestureEvent(gesture_event)) 203 return; 204 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 205 } 206 207 void ImmediateInputRouter::SendTouchEvent( 208 const TouchEventWithLatencyInfo& touch_event) { 209 // Always queue TouchEvents, even if the client request they be dropped. 210 client_->OnSendTouchEvent(touch_event); 211 touch_event_queue_->QueueEvent(touch_event); 212 } 213 214 // Forwards MouseEvent without passing it through 215 // TouchpadTapSuppressionController. 216 void ImmediateInputRouter::SendMouseEventImmediately( 217 const MouseEventWithLatencyInfo& mouse_event) { 218 if (!client_->OnSendMouseEventImmediately(mouse_event)) 219 return; 220 221 // Avoid spamming the renderer with mouse move events. It is important 222 // to note that WM_MOUSEMOVE events are anyways synthetic, but since our 223 // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way 224 // more WM_MOUSEMOVE events than we wish to send to the renderer. 225 if (mouse_event.event.type == WebInputEvent::MouseMove) { 226 if (mouse_move_pending_) { 227 if (!next_mouse_move_) { 228 next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event)); 229 } else { 230 // Accumulate movement deltas. 231 int x = next_mouse_move_->event.movementX; 232 int y = next_mouse_move_->event.movementY; 233 next_mouse_move_->event = mouse_event.event; 234 next_mouse_move_->event.movementX += x; 235 next_mouse_move_->event.movementY += y; 236 next_mouse_move_->latency.MergeWith(mouse_event.latency); 237 } 238 return; 239 } 240 mouse_move_pending_ = true; 241 } 242 243 FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false); 244 } 245 246 void ImmediateInputRouter::SendTouchEventImmediately( 247 const TouchEventWithLatencyInfo& touch_event) { 248 if (!client_->OnSendTouchEventImmediately(touch_event)) 249 return; 250 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); 251 } 252 253 void ImmediateInputRouter::SendGestureEventImmediately( 254 const GestureEventWithLatencyInfo& gesture_event) { 255 if (!client_->OnSendGestureEventImmediately(gesture_event)) 256 return; 257 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 258 } 259 260 const NativeWebKeyboardEvent* 261 ImmediateInputRouter::GetLastKeyboardEvent() const { 262 if (key_queue_.empty()) 263 return NULL; 264 return &key_queue_.front(); 265 } 266 267 bool ImmediateInputRouter::ShouldForwardTouchEvent() const { 268 // Always send a touch event if the renderer has a touch-event handler. It is 269 // possible that a renderer stops listening to touch-events while there are 270 // still events in the touch-queue. In such cases, the new events should still 271 // get into the queue. 272 return has_touch_handler_ || !touch_event_queue_->empty(); 273 } 274 275 bool ImmediateInputRouter::ShouldForwardGestureEvent( 276 const GestureEventWithLatencyInfo& touch_event) const { 277 return gesture_event_filter_->ShouldForward(touch_event); 278 } 279 280 bool ImmediateInputRouter::HasQueuedGestureEvents() const { 281 return gesture_event_filter_->HasQueuedGestureEvents(); 282 } 283 284 bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) { 285 bool handled = true; 286 bool message_is_ok = true; 287 IPC_BEGIN_MESSAGE_MAP_EX(ImmediateInputRouter, message, message_is_ok) 288 IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck) 289 IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck) 290 IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck) 291 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, 292 OnHasTouchEventHandlers) 293 IPC_MESSAGE_UNHANDLED(handled = false) 294 IPC_END_MESSAGE_MAP() 295 296 if (!message_is_ok) 297 client_->OnUnexpectedEventAck(true); 298 299 return handled; 300 } 301 302 void ImmediateInputRouter::OnTouchEventAck( 303 const TouchEventWithLatencyInfo& event, 304 InputEventAckState ack_result) { 305 client_->OnTouchEventAck(event, ack_result); 306 } 307 308 bool ImmediateInputRouter::SendSelectRange(IPC::Message* message) { 309 DCHECK(message->type() == InputMsg_SelectRange::ID); 310 if (select_range_pending_) { 311 next_selection_range_.reset(message); 312 return true; 313 } 314 315 select_range_pending_ = true; 316 return Send(message); 317 } 318 319 bool ImmediateInputRouter::SendMoveCaret(IPC::Message* message) { 320 DCHECK(message->type() == InputMsg_MoveCaret::ID); 321 if (move_caret_pending_) { 322 next_move_caret_.reset(message); 323 return true; 324 } 325 326 move_caret_pending_ = true; 327 return Send(message); 328 } 329 330 bool ImmediateInputRouter::Send(IPC::Message* message) { 331 return process_->Send(message); 332 } 333 334 void ImmediateInputRouter::SendWebInputEvent( 335 const WebInputEvent& input_event, 336 const ui::LatencyInfo& latency_info, 337 bool is_keyboard_shortcut) { 338 input_event_start_time_ = TimeTicks::Now(); 339 Send(new InputMsg_HandleInputEvent( 340 routing_id(), &input_event, latency_info, is_keyboard_shortcut)); 341 client_->IncrementInFlightEventCount(); 342 } 343 344 void ImmediateInputRouter::FilterAndSendWebInputEvent( 345 const WebInputEvent& input_event, 346 const ui::LatencyInfo& latency_info, 347 bool is_keyboard_shortcut) { 348 TRACE_EVENT0("input", "ImmediateInputRouter::FilterAndSendWebInputEvent"); 349 350 if (!process_->HasConnection()) 351 return; 352 353 DCHECK(!process_->IgnoreInputEvents()); 354 355 // Perform optional, synchronous event handling, sending ACK messages for 356 // processed events, or proceeding as usual. 357 InputEventAckState filter_ack = client_->FilterInputEvent(input_event, 358 latency_info); 359 switch (filter_ack) { 360 // Send the ACK and early exit. 361 case INPUT_EVENT_ACK_STATE_CONSUMED: 362 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: 363 next_mouse_move_.reset(); 364 ProcessInputEventAck(input_event.type, filter_ack, latency_info); 365 // WARNING: |this| may be deleted at this point. 366 return; 367 368 case INPUT_EVENT_ACK_STATE_UNKNOWN: { 369 if (input_event.type == WebKit::WebInputEvent::MouseMove) { 370 // Since this mouse-move event has been consumed, there will be no ACKs. 371 // So reset the state here so that future mouse-move events do reach the 372 // renderer. 373 mouse_move_pending_ = false; 374 } else if (input_event.type == WebKit::WebInputEvent::MouseWheel) { 375 // Reset the wheel-event state when appropriate. 376 mouse_wheel_pending_ = false; 377 } else if (WebInputEvent::isGestureEventType(input_event.type) && 378 gesture_event_filter_->HasQueuedGestureEvents()) { 379 // If the gesture-event filter has queued gesture events, that implies 380 // it's awaiting an ack for the event. Since the event is being dropped, 381 // it is never sent to the renderer, and so it won't receive any ACKs. 382 // So send the ACK to the gesture event filter immediately, and mark it 383 // as having been processed. 384 gesture_event_filter_->ProcessGestureAck(true, input_event.type); 385 } else if (WebInputEvent::isTouchEventType(input_event.type)) { 386 // During an overscroll gesture initiated by touch-scrolling, the 387 // touch-events do not reset or contribute to the overscroll gesture. 388 // However, the touch-events are not sent to the renderer. So send an 389 // ACK to the touch-event queue immediately. Mark the event as not 390 // processed, to make sure that the touch-scroll gesture that initiated 391 // the overscroll is updated properly. 392 touch_event_queue_->ProcessTouchAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, 393 latency_info); 394 } 395 return; 396 } 397 398 // Proceed as normal. 399 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: 400 break; 401 }; 402 403 // Transmit any pending wheel events on a non-wheel event. This ensures that 404 // the renderer receives the final PhaseEnded wheel event, which is necessary 405 // to terminate rubber-banding, for example. 406 if (input_event.type != WebInputEvent::MouseWheel) { 407 for (size_t i = 0; i < coalesced_mouse_wheel_events_.size(); ++i) { 408 SendWebInputEvent(coalesced_mouse_wheel_events_[i].event, 409 coalesced_mouse_wheel_events_[i].latency, 410 false); 411 } 412 coalesced_mouse_wheel_events_.clear(); 413 } 414 415 SendWebInputEvent(input_event, latency_info, is_keyboard_shortcut); 416 417 // Any input event cancels a pending mouse move event. 418 next_mouse_move_.reset(); 419 } 420 421 void ImmediateInputRouter::OnInputEventAck( 422 WebInputEvent::Type event_type, 423 InputEventAckState ack_result, 424 const ui::LatencyInfo& latency_info) { 425 // Log the time delta for processing an input event. 426 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; 427 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); 428 429 client_->DecrementInFlightEventCount(); 430 431 ProcessInputEventAck(event_type, ack_result, latency_info); 432 } 433 434 void ImmediateInputRouter::OnMsgMoveCaretAck() { 435 move_caret_pending_ = false; 436 if (next_move_caret_) 437 SendMoveCaret(next_move_caret_.release()); 438 } 439 440 void ImmediateInputRouter::OnSelectRangeAck() { 441 select_range_pending_ = false; 442 if (next_selection_range_) 443 SendSelectRange(next_selection_range_.release()); 444 } 445 446 void ImmediateInputRouter::OnHasTouchEventHandlers(bool has_handlers) { 447 if (has_touch_handler_ == has_handlers) 448 return; 449 has_touch_handler_ = has_handlers; 450 if (!has_handlers) 451 touch_event_queue_->FlushQueue(); 452 client_->OnHasTouchEventHandlers(has_handlers); 453 } 454 455 void ImmediateInputRouter::ProcessInputEventAck( 456 WebInputEvent::Type event_type, 457 InputEventAckState ack_result, 458 const ui::LatencyInfo& latency_info) { 459 TRACE_EVENT1("input", "ImmediateInputRouter::ProcessInputEventAck", 460 "ack", GetEventAckName(ack_result)); 461 462 int type = static_cast<int>(event_type); 463 if (type < WebInputEvent::Undefined) { 464 client_->OnUnexpectedEventAck(true); 465 } else if (type == WebInputEvent::MouseMove) { 466 mouse_move_pending_ = false; 467 468 // now, we can send the next mouse move event 469 if (next_mouse_move_) { 470 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); 471 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move 472 = next_mouse_move_.Pass(); 473 SendMouseEvent(*next_mouse_move); 474 } 475 } else if (WebInputEvent::isKeyboardEventType(type)) { 476 ProcessKeyboardAck(type, ack_result); 477 } else if (type == WebInputEvent::MouseWheel) { 478 ProcessWheelAck(ack_result); 479 } else if (WebInputEvent::isTouchEventType(type)) { 480 ProcessTouchAck(ack_result, latency_info); 481 } else if (WebInputEvent::isGestureEventType(type)) { 482 ProcessGestureAck(type, ack_result); 483 } 484 485 // WARNING: |this| may be deleted at this point. 486 487 // This is used only for testing, and the other end does not use the 488 // source object. On linux, specifying 489 // Source<RenderWidgetHost> results in a very strange 490 // runtime error in the epilogue of the enclosing 491 // (ProcessInputEventAck) method, but not on other platforms; using 492 // 'void' instead is just as safe (since NotificationSource 493 // is not actually typesafe) and avoids this error. 494 NotificationService::current()->Notify( 495 NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK, 496 Source<void>(this), 497 Details<int>(&type)); 498 } 499 500 void ImmediateInputRouter::ProcessKeyboardAck( 501 int type, 502 InputEventAckState ack_result) { 503 if (key_queue_.empty()) { 504 LOG(ERROR) << "Got a KeyEvent back from the renderer but we " 505 << "don't seem to have sent it to the renderer!"; 506 } else if (key_queue_.front().type != type) { 507 LOG(ERROR) << "We seem to have a different key type sent from " 508 << "the renderer. (" << key_queue_.front().type << " vs. " 509 << type << "). Ignoring event."; 510 511 // Something must be wrong. Clear the |key_queue_| and char event 512 // suppression so that we can resume from the error. 513 key_queue_.clear(); 514 client_->OnUnexpectedEventAck(false); 515 } else { 516 NativeWebKeyboardEvent front_item = key_queue_.front(); 517 key_queue_.pop_front(); 518 519 client_->OnKeyboardEventAck(front_item, ack_result); 520 521 // WARNING: This ImmediateInputRouter can be deallocated at this point 522 // (i.e. in the case of Ctrl+W, where the call to 523 // HandleKeyboardEvent destroys this ImmediateInputRouter). 524 } 525 } 526 527 void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) { 528 mouse_wheel_pending_ = false; 529 530 // Process the unhandled wheel event here before calling 531 // ForwardWheelEventWithLatencyInfo() since it will mutate 532 // current_wheel_event_. 533 client_->OnWheelEventAck(current_wheel_event_.event, ack_result); 534 535 // Now send the next (coalesced) mouse wheel event. 536 if (!coalesced_mouse_wheel_events_.empty()) { 537 MouseWheelEventWithLatencyInfo next_wheel_event = 538 coalesced_mouse_wheel_events_.front(); 539 coalesced_mouse_wheel_events_.pop_front(); 540 SendWheelEvent(next_wheel_event); 541 } 542 } 543 544 void ImmediateInputRouter::ProcessGestureAck(int type, 545 InputEventAckState ack_result) { 546 const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result); 547 client_->OnGestureEventAck( 548 gesture_event_filter_->GetGestureEventAwaitingAck(), ack_result); 549 gesture_event_filter_->ProcessGestureAck(processed, type); 550 } 551 552 void ImmediateInputRouter::ProcessTouchAck( 553 InputEventAckState ack_result, 554 const ui::LatencyInfo& latency_info) { 555 // |touch_event_queue_| will forward to OnTouchEventAck when appropriate. 556 touch_event_queue_->ProcessTouchAck(ack_result, latency_info); 557 } 558 559 } // namespace content 560