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"
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"
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
;
46 const char* GetEventAckName(InputEventAckState 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.";
60 InputRouterImpl::Config::Config() {
63 InputRouterImpl::InputRouterImpl(IPC::Sender
* sender
,
64 InputRouterClient
* client
,
65 InputAckHandler
* ack_handler
,
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
) {
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.";
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
))
116 if (mouse_event
.event
.type
== WebInputEvent::MouseUp
&&
117 gesture_event_queue_
.GetTouchpadTapSuppressionController()->
118 ShouldSuppressMouseUp())
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
);
145 coalesced_mouse_wheel_events_
.back().event
.CoalesceWith(
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
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
))
184 touch_event_queue_
.OnGestureScrollEvent(gesture_event
);
186 if (!IsInOverscrollGesture() &&
187 !gesture_event_queue_
.ShouldForward(gesture_event
)) {
188 OverscrollController
* controller
= client_
->GetOverscrollController();
190 controller
->DiscardingGestureEvent(gesture_event
.event
);
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
));
215 next_mouse_move_
->CoalesceWith(mouse_event
);
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
);
246 FilterAndSendWebInputEvent(gesture_event
.event
, gesture_event
.latency
, false);
249 const NativeWebKeyboardEvent
* InputRouterImpl::GetLastKeyboardEvent() const {
250 if (key_queue_
.empty())
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
) {
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
,
279 IPC_MESSAGE_UNHANDLED(handled
= false)
280 IPC_END_MESSAGE_MAP()
283 ack_handler_
->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE
);
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();
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();
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",
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
))
355 if (OfferToClient(input_event
, latency_info
))
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.
370 ProcessInputEventAck(input_event
.type
,
371 INPUT_EVENT_ACK_STATE_IGNORED
,
373 IGNORING_DISPOSITION
);
377 bool InputRouterImpl::OfferToOverscrollController(
378 const WebInputEvent
& input_event
,
379 const ui::LatencyInfo
& latency_info
) {
380 OverscrollController
* controller
= client_
->GetOverscrollController();
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
));
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
,
405 OVERSCROLL_CONTROLLER
);
406 // WARNING: |this| may be deleted at this point.
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.
427 case INPUT_EVENT_ACK_STATE_UNKNOWN
:
428 // Simply drop the event.
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();
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
,
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.
568 base::AutoReset
<AckSource
> auto_reset_current_ack_source(
569 ¤t_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.
594 ack_handler_
->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE
);
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
)
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
,
632 current_wheel_event_
.event
.latency
,
633 current_ack_source_
);
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()) {
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
)
683 OverscrollController
* controller
= client_
->GetOverscrollController();
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
&&
706 touch_event_queue_
.SetAckTimeoutEnabled(touch_ack_timeout_enabled
);
709 void InputRouterImpl::SignalFlushedIfNecessary() {
710 if (!flush_requested_
)
713 if (HasPendingEvents())
716 flush_requested_
= false;
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_
||
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