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