input_router_impl.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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 "base/auto_reset.h" 8#include "base/command_line.h" 9#include "base/metrics/histogram.h" 10#include "base/strings/string_number_conversions.h" 11#include "content/browser/renderer_host/input/gesture_event_queue.h" 12#include "content/browser/renderer_host/input/input_ack_handler.h" 13#include "content/browser/renderer_host/input/input_router_client.h" 14#include "content/browser/renderer_host/input/touch_event_queue.h" 15#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" 16#include "content/browser/renderer_host/input/web_touch_event_traits.h" 17#include "content/browser/renderer_host/overscroll_controller.h" 18#include "content/common/content_constants_internal.h" 19#include "content/common/edit_command.h" 20#include "content/common/input/touch_action.h" 21#include "content/common/input_messages.h" 22#include "content/common/view_messages.h" 23#include "content/port/common/input_event_ack_state.h" 24#include "content/public/browser/notification_service.h" 25#include "content/public/browser/notification_types.h" 26#include "content/public/browser/user_metrics.h" 27#include "content/public/common/content_switches.h" 28#include "ipc/ipc_sender.h" 29#include "ui/events/event.h" 30#include "ui/events/keycodes/keyboard_codes.h" 31 32#if defined(OS_ANDROID) 33#include "ui/gfx/android/view_configuration.h" 34#include "ui/gfx/screen.h" 35#else 36#include "ui/events/gestures/gesture_configuration.h" 37#endif 38 39using base::Time; 40using base::TimeDelta; 41using base::TimeTicks; 42using blink::WebGestureEvent; 43using blink::WebInputEvent; 44using blink::WebKeyboardEvent; 45using blink::WebMouseEvent; 46using blink::WebMouseWheelEvent; 47 48namespace content { 49namespace { 50 51// TODO(jdduke): Instead of relying on command line flags or conditional 52// conditional compilation here, we should instead use an InputRouter::Settings 53// construct, supplied and customized by the RenderWidgetHostView. See 54// crbug.com/343917. 55bool GetTouchAckTimeoutDelay(base::TimeDelta* touch_ack_timeout_delay) { 56 CommandLine* parsed_command_line = CommandLine::ForCurrentProcess(); 57 if (!parsed_command_line->HasSwitch(switches::kTouchAckTimeoutDelayMs)) 58 return false; 59 60 std::string timeout_string = parsed_command_line->GetSwitchValueASCII( 61 switches::kTouchAckTimeoutDelayMs); 62 size_t timeout_ms; 63 if (!base::StringToSizeT(timeout_string, &timeout_ms)) 64 return false; 65 66 *touch_ack_timeout_delay = base::TimeDelta::FromMilliseconds(timeout_ms); 67 return true; 68} 69 70#if defined(OS_ANDROID) 71double GetTouchMoveSlopSuppressionLengthDips() { 72 const double touch_slop_length_pixels = 73 static_cast<double>(gfx::ViewConfiguration::GetTouchSlopInPixels()); 74 const double device_scale_factor = 75 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor(); 76 return touch_slop_length_pixels / device_scale_factor; 77} 78#elif defined(USE_AURA) 79double GetTouchMoveSlopSuppressionLengthDips() { 80 return ui::GestureConfiguration::max_touch_move_in_pixels_for_click(); 81} 82#else 83double GetTouchMoveSlopSuppressionLengthDips() { 84 return 0; 85} 86#endif 87 88TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() { 89 std::string modeString = CommandLine::ForCurrentProcess()-> 90 GetSwitchValueASCII(switches::kTouchScrollingMode); 91 if (modeString == switches::kTouchScrollingModeSyncTouchmove) 92 return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE; 93 if (modeString == switches::kTouchScrollingModeAbsorbTouchmove) 94 return TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE; 95 if (modeString == switches::kTouchScrollingModeTouchcancel) 96 return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL; 97 if (modeString != "") 98 LOG(ERROR) << "Invalid --touch-scrolling-mode option: " << modeString; 99 return TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT; 100} 101 102GestureEventWithLatencyInfo MakeGestureEvent(WebInputEvent::Type type, 103 double timestamp_seconds, 104 int x, 105 int y, 106 int modifiers, 107 const ui::LatencyInfo& latency) { 108 WebGestureEvent result; 109 110 result.type = type; 111 result.x = x; 112 result.y = y; 113 result.sourceDevice = WebGestureEvent::Touchscreen; 114 result.timeStampSeconds = timestamp_seconds; 115 result.modifiers = modifiers; 116 117 return GestureEventWithLatencyInfo(result, latency); 118} 119 120const char* GetEventAckName(InputEventAckState ack_result) { 121 switch(ack_result) { 122 case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN"; 123 case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED"; 124 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED"; 125 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS"; 126 case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED"; 127 } 128 DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName."; 129 return ""; 130} 131 132} // namespace 133 134InputRouterImpl::InputRouterImpl(IPC::Sender* sender, 135 InputRouterClient* client, 136 InputAckHandler* ack_handler, 137 int routing_id) 138 : sender_(sender), 139 client_(client), 140 ack_handler_(ack_handler), 141 routing_id_(routing_id), 142 select_range_pending_(false), 143 move_caret_pending_(false), 144 mouse_move_pending_(false), 145 mouse_wheel_pending_(false), 146 touch_ack_timeout_supported_(false), 147 current_view_flags_(0), 148 current_ack_source_(ACK_SOURCE_NONE), 149 gesture_event_queue_(new GestureEventQueue(this, this)) { 150 DCHECK(sender); 151 DCHECK(client); 152 DCHECK(ack_handler); 153 touch_event_queue_.reset(new TouchEventQueue( 154 this, GetTouchScrollingMode(), GetTouchMoveSlopSuppressionLengthDips())); 155 touch_ack_timeout_supported_ = 156 GetTouchAckTimeoutDelay(&touch_ack_timeout_delay_); 157 UpdateTouchAckTimeoutEnabled(); 158} 159 160InputRouterImpl::~InputRouterImpl() {} 161 162void InputRouterImpl::Flush() {} 163 164bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) { 165 DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); 166 switch (message->type()) { 167 // Check for types that require an ACK. 168 case InputMsg_SelectRange::ID: 169 return SendSelectRange(message.Pass()); 170 case InputMsg_MoveCaret::ID: 171 return SendMoveCaret(message.Pass()); 172 case InputMsg_HandleInputEvent::ID: 173 NOTREACHED() << "WebInputEvents should never be sent via SendInput."; 174 return false; 175 default: 176 return Send(message.release()); 177 } 178} 179 180void InputRouterImpl::SendMouseEvent( 181 const MouseEventWithLatencyInfo& mouse_event) { 182 // Order is important here; we need to convert all MouseEvents before they 183 // propagate further, e.g., to the tap suppression controller. 184 if (CommandLine::ForCurrentProcess()->HasSwitch( 185 switches::kSimulateTouchScreenWithMouse)) { 186 SimulateTouchGestureWithMouse(mouse_event); 187 return; 188 } 189 190 if (mouse_event.event.type == WebInputEvent::MouseDown && 191 gesture_event_queue_->GetTouchpadTapSuppressionController()-> 192 ShouldDeferMouseDown(mouse_event)) 193 return; 194 if (mouse_event.event.type == WebInputEvent::MouseUp && 195 gesture_event_queue_->GetTouchpadTapSuppressionController()-> 196 ShouldSuppressMouseUp()) 197 return; 198 199 SendMouseEventImmediately(mouse_event); 200} 201 202void InputRouterImpl::SendWheelEvent( 203 const MouseWheelEventWithLatencyInfo& wheel_event) { 204 // If there's already a mouse wheel event waiting to be sent to the renderer, 205 // add the new deltas to that event. Not doing so (e.g., by dropping the old 206 // event, as for mouse moves) results in very slow scrolling on the Mac (on 207 // which many, very small wheel events are sent). 208 if (mouse_wheel_pending_) { 209 if (coalesced_mouse_wheel_events_.empty() || 210 !coalesced_mouse_wheel_events_.back().CanCoalesceWith(wheel_event)) { 211 coalesced_mouse_wheel_events_.push_back(wheel_event); 212 } else { 213 coalesced_mouse_wheel_events_.back().CoalesceWith(wheel_event); 214 } 215 return; 216 } 217 mouse_wheel_pending_ = true; 218 current_wheel_event_ = wheel_event; 219 220 HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", 221 coalesced_mouse_wheel_events_.size()); 222 223 FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false); 224} 225 226void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, 227 const ui::LatencyInfo& latency_info, 228 bool is_keyboard_shortcut) { 229 // Put all WebKeyboardEvent objects in a queue since we can't trust the 230 // renderer and we need to give something to the HandleKeyboardEvent 231 // handler. 232 key_queue_.push_back(key_event); 233 HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); 234 235 gesture_event_queue_->FlingHasBeenHalted(); 236 237 // Only forward the non-native portions of our event. 238 FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut); 239} 240 241void InputRouterImpl::SendGestureEvent( 242 const GestureEventWithLatencyInfo& original_gesture_event) { 243 event_stream_validator_.OnEvent(original_gesture_event.event); 244 GestureEventWithLatencyInfo gesture_event(original_gesture_event); 245 246 if (touch_action_filter_.FilterGestureEvent(&gesture_event.event)) 247 return; 248 249 touch_event_queue_->OnGestureScrollEvent(gesture_event); 250 251 if (!IsInOverscrollGesture() && 252 !gesture_event_queue_->ShouldForward(gesture_event)) { 253 OverscrollController* controller = client_->GetOverscrollController(); 254 if (controller) 255 controller->DiscardingGestureEvent(gesture_event.event); 256 return; 257 } 258 259 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 260} 261 262void InputRouterImpl::SendTouchEvent( 263 const TouchEventWithLatencyInfo& touch_event) { 264 touch_event_queue_->QueueEvent(touch_event); 265} 266 267// Forwards MouseEvent without passing it through 268// TouchpadTapSuppressionController. 269void InputRouterImpl::SendMouseEventImmediately( 270 const MouseEventWithLatencyInfo& mouse_event) { 271 // Avoid spamming the renderer with mouse move events. It is important 272 // to note that WM_MOUSEMOVE events are anyways synthetic, but since our 273 // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way 274 // more WM_MOUSEMOVE events than we wish to send to the renderer. 275 if (mouse_event.event.type == WebInputEvent::MouseMove) { 276 if (mouse_move_pending_) { 277 if (!next_mouse_move_) 278 next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event)); 279 else 280 next_mouse_move_->CoalesceWith(mouse_event); 281 return; 282 } 283 mouse_move_pending_ = true; 284 } 285 286 FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false); 287} 288 289void InputRouterImpl::SendTouchEventImmediately( 290 const TouchEventWithLatencyInfo& touch_event) { 291 if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) { 292 touch_action_filter_.ResetTouchAction(); 293 // Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling 294 // the timeout here will not take effect until the *following* touch 295 // sequence. This is a desirable side-effect, giving the renderer a chance 296 // to send a touch-action response without racing against the ack timeout. 297 UpdateTouchAckTimeoutEnabled(); 298 } 299 300 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); 301} 302 303void InputRouterImpl::SendGestureEventImmediately( 304 const GestureEventWithLatencyInfo& gesture_event) { 305 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 306} 307 308const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const { 309 if (key_queue_.empty()) 310 return NULL; 311 return &key_queue_.front(); 312} 313 314bool InputRouterImpl::ShouldForwardTouchEvent() const { 315 // Always send a touch event if the renderer has a touch-event handler or 316 // there are pending touch events. 317 return touch_event_queue_->has_handlers() || !touch_event_queue_->empty(); 318} 319 320void InputRouterImpl::OnViewUpdated(int view_flags) { 321 current_view_flags_ = view_flags; 322 323 // A fixed page scale or mobile viewport should disable the touch ack timeout. 324 UpdateTouchAckTimeoutEnabled(); 325} 326 327bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) { 328 bool handled = true; 329 bool message_is_ok = true; 330 IPC_BEGIN_MESSAGE_MAP_EX(InputRouterImpl, message, message_is_ok) 331 IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck) 332 IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck) 333 IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck) 334 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, 335 OnHasTouchEventHandlers) 336 IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction, 337 OnSetTouchAction) 338 IPC_MESSAGE_UNHANDLED(handled = false) 339 IPC_END_MESSAGE_MAP() 340 341 if (!message_is_ok) 342 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); 343 344 return handled; 345} 346 347void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event, 348 InputEventAckState ack_result) { 349 // Touchstart events sent to the renderer indicate a new touch sequence, but 350 // in some cases we may filter out sending the touchstart - catch those here. 351 if (WebTouchEventTraits::IsTouchSequenceStart(event.event) && 352 ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) { 353 touch_action_filter_.ResetTouchAction(); 354 UpdateTouchAckTimeoutEnabled(); 355 } 356 ack_handler_->OnTouchEventAck(event, ack_result); 357} 358 359void InputRouterImpl::OnGestureEventAck( 360 const GestureEventWithLatencyInfo& event, 361 InputEventAckState ack_result) { 362 touch_event_queue_->OnGestureEventAck(event, ack_result); 363 ProcessAckForOverscroll(event.event, ack_result); 364 ack_handler_->OnGestureEventAck(event, ack_result); 365} 366 367bool InputRouterImpl::SendSelectRange(scoped_ptr<IPC::Message> message) { 368 DCHECK(message->type() == InputMsg_SelectRange::ID); 369 if (select_range_pending_) { 370 next_selection_range_ = message.Pass(); 371 return true; 372 } 373 374 select_range_pending_ = true; 375 return Send(message.release()); 376} 377 378bool InputRouterImpl::SendMoveCaret(scoped_ptr<IPC::Message> message) { 379 DCHECK(message->type() == InputMsg_MoveCaret::ID); 380 if (move_caret_pending_) { 381 next_move_caret_ = message.Pass(); 382 return true; 383 } 384 385 move_caret_pending_ = true; 386 return Send(message.release()); 387} 388 389bool InputRouterImpl::Send(IPC::Message* message) { 390 return sender_->Send(message); 391} 392 393void InputRouterImpl::FilterAndSendWebInputEvent( 394 const WebInputEvent& input_event, 395 const ui::LatencyInfo& latency_info, 396 bool is_keyboard_shortcut) { 397 TRACE_EVENT1("input", 398 "InputRouterImpl::FilterAndSendWebInputEvent", 399 "type", 400 WebInputEventTraits::GetName(input_event.type)); 401 402 // Transmit any pending wheel events on a non-wheel event. This ensures that 403 // final PhaseEnded wheel event is received, which is necessary to terminate 404 // rubber-banding, for example. 405 if (input_event.type != WebInputEvent::MouseWheel) { 406 WheelEventQueue mouse_wheel_events; 407 mouse_wheel_events.swap(coalesced_mouse_wheel_events_); 408 for (size_t i = 0; i < mouse_wheel_events.size(); ++i) { 409 OfferToHandlers(mouse_wheel_events[i].event, 410 mouse_wheel_events[i].latency, 411 false); 412 } 413 } 414 415 // Any input event cancels a pending mouse move event. 416 next_mouse_move_.reset(); 417 418 OfferToHandlers(input_event, latency_info, is_keyboard_shortcut); 419} 420 421void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, 422 const ui::LatencyInfo& latency_info, 423 bool is_keyboard_shortcut) { 424 // Trackpad pinch gestures are not yet handled by the renderer. 425 // TODO(rbyers): Send mousewheel for trackpad pinch - crbug.com/289887. 426 if (input_event.type == WebInputEvent::GesturePinchUpdate && 427 static_cast<const WebGestureEvent&>(input_event).sourceDevice == 428 WebGestureEvent::Touchpad) { 429 ProcessInputEventAck(input_event.type, 430 INPUT_EVENT_ACK_STATE_NOT_CONSUMED, 431 latency_info, 432 ACK_SOURCE_NONE); 433 return; 434 } 435 436 if (OfferToOverscrollController(input_event, latency_info)) 437 return; 438 439 if (OfferToClient(input_event, latency_info)) 440 return; 441 442 OfferToRenderer(input_event, latency_info, is_keyboard_shortcut); 443 444 // If we don't care about the ack disposition, send the ack immediately. 445 if (WebInputEventTraits::IgnoresAckDisposition(input_event.type)) { 446 ProcessInputEventAck(input_event.type, 447 INPUT_EVENT_ACK_STATE_IGNORED, 448 latency_info, 449 IGNORING_DISPOSITION); 450 } 451} 452 453bool InputRouterImpl::OfferToOverscrollController( 454 const WebInputEvent& input_event, 455 const ui::LatencyInfo& latency_info) { 456 OverscrollController* controller = client_->GetOverscrollController(); 457 if (!controller) 458 return false; 459 460 OverscrollController::Disposition disposition = 461 controller->DispatchEvent(input_event, latency_info); 462 463 bool consumed = disposition == OverscrollController::CONSUMED; 464 465 if (disposition == OverscrollController::SHOULD_FORWARD_TO_GESTURE_QUEUE) { 466 DCHECK(WebInputEvent::isGestureEventType(input_event.type)); 467 const blink::WebGestureEvent& gesture_event = 468 static_cast<const blink::WebGestureEvent&>(input_event); 469 // An ACK is expected for the event, so mark it as consumed. 470 consumed = !gesture_event_queue_->ShouldForward( 471 GestureEventWithLatencyInfo(gesture_event, latency_info)); 472 } 473 474 if (consumed) { 475 InputEventAckState overscroll_ack = 476 WebInputEvent::isTouchEventType(input_event.type) ? 477 INPUT_EVENT_ACK_STATE_NOT_CONSUMED : INPUT_EVENT_ACK_STATE_CONSUMED; 478 ProcessInputEventAck(input_event.type, 479 overscroll_ack, 480 latency_info, 481 OVERSCROLL_CONTROLLER); 482 // WARNING: |this| may be deleted at this point. 483 } 484 485 return consumed; 486} 487 488bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event, 489 const ui::LatencyInfo& latency_info) { 490 bool consumed = false; 491 492 InputEventAckState filter_ack = 493 client_->FilterInputEvent(input_event, latency_info); 494 switch (filter_ack) { 495 case INPUT_EVENT_ACK_STATE_CONSUMED: 496 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: 497 // Send the ACK and early exit. 498 next_mouse_move_.reset(); 499 ProcessInputEventAck(input_event.type, filter_ack, latency_info, CLIENT); 500 // WARNING: |this| may be deleted at this point. 501 consumed = true; 502 break; 503 case INPUT_EVENT_ACK_STATE_UNKNOWN: 504 // Simply drop the event. 505 consumed = true; 506 break; 507 default: 508 break; 509 } 510 511 return consumed; 512} 513 514bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, 515 const ui::LatencyInfo& latency_info, 516 bool is_keyboard_shortcut) { 517 if (Send(new InputMsg_HandleInputEvent( 518 routing_id(), &input_event, latency_info, is_keyboard_shortcut))) { 519 // Ack messages for ignored ack event types are not required, and might 520 // never be sent by the renderer. Consequently, such event types should not 521 // affect event timing or in-flight event count metrics. 522 if (!WebInputEventTraits::IgnoresAckDisposition(input_event.type)) { 523 input_event_start_time_ = TimeTicks::Now(); 524 client_->IncrementInFlightEventCount(); 525 } 526 return true; 527 } 528 return false; 529} 530 531void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type, 532 InputEventAckState ack_result, 533 const ui::LatencyInfo& latency_info) { 534 // A synthetic ack will already have been sent for this event, and it should 535 // not affect event timing or in-flight count metrics. 536 if (WebInputEventTraits::IgnoresAckDisposition(event_type)) 537 return; 538 539 client_->DecrementInFlightEventCount(); 540 541 // Log the time delta for processing an input event. 542 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; 543 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); 544 545 ProcessInputEventAck(event_type, ack_result, latency_info, RENDERER); 546 // WARNING: |this| may be deleted at this point. 547 548 // This is used only for testing, and the other end does not use the 549 // source object. On linux, specifying 550 // Source<RenderWidgetHost> results in a very strange 551 // runtime error in the epilogue of the enclosing 552 // (ProcessInputEventAck) method, but not on other platforms; using 553 // 'void' instead is just as safe (since NotificationSource 554 // is not actually typesafe) and avoids this error. 555 int type = static_cast<int>(event_type); 556 NotificationService::current()->Notify( 557 NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK, 558 Source<void>(this), 559 Details<int>(&type)); 560} 561 562void InputRouterImpl::OnMsgMoveCaretAck() { 563 move_caret_pending_ = false; 564 if (next_move_caret_) 565 SendMoveCaret(next_move_caret_.Pass()); 566} 567 568void InputRouterImpl::OnSelectRangeAck() { 569 select_range_pending_ = false; 570 if (next_selection_range_) 571 SendSelectRange(next_selection_range_.Pass()); 572} 573 574void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) { 575 TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers", 576 "has_handlers", has_handlers); 577 578 touch_event_queue_->OnHasTouchEventHandlers(has_handlers); 579 client_->OnHasTouchEventHandlers(has_handlers); 580} 581 582void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) { 583 // Synthetic touchstart events should get filtered out in RenderWidget. 584 DCHECK(touch_event_queue_->IsPendingAckTouchStart()); 585 TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction", 586 "action", touch_action); 587 588 touch_action_filter_.OnSetTouchAction(touch_action); 589 590 // TOUCH_ACTION_NONE should disable the touch ack timeout. 591 UpdateTouchAckTimeoutEnabled(); 592} 593 594void InputRouterImpl::ProcessInputEventAck( 595 WebInputEvent::Type event_type, 596 InputEventAckState ack_result, 597 const ui::LatencyInfo& latency_info, 598 AckSource ack_source) { 599 TRACE_EVENT2("input", "InputRouterImpl::ProcessInputEventAck", 600 "type", WebInputEventTraits::GetName(event_type), 601 "ack", GetEventAckName(ack_result)); 602 603 // Note: The keyboard ack must be treated carefully, as it may result in 604 // synchronous destruction of |this|. Handling immediately guards against 605 // future references to |this|, as with |auto_reset_current_ack_source| below. 606 if (WebInputEvent::isKeyboardEventType(event_type)) { 607 ProcessKeyboardAck(event_type, ack_result); 608 // WARNING: |this| may be deleted at this point. 609 return; 610 } 611 612 base::AutoReset<AckSource> auto_reset_current_ack_source( 613 ¤t_ack_source_, ack_source); 614 615 if (WebInputEvent::isMouseEventType(event_type)) { 616 ProcessMouseAck(event_type, ack_result); 617 } else if (event_type == WebInputEvent::MouseWheel) { 618 ProcessWheelAck(ack_result, latency_info); 619 } else if (WebInputEvent::isTouchEventType(event_type)) { 620 ProcessTouchAck(ack_result, latency_info); 621 } else if (WebInputEvent::isGestureEventType(event_type)) { 622 ProcessGestureAck(event_type, ack_result, latency_info); 623 } else if (event_type != WebInputEvent::Undefined) { 624 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); 625 } 626} 627 628void InputRouterImpl::ProcessKeyboardAck(blink::WebInputEvent::Type type, 629 InputEventAckState ack_result) { 630 if (key_queue_.empty()) { 631 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); 632 } else if (key_queue_.front().type != type) { 633 // Something must be wrong. Clear the |key_queue_| and char event 634 // suppression so that we can resume from the error. 635 key_queue_.clear(); 636 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE); 637 } else { 638 NativeWebKeyboardEvent front_item = key_queue_.front(); 639 key_queue_.pop_front(); 640 641 ack_handler_->OnKeyboardEventAck(front_item, ack_result); 642 // WARNING: This InputRouterImpl can be deallocated at this point 643 // (i.e. in the case of Ctrl+W, where the call to 644 // HandleKeyboardEvent destroys this InputRouterImpl). 645 // TODO(jdduke): crbug.com/274029 - Make ack-triggered shutdown async. 646 } 647} 648 649void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type, 650 InputEventAckState ack_result) { 651 if (type != WebInputEvent::MouseMove) 652 return; 653 654 mouse_move_pending_ = false; 655 656 if (next_mouse_move_) { 657 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); 658 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move 659 = next_mouse_move_.Pass(); 660 SendMouseEvent(*next_mouse_move); 661 } 662} 663 664void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result, 665 const ui::LatencyInfo& latency) { 666 ProcessAckForOverscroll(current_wheel_event_.event, ack_result); 667 668 // TODO(miletus): Add renderer side latency to each uncoalesced mouse 669 // wheel event and add terminal component to each of them. 670 current_wheel_event_.latency.AddNewLatencyFrom(latency); 671 // Process the unhandled wheel event here before calling SendWheelEvent() 672 // since it will mutate current_wheel_event_. 673 ack_handler_->OnWheelEventAck(current_wheel_event_, ack_result); 674 mouse_wheel_pending_ = false; 675 676 // Now send the next (coalesced) mouse wheel event. 677 if (!coalesced_mouse_wheel_events_.empty()) { 678 MouseWheelEventWithLatencyInfo next_wheel_event = 679 coalesced_mouse_wheel_events_.front(); 680 coalesced_mouse_wheel_events_.pop_front(); 681 SendWheelEvent(next_wheel_event); 682 } 683} 684 685void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type, 686 InputEventAckState ack_result, 687 const ui::LatencyInfo& latency) { 688 // If |ack_result| originated from the overscroll controller, only 689 // feed |gesture_event_queue_| the ack if it was expecting one. 690 if (current_ack_source_ == OVERSCROLL_CONTROLLER && 691 !gesture_event_queue_->HasQueuedGestureEvents()) { 692 return; 693 } 694 695 // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate. 696 gesture_event_queue_->ProcessGestureAck(ack_result, type, latency); 697} 698 699void InputRouterImpl::ProcessTouchAck( 700 InputEventAckState ack_result, 701 const ui::LatencyInfo& latency) { 702 // |touch_event_queue_| will forward to OnTouchEventAck when appropriate. 703 touch_event_queue_->ProcessTouchAck(ack_result, latency); 704} 705 706void InputRouterImpl::ProcessAckForOverscroll(const WebInputEvent& event, 707 InputEventAckState ack_result) { 708 // Acks sent from the overscroll controller need not be fed back into the 709 // overscroll controller. 710 if (current_ack_source_ == OVERSCROLL_CONTROLLER) 711 return; 712 713 OverscrollController* controller = client_->GetOverscrollController(); 714 if (!controller) 715 return; 716 717 controller->ReceivedEventACK( 718 event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result)); 719} 720 721void InputRouterImpl::SimulateTouchGestureWithMouse( 722 const MouseEventWithLatencyInfo& event) { 723 const WebMouseEvent& mouse_event = event.event; 724 int x = mouse_event.x, y = mouse_event.y; 725 float dx = mouse_event.movementX, dy = mouse_event.movementY; 726 static int startX = 0, startY = 0; 727 728 switch (mouse_event.button) { 729 case WebMouseEvent::ButtonLeft: 730 if (mouse_event.type == WebInputEvent::MouseDown) { 731 startX = x; 732 startY = y; 733 SendGestureEvent(MakeGestureEvent( 734 WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds, 735 x, y, 0, event.latency)); 736 } 737 if (dx != 0 || dy != 0) { 738 GestureEventWithLatencyInfo gesture_event = MakeGestureEvent( 739 WebInputEvent::GestureScrollUpdate, mouse_event.timeStampSeconds, 740 x, y, 0, event.latency); 741 gesture_event.event.data.scrollUpdate.deltaX = dx; 742 gesture_event.event.data.scrollUpdate.deltaY = dy; 743 SendGestureEvent(gesture_event); 744 } 745 if (mouse_event.type == WebInputEvent::MouseUp) { 746 SendGestureEvent(MakeGestureEvent( 747 WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds, 748 x, y, 0, event.latency)); 749 } 750 break; 751 case WebMouseEvent::ButtonMiddle: 752 if (mouse_event.type == WebInputEvent::MouseDown) { 753 startX = x; 754 startY = y; 755 SendGestureEvent(MakeGestureEvent( 756 WebInputEvent::GestureShowPress, mouse_event.timeStampSeconds, 757 x, y, 0, event.latency)); 758 SendGestureEvent(MakeGestureEvent( 759 WebInputEvent::GestureTapDown, mouse_event.timeStampSeconds, 760 x, y, 0, event.latency)); 761 } 762 if (mouse_event.type == WebInputEvent::MouseUp) { 763 SendGestureEvent(MakeGestureEvent( 764 WebInputEvent::GestureTap, mouse_event.timeStampSeconds, 765 x, y, 0, event.latency)); 766 } 767 break; 768 case WebMouseEvent::ButtonRight: 769 if (mouse_event.type == WebInputEvent::MouseDown) { 770 startX = x; 771 startY = y; 772 SendGestureEvent(MakeGestureEvent( 773 WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds, 774 x, y, 0, event.latency)); 775 SendGestureEvent(MakeGestureEvent( 776 WebInputEvent::GesturePinchBegin, mouse_event.timeStampSeconds, 777 x, y, 0, event.latency)); 778 } 779 if (dx != 0 || dy != 0) { 780 dx = pow(dy < 0 ? 0.998f : 1.002f, fabs(dy)); 781 GestureEventWithLatencyInfo gesture_event = MakeGestureEvent( 782 WebInputEvent::GesturePinchUpdate, mouse_event.timeStampSeconds, 783 startX, startY, 0, event.latency); 784 gesture_event.event.data.pinchUpdate.scale = dx; 785 SendGestureEvent(gesture_event); 786 } 787 if (mouse_event.type == WebInputEvent::MouseUp) { 788 SendGestureEvent(MakeGestureEvent( 789 WebInputEvent::GesturePinchEnd, mouse_event.timeStampSeconds, 790 x, y, 0, event.latency)); 791 SendGestureEvent(MakeGestureEvent( 792 WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds, 793 x, y, 0, event.latency)); 794 } 795 break; 796 case WebMouseEvent::ButtonNone: 797 break; 798 } 799} 800 801void InputRouterImpl::UpdateTouchAckTimeoutEnabled() { 802 if (!touch_ack_timeout_supported_) { 803 touch_event_queue_->SetAckTimeoutEnabled(false, base::TimeDelta()); 804 return; 805 } 806 807 // Mobile sites tend to be well-behaved with respect to touch handling, so 808 // they have less need for the touch timeout fallback. 809 const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0; 810 const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0; 811 812 // TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves 813 // little purpose. It's also a strong signal that touch handling is critical 814 // to page functionality, so the timeout could do more harm than good. 815 const bool touch_action_none = 816 touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE; 817 818 const bool touch_ack_timeout_enabled = !fixed_page_scale && 819 !mobile_viewport && 820 !touch_action_none; 821 touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled, 822 touch_ack_timeout_delay_); 823} 824 825bool InputRouterImpl::IsInOverscrollGesture() const { 826 OverscrollController* controller = client_->GetOverscrollController(); 827 return controller && controller->overscroll_mode() != OVERSCROLL_NONE; 828} 829 830} // namespace content 831