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
33using base::Time;
34using base::TimeDelta;
35using base::TimeTicks;
36using blink::WebGestureEvent;
37using blink::WebInputEvent;
38using blink::WebKeyboardEvent;
39using blink::WebMouseEvent;
40using blink::WebMouseWheelEvent;
41
42namespace content {
43namespace {
44
45const 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
59InputRouterImpl::Config::Config() {
60}
61
62InputRouterImpl::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
86InputRouterImpl::~InputRouterImpl() {}
87
88void InputRouterImpl::Flush() {
89  flush_requested_ = true;
90  SignalFlushedIfNecessary();
91}
92
93bool 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
109void 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
123void InputRouterImpl::SendWheelEvent(
124    const MouseWheelEventWithLatencyInfo& wheel_event) {
125  SendWheelEvent(QueuedWheelEvent(wheel_event, false));
126}
127
128void 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
160void 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
175void 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
193void 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.
201void 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
221void 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
235void 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
246const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const {
247  if (key_queue_.empty())
248    return NULL;
249  return &key_queue_.front();
250}
251
252bool 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
258void 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
265bool 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
282void 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
294void 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
301bool 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
312bool 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
323bool InputRouterImpl::Send(IPC::Message* message) {
324  return sender_->Send(message);
325}
326
327void 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
342void 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
369bool 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
395bool 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
412void 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
449void 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
480void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) {
481  client_->DidOverscroll(params);
482}
483
484void InputRouterImpl::OnMsgMoveCaretAck() {
485  move_caret_pending_ = false;
486  if (next_move_caret_)
487    SendMoveCaret(next_move_caret_.Pass());
488}
489
490void InputRouterImpl::OnSelectRangeAck() {
491  select_range_pending_ = false;
492  if (next_selection_range_)
493    SendSelectRange(next_selection_range_.Pass());
494}
495
496void 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
513void 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
525void 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
561void 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
582void 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
598void 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
630void 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
640void 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
647void 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
665void InputRouterImpl::SignalFlushedIfNecessary() {
666  if (!flush_requested_)
667    return;
668
669  if (HasPendingEvents())
670    return;
671
672  flush_requested_ = false;
673  client_->DidFlush();
674}
675
676bool 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
686InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent()
687    : synthesized_from_pinch(false) {
688}
689
690InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent(
691    const MouseWheelEventWithLatencyInfo& event,
692    bool synthesized_from_pinch)
693    : event(event), synthesized_from_pinch(synthesized_from_pinch) {
694}
695
696InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() {
697}
698
699}  // namespace content
700