• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <math.h>
8 
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"
32 
33 using base::Time;
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;
41 
42 namespace content {
43 namespace {
44 
GetEventAckName(InputEventAckState ack_result)45 const char* GetEventAckName(InputEventAckState ack_result) {
46   switch(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";
52   }
53   DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName.";
54   return "";
55 }
56 
57 } // namespace
58 
Config()59 InputRouterImpl::Config::Config() {
60 }
61 
InputRouterImpl(IPC::Sender * sender,InputRouterClient * client,InputAckHandler * ack_handler,int routing_id,const Config & config)62 InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
63                                  InputRouterClient* client,
64                                  InputAckHandler* ack_handler,
65                                  int routing_id,
66                                  const Config& config)
67     : sender_(sender),
68       client_(client),
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) {
80   DCHECK(sender);
81   DCHECK(client);
82   DCHECK(ack_handler);
83   UpdateTouchAckTimeoutEnabled();
84 }
85 
~InputRouterImpl()86 InputRouterImpl::~InputRouterImpl() {}
87 
Flush()88 void InputRouterImpl::Flush() {
89   flush_requested_ = true;
90   SignalFlushedIfNecessary();
91 }
92 
SendInput(scoped_ptr<IPC::Message> message)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.";
103       return false;
104     default:
105       return Send(message.release());
106   }
107 }
108 
SendMouseEvent(const MouseEventWithLatencyInfo & mouse_event)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))
114       return;
115   if (mouse_event.event.type == WebInputEvent::MouseUp &&
116       gesture_event_queue_.GetTouchpadTapSuppressionController()->
117           ShouldSuppressMouseUp())
118       return;
119 
120   SendMouseEventImmediately(mouse_event);
121 }
122 
SendWheelEvent(const MouseWheelEventWithLatencyInfo & wheel_event)123 void InputRouterImpl::SendWheelEvent(
124     const MouseWheelEventWithLatencyInfo& wheel_event) {
125   SendWheelEvent(QueuedWheelEvent(wheel_event, false));
126 }
127 
SendWheelEvent(const QueuedWheelEvent & wheel_event)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);
143     } else {
144       coalesced_mouse_wheel_events_.back().event.CoalesceWith(
145           wheel_event.event);
146     }
147     return;
148   }
149 
150   mouse_wheel_pending_ = true;
151   current_wheel_event_ = wheel_event;
152 
153   LOCAL_HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize",
154                              coalesced_mouse_wheel_events_.size());
155 
156   FilterAndSendWebInputEvent(
157       wheel_event.event.event, wheel_event.event.latency, false);
158 }
159 
SendKeyboardEvent(const NativeWebKeyboardEvent & key_event,const ui::LatencyInfo & latency_info,bool is_keyboard_shortcut)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
165   // handler.
166   key_queue_.push_back(key_event);
167   LOCAL_HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size());
168 
169   gesture_event_queue_.FlingHasBeenHalted();
170 
171   // Only forward the non-native portions of our event.
172   FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut);
173 }
174 
SendGestureEvent(const GestureEventWithLatencyInfo & original_gesture_event)175 void InputRouterImpl::SendGestureEvent(
176     const GestureEventWithLatencyInfo& original_gesture_event) {
177   input_stream_validator_.Validate(original_gesture_event.event);
178 
179   GestureEventWithLatencyInfo gesture_event(original_gesture_event);
180 
181   if (touch_action_filter_.FilterGestureEvent(&gesture_event.event))
182     return;
183 
184   if (gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchscreen)
185     touch_event_queue_.OnGestureScrollEvent(gesture_event);
186 
187   if (!gesture_event_queue_.ShouldForward(gesture_event))
188     return;
189 
190   SendGestureEventImmediately(gesture_event);
191 }
192 
SendTouchEvent(const TouchEventWithLatencyInfo & touch_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);
197 }
198 
199 // Forwards MouseEvent without passing it through
200 // TouchpadTapSuppressionController.
SendMouseEventImmediately(const MouseEventWithLatencyInfo & mouse_event)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));
211       else
212         next_mouse_move_->CoalesceWith(mouse_event);
213       return;
214     }
215     mouse_move_pending_ = true;
216   }
217 
218   FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false);
219 }
220 
SendTouchEventImmediately(const TouchEventWithLatencyInfo & touch_event)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();
230   }
231 
232   FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
233 }
234 
SendGestureEventImmediately(const GestureEventWithLatencyInfo & gesture_event)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);
240     return;
241   }
242 
243   FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
244 }
245 
GetLastKeyboardEvent() const246 const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const {
247   if (key_queue_.empty())
248     return NULL;
249   return &key_queue_.front();
250 }
251 
ShouldForwardTouchEvent() const252 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();
256 }
257 
OnViewUpdated(int view_flags)258 void InputRouterImpl::OnViewUpdated(int view_flags) {
259   current_view_flags_ = view_flags;
260 
261   // A fixed page scale or mobile viewport should disable the touch ack timeout.
262   UpdateTouchAckTimeoutEnabled();
263 }
264 
OnMessageReceived(const IPC::Message & message)265 bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
266   bool handled = true;
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,
275                         OnSetTouchAction)
276     IPC_MESSAGE_UNHANDLED(handled = false)
277   IPC_END_MESSAGE_MAP()
278 
279   return handled;
280 }
281 
OnTouchEventAck(const TouchEventWithLatencyInfo & event,InputEventAckState ack_result)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();
290   }
291   ack_handler_->OnTouchEventAck(event, ack_result);
292 }
293 
OnGestureEventAck(const GestureEventWithLatencyInfo & event,InputEventAckState 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);
299 }
300 
SendSelectRange(scoped_ptr<IPC::Message> message)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();
305     return true;
306   }
307 
308   select_range_pending_ = true;
309   return Send(message.release());
310 }
311 
SendMoveCaret(scoped_ptr<IPC::Message> message)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();
316     return true;
317   }
318 
319   move_caret_pending_ = true;
320   return Send(message.release());
321 }
322 
Send(IPC::Message * message)323 bool InputRouterImpl::Send(IPC::Message* message) {
324   return sender_->Send(message);
325 }
326 
FilterAndSendWebInputEvent(const WebInputEvent & input_event,const ui::LatencyInfo & latency_info,bool is_keyboard_shortcut)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",
333                "type",
334                WebInputEventTraits::GetName(input_event.type));
335 
336   // Any input event cancels a pending mouse move event.
337   next_mouse_move_.reset();
338 
339   OfferToHandlers(input_event, latency_info, is_keyboard_shortcut);
340 }
341 
OfferToHandlers(const WebInputEvent & input_event,const ui::LatencyInfo & latency_info,bool 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);
346 
347   if (OfferToClient(input_event, latency_info))
348     return;
349 
350   OfferToRenderer(input_event, latency_info, is_keyboard_shortcut);
351 
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);
358   }
359 
360   // If we don't care about the ack disposition, send the ack immediately.
361   if (ignores_ack) {
362     ProcessInputEventAck(input_event.type,
363                          INPUT_EVENT_ACK_STATE_IGNORED,
364                          latency_info,
365                          IGNORING_DISPOSITION);
366   }
367 }
368 
OfferToClient(const WebInputEvent & input_event,const ui::LatencyInfo & latency_info)369 bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event,
370                                     const ui::LatencyInfo& latency_info) {
371   bool consumed = false;
372 
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.
382       consumed = true;
383       break;
384     case INPUT_EVENT_ACK_STATE_UNKNOWN:
385       // Simply drop the event.
386       consumed = true;
387       break;
388     default:
389       break;
390   }
391 
392   return consumed;
393 }
394 
OfferToRenderer(const WebInputEvent & input_event,const ui::LatencyInfo & latency_info,bool is_keyboard_shortcut)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();
406     }
407     return true;
408   }
409   return false;
410 }
411 
SendSyntheticWheelEventForPinch(const GestureEventWithLatencyInfo & pinch_event)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.
417 
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;
444 
445   SendWheelEvent(QueuedWheelEvent(
446       MouseWheelEventWithLatencyInfo(wheelEvent, pinch_event.latency), true));
447 }
448 
OnInputEventAck(const InputHostMsg_HandleInputEvent_ACK_Params & ack)449 void InputRouterImpl::OnInputEventAck(
450     const InputHostMsg_HandleInputEvent_ACK_Params& ack) {
451   client_->DecrementInFlightEventCount();
452 
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);
456 
457   if (ack.overscroll) {
458     DCHECK(ack.type == WebInputEvent::MouseWheel ||
459            ack.type == WebInputEvent::GestureScrollUpdate);
460     OnDidOverscroll(*ack.overscroll);
461   }
462 
463   ProcessInputEventAck(ack.type, ack.state, ack.latency, RENDERER);
464   // WARNING: |this| may be deleted at this point.
465 
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,
476       Source<void>(this),
477       Details<int>(&type));
478 }
479 
OnDidOverscroll(const DidOverscrollParams & params)480 void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) {
481   client_->DidOverscroll(params);
482 }
483 
OnMsgMoveCaretAck()484 void InputRouterImpl::OnMsgMoveCaretAck() {
485   move_caret_pending_ = false;
486   if (next_move_caret_)
487     SendMoveCaret(next_move_caret_.Pass());
488 }
489 
OnSelectRangeAck()490 void InputRouterImpl::OnSelectRangeAck() {
491   select_range_pending_ = false;
492   if (next_selection_range_)
493     SendSelectRange(next_selection_range_.Pass());
494 }
495 
OnHasTouchEventHandlers(bool has_handlers)496 void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) {
497   TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers",
498                "has_handlers", has_handlers);
499 
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.
506   if (!has_handlers)
507     touch_action_filter_.ResetTouchAction();
508 
509   touch_event_queue_.OnHasTouchEventHandlers(has_handlers);
510   client_->OnHasTouchEventHandlers(has_handlers);
511 }
512 
OnSetTouchAction(TouchAction touch_action)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);
518 
519   touch_action_filter_.OnSetTouchAction(touch_action);
520 
521   // TOUCH_ACTION_NONE should disable the touch ack timeout.
522   UpdateTouchAckTimeoutEnabled();
523 }
524 
ProcessInputEventAck(WebInputEvent::Type event_type,InputEventAckState ack_result,const ui::LatencyInfo & latency_info,AckSource ack_source)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));
533 
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.
540     return;
541   }
542 
543   base::AutoReset<AckSource> auto_reset_current_ack_source(
544       &current_ack_source_, ack_source);
545 
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);
556   }
557 
558   SignalFlushedIfNecessary();
559 }
560 
ProcessKeyboardAck(blink::WebInputEvent::Type type,InputEventAckState ack_result)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.
568     key_queue_.clear();
569     ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE);
570   } else {
571     NativeWebKeyboardEvent front_item = key_queue_.front();
572     key_queue_.pop_front();
573 
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.
579   }
580 }
581 
ProcessMouseAck(blink::WebInputEvent::Type type,InputEventAckState ack_result)582 void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type,
583                                       InputEventAckState ack_result) {
584   if (type != WebInputEvent::MouseMove)
585     return;
586 
587   DCHECK(mouse_move_pending_);
588   mouse_move_pending_ = false;
589 
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);
595   }
596 }
597 
ProcessWheelAck(InputEventAckState ack_result,const ui::LatencyInfo & latency)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);
603 
604   if (current_wheel_event_.synthesized_from_pinch) {
605     // Ack the GesturePinchUpdate event that generated this wheel event.
606     ProcessInputEventAck(WebInputEvent::GesturePinchUpdate,
607                          ack_result,
608                          current_wheel_event_.event.latency,
609                          current_ack_source_);
610   } else {
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);
614   }
615 
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;
621 
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);
627   }
628 }
629 
ProcessGestureAck(WebInputEvent::Type type,InputEventAckState ack_result,const ui::LatencyInfo & latency)630 void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type,
631                                         InputEventAckState ack_result,
632                                         const ui::LatencyInfo& latency) {
633   if (!gesture_event_queue_.ExpectingGestureAck())
634     return;
635 
636   // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate.
637   gesture_event_queue_.ProcessGestureAck(ack_result, type, latency);
638 }
639 
ProcessTouchAck(InputEventAckState ack_result,const ui::LatencyInfo & 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);
645 }
646 
UpdateTouchAckTimeoutEnabled()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;
652 
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;
658 
659   const bool touch_ack_timeout_enabled = !fixed_page_scale &&
660                                          !mobile_viewport &&
661                                          !touch_action_none;
662   touch_event_queue_.SetAckTimeoutEnabled(touch_ack_timeout_enabled);
663 }
664 
SignalFlushedIfNecessary()665 void InputRouterImpl::SignalFlushedIfNecessary() {
666   if (!flush_requested_)
667     return;
668 
669   if (HasPendingEvents())
670     return;
671 
672   flush_requested_ = false;
673   client_->DidFlush();
674 }
675 
HasPendingEvents() const676 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_ ||
683          move_caret_pending_;
684 }
685 
QueuedWheelEvent()686 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent()
687     : synthesized_from_pinch(false) {
688 }
689 
QueuedWheelEvent(const MouseWheelEventWithLatencyInfo & event,bool synthesized_from_pinch)690 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent(
691     const MouseWheelEventWithLatencyInfo& event,
692     bool synthesized_from_pinch)
693     : event(event), synthesized_from_pinch(synthesized_from_pinch) {
694 }
695 
~QueuedWheelEvent()696 InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() {
697 }
698 
699 }  // namespace content
700