Home | History | Annotate | Download | only in input
      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 
     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 
     59 InputRouterImpl::Config::Config() {
     60 }
     61 
     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 
     86 InputRouterImpl::~InputRouterImpl() {}
     87 
     88 void InputRouterImpl::Flush() {
     89   flush_requested_ = true;
     90   SignalFlushedIfNecessary();
     91 }
     92 
     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 
    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 
    123 void InputRouterImpl::SendWheelEvent(
    124     const MouseWheelEventWithLatencyInfo& wheel_event) {
    125   SendWheelEvent(QueuedWheelEvent(wheel_event, false));
    126 }
    127 
    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   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 
    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   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 
    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 
    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.
    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 
    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 
    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 
    246 const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const {
    247   if (key_queue_.empty())
    248     return NULL;
    249   return &key_queue_.front();
    250 }
    251 
    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();
    256 }
    257 
    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 
    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 
    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 
    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 
    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 
    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 
    323 bool InputRouterImpl::Send(IPC::Message* message) {
    324   return sender_->Send(message);
    325 }
    326 
    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 
    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 
    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 
    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 
    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 
    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 
    480 void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) {
    481   client_->DidOverscroll(params);
    482 }
    483 
    484 void InputRouterImpl::OnMsgMoveCaretAck() {
    485   move_caret_pending_ = false;
    486   if (next_move_caret_)
    487     SendMoveCaret(next_move_caret_.Pass());
    488 }
    489 
    490 void InputRouterImpl::OnSelectRangeAck() {
    491   select_range_pending_ = false;
    492   if (next_selection_range_)
    493     SendSelectRange(next_selection_range_.Pass());
    494 }
    495 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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_ ||
    683          move_caret_pending_;
    684 }
    685 
    686 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent()
    687     : synthesized_from_pinch(false) {
    688 }
    689 
    690 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent(
    691     const MouseWheelEventWithLatencyInfo& event,
    692     bool synthesized_from_pinch)
    693     : event(event), synthesized_from_pinch(synthesized_from_pinch) {
    694 }
    695 
    696 InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() {
    697 }
    698 
    699 }  // namespace content
    700