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