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/immediate_input_router.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/metrics/histogram.h"
      9 #include "content/browser/renderer_host/input/gesture_event_filter.h"
     10 #include "content/browser/renderer_host/input/input_router_client.h"
     11 #include "content/browser/renderer_host/input/touch_event_queue.h"
     12 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
     13 #include "content/browser/renderer_host/render_process_host_impl.h"
     14 #include "content/common/content_constants_internal.h"
     15 #include "content/common/edit_command.h"
     16 #include "content/common/input_messages.h"
     17 #include "content/common/view_messages.h"
     18 #include "content/port/common/input_event_ack_state.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/browser/notification_types.h"
     21 #include "content/public/browser/user_metrics.h"
     22 #include "content/public/common/content_switches.h"
     23 #include "ui/base/events/event.h"
     24 #include "ui/base/keycodes/keyboard_codes.h"
     25 
     26 using base::Time;
     27 using base::TimeDelta;
     28 using base::TimeTicks;
     29 using WebKit::WebGestureEvent;
     30 using WebKit::WebInputEvent;
     31 using WebKit::WebKeyboardEvent;
     32 using WebKit::WebMouseEvent;
     33 using WebKit::WebMouseWheelEvent;
     34 
     35 namespace content {
     36 namespace {
     37 
     38 // Returns |true| if the two wheel events should be coalesced.
     39 bool ShouldCoalesceMouseWheelEvents(const WebMouseWheelEvent& last_event,
     40                                     const WebMouseWheelEvent& new_event) {
     41   return last_event.modifiers == new_event.modifiers &&
     42          last_event.scrollByPage == new_event.scrollByPage &&
     43          last_event.hasPreciseScrollingDeltas
     44              == new_event.hasPreciseScrollingDeltas &&
     45          last_event.phase == new_event.phase &&
     46          last_event.momentumPhase == new_event.momentumPhase;
     47 }
     48 
     49 float GetUnacceleratedDelta(float accelerated_delta, float acceleration_ratio) {
     50   return accelerated_delta * acceleration_ratio;
     51 }
     52 
     53 float GetAccelerationRatio(float accelerated_delta, float unaccelerated_delta) {
     54   if (unaccelerated_delta == 0.f || accelerated_delta == 0.f)
     55     return 1.f;
     56   return unaccelerated_delta / accelerated_delta;
     57 }
     58 
     59 const char* GetEventAckName(InputEventAckState ack_result) {
     60   switch(ack_result) {
     61     case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN";
     62     case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED";
     63     case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED";
     64     case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS";
     65   default:
     66     DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName.\n";
     67     break;
     68   }
     69   return "";
     70 }
     71 
     72 } // namespace
     73 
     74 ImmediateInputRouter::ImmediateInputRouter(
     75     RenderProcessHost* process,
     76     InputRouterClient* client,
     77     int routing_id)
     78     : process_(process),
     79       client_(client),
     80       routing_id_(routing_id),
     81       select_range_pending_(false),
     82       move_caret_pending_(false),
     83       mouse_move_pending_(false),
     84       mouse_wheel_pending_(false),
     85       has_touch_handler_(false),
     86       touch_event_queue_(new TouchEventQueue(this)),
     87       gesture_event_filter_(new GestureEventFilter(this)) {
     88   DCHECK(process);
     89   DCHECK(client);
     90 }
     91 
     92 ImmediateInputRouter::~ImmediateInputRouter() {
     93 }
     94 
     95 bool ImmediateInputRouter::SendInput(IPC::Message* message) {
     96   DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart);
     97   scoped_ptr<IPC::Message> scoped_message(message);
     98   switch (scoped_message->type()) {
     99     // Check for types that require an ACK.
    100     case InputMsg_SelectRange::ID:
    101       return SendSelectRange(scoped_message.release());
    102     case InputMsg_MoveCaret::ID:
    103       return SendMoveCaret(scoped_message.release());
    104     case InputMsg_HandleInputEvent::ID:
    105       NOTREACHED() << "WebInputEvents should never be sent via SendInput.";
    106       return false;
    107     default:
    108       return Send(scoped_message.release());
    109   }
    110 }
    111 
    112 void ImmediateInputRouter::SendMouseEvent(
    113     const MouseEventWithLatencyInfo& mouse_event) {
    114   if (!client_->OnSendMouseEvent(mouse_event))
    115     return;
    116 
    117   if (mouse_event.event.type == WebInputEvent::MouseDown &&
    118       gesture_event_filter_->GetTouchpadTapSuppressionController()->
    119           ShouldDeferMouseDown(mouse_event))
    120       return;
    121   if (mouse_event.event.type == WebInputEvent::MouseUp &&
    122       gesture_event_filter_->GetTouchpadTapSuppressionController()->
    123           ShouldSuppressMouseUp())
    124       return;
    125 
    126   SendMouseEventImmediately(mouse_event);
    127 }
    128 
    129 void ImmediateInputRouter::SendWheelEvent(
    130     const MouseWheelEventWithLatencyInfo& wheel_event) {
    131   if (!client_->OnSendWheelEvent(wheel_event))
    132     return;
    133 
    134   // If there's already a mouse wheel event waiting to be sent to the renderer,
    135   // add the new deltas to that event. Not doing so (e.g., by dropping the old
    136   // event, as for mouse moves) results in very slow scrolling on the Mac (on
    137   // which many, very small wheel events are sent).
    138   if (mouse_wheel_pending_) {
    139     if (coalesced_mouse_wheel_events_.empty() ||
    140         !ShouldCoalesceMouseWheelEvents(
    141             coalesced_mouse_wheel_events_.back().event, wheel_event.event)) {
    142       coalesced_mouse_wheel_events_.push_back(wheel_event);
    143     } else {
    144       MouseWheelEventWithLatencyInfo* last_wheel_event =
    145           &coalesced_mouse_wheel_events_.back();
    146       float unaccelerated_x =
    147           GetUnacceleratedDelta(last_wheel_event->event.deltaX,
    148                                 last_wheel_event->event.accelerationRatioX) +
    149           GetUnacceleratedDelta(wheel_event.event.deltaX,
    150                                 wheel_event.event.accelerationRatioX);
    151       float unaccelerated_y =
    152           GetUnacceleratedDelta(last_wheel_event->event.deltaY,
    153                                 last_wheel_event->event.accelerationRatioY) +
    154           GetUnacceleratedDelta(wheel_event.event.deltaY,
    155                                 wheel_event.event.accelerationRatioY);
    156       last_wheel_event->event.deltaX += wheel_event.event.deltaX;
    157       last_wheel_event->event.deltaY += wheel_event.event.deltaY;
    158       last_wheel_event->event.wheelTicksX += wheel_event.event.wheelTicksX;
    159       last_wheel_event->event.wheelTicksY += wheel_event.event.wheelTicksY;
    160       last_wheel_event->event.accelerationRatioX =
    161           GetAccelerationRatio(last_wheel_event->event.deltaX, unaccelerated_x);
    162       last_wheel_event->event.accelerationRatioY =
    163           GetAccelerationRatio(last_wheel_event->event.deltaY, unaccelerated_y);
    164       DCHECK_GE(wheel_event.event.timeStampSeconds,
    165                 last_wheel_event->event.timeStampSeconds);
    166       last_wheel_event->event.timeStampSeconds =
    167           wheel_event.event.timeStampSeconds;
    168       last_wheel_event->latency.MergeWith(wheel_event.latency);
    169     }
    170     return;
    171   }
    172   mouse_wheel_pending_ = true;
    173   current_wheel_event_ = wheel_event;
    174 
    175   HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize",
    176                        coalesced_mouse_wheel_events_.size());
    177 
    178   FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false);
    179 }
    180 
    181 void ImmediateInputRouter::SendKeyboardEvent(
    182     const NativeWebKeyboardEvent& key_event,
    183     const ui::LatencyInfo& latency_info) {
    184   bool is_shortcut = false;
    185   if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut))
    186     return;
    187 
    188   // Put all WebKeyboardEvent objects in a queue since we can't trust the
    189   // renderer and we need to give something to the HandleKeyboardEvent
    190   // handler.
    191   key_queue_.push_back(key_event);
    192   HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size());
    193 
    194   gesture_event_filter_->FlingHasBeenHalted();
    195 
    196   // Only forward the non-native portions of our event.
    197   FilterAndSendWebInputEvent(key_event, latency_info, is_shortcut);
    198 }
    199 
    200 void ImmediateInputRouter::SendGestureEvent(
    201     const GestureEventWithLatencyInfo& gesture_event) {
    202   if (!client_->OnSendGestureEvent(gesture_event))
    203     return;
    204   FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
    205 }
    206 
    207 void ImmediateInputRouter::SendTouchEvent(
    208     const TouchEventWithLatencyInfo& touch_event) {
    209   // Always queue TouchEvents, even if the client request they be dropped.
    210   client_->OnSendTouchEvent(touch_event);
    211   touch_event_queue_->QueueEvent(touch_event);
    212 }
    213 
    214 // Forwards MouseEvent without passing it through
    215 // TouchpadTapSuppressionController.
    216 void ImmediateInputRouter::SendMouseEventImmediately(
    217     const MouseEventWithLatencyInfo& mouse_event) {
    218   if (!client_->OnSendMouseEventImmediately(mouse_event))
    219     return;
    220 
    221   // Avoid spamming the renderer with mouse move events.  It is important
    222   // to note that WM_MOUSEMOVE events are anyways synthetic, but since our
    223   // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way
    224   // more WM_MOUSEMOVE events than we wish to send to the renderer.
    225   if (mouse_event.event.type == WebInputEvent::MouseMove) {
    226     if (mouse_move_pending_) {
    227       if (!next_mouse_move_) {
    228         next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event));
    229       } else {
    230         // Accumulate movement deltas.
    231         int x = next_mouse_move_->event.movementX;
    232         int y = next_mouse_move_->event.movementY;
    233         next_mouse_move_->event = mouse_event.event;
    234         next_mouse_move_->event.movementX += x;
    235         next_mouse_move_->event.movementY += y;
    236         next_mouse_move_->latency.MergeWith(mouse_event.latency);
    237       }
    238       return;
    239     }
    240     mouse_move_pending_ = true;
    241   }
    242 
    243   FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false);
    244 }
    245 
    246 void ImmediateInputRouter::SendTouchEventImmediately(
    247     const TouchEventWithLatencyInfo& touch_event) {
    248   if (!client_->OnSendTouchEventImmediately(touch_event))
    249     return;
    250   FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
    251 }
    252 
    253 void ImmediateInputRouter::SendGestureEventImmediately(
    254     const GestureEventWithLatencyInfo& gesture_event) {
    255   if (!client_->OnSendGestureEventImmediately(gesture_event))
    256     return;
    257   FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
    258 }
    259 
    260 const NativeWebKeyboardEvent*
    261     ImmediateInputRouter::GetLastKeyboardEvent() const {
    262   if (key_queue_.empty())
    263     return NULL;
    264   return &key_queue_.front();
    265 }
    266 
    267 bool ImmediateInputRouter::ShouldForwardTouchEvent() const {
    268   // Always send a touch event if the renderer has a touch-event handler. It is
    269   // possible that a renderer stops listening to touch-events while there are
    270   // still events in the touch-queue. In such cases, the new events should still
    271   // get into the queue.
    272   return has_touch_handler_ || !touch_event_queue_->empty();
    273 }
    274 
    275 bool ImmediateInputRouter::ShouldForwardGestureEvent(
    276     const GestureEventWithLatencyInfo& touch_event) const {
    277   return gesture_event_filter_->ShouldForward(touch_event);
    278 }
    279 
    280 bool ImmediateInputRouter::HasQueuedGestureEvents() const {
    281   return gesture_event_filter_->HasQueuedGestureEvents();
    282 }
    283 
    284 bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) {
    285   bool handled = true;
    286   bool message_is_ok = true;
    287   IPC_BEGIN_MESSAGE_MAP_EX(ImmediateInputRouter, message, message_is_ok)
    288     IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck)
    289     IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck)
    290     IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck)
    291     IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
    292                         OnHasTouchEventHandlers)
    293     IPC_MESSAGE_UNHANDLED(handled = false)
    294   IPC_END_MESSAGE_MAP()
    295 
    296   if (!message_is_ok)
    297     client_->OnUnexpectedEventAck(true);
    298 
    299   return handled;
    300 }
    301 
    302 void ImmediateInputRouter::OnTouchEventAck(
    303     const TouchEventWithLatencyInfo& event,
    304     InputEventAckState ack_result) {
    305   client_->OnTouchEventAck(event, ack_result);
    306 }
    307 
    308 bool ImmediateInputRouter::SendSelectRange(IPC::Message* message) {
    309   DCHECK(message->type() == InputMsg_SelectRange::ID);
    310   if (select_range_pending_) {
    311     next_selection_range_.reset(message);
    312     return true;
    313   }
    314 
    315   select_range_pending_ = true;
    316   return Send(message);
    317 }
    318 
    319 bool ImmediateInputRouter::SendMoveCaret(IPC::Message* message) {
    320   DCHECK(message->type() == InputMsg_MoveCaret::ID);
    321   if (move_caret_pending_) {
    322     next_move_caret_.reset(message);
    323     return true;
    324   }
    325 
    326   move_caret_pending_ = true;
    327   return Send(message);
    328 }
    329 
    330 bool ImmediateInputRouter::Send(IPC::Message* message) {
    331   return process_->Send(message);
    332 }
    333 
    334 void ImmediateInputRouter::SendWebInputEvent(
    335     const WebInputEvent& input_event,
    336     const ui::LatencyInfo& latency_info,
    337     bool is_keyboard_shortcut) {
    338   input_event_start_time_ = TimeTicks::Now();
    339   Send(new InputMsg_HandleInputEvent(
    340       routing_id(), &input_event, latency_info, is_keyboard_shortcut));
    341   client_->IncrementInFlightEventCount();
    342 }
    343 
    344 void ImmediateInputRouter::FilterAndSendWebInputEvent(
    345     const WebInputEvent& input_event,
    346     const ui::LatencyInfo& latency_info,
    347     bool is_keyboard_shortcut) {
    348   TRACE_EVENT0("input", "ImmediateInputRouter::FilterAndSendWebInputEvent");
    349 
    350   if (!process_->HasConnection())
    351     return;
    352 
    353   DCHECK(!process_->IgnoreInputEvents());
    354 
    355   // Perform optional, synchronous event handling, sending ACK messages for
    356   // processed events, or proceeding as usual.
    357   InputEventAckState filter_ack = client_->FilterInputEvent(input_event,
    358                                                             latency_info);
    359   switch (filter_ack) {
    360     // Send the ACK and early exit.
    361     case INPUT_EVENT_ACK_STATE_CONSUMED:
    362     case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS:
    363       next_mouse_move_.reset();
    364       ProcessInputEventAck(input_event.type, filter_ack, latency_info);
    365       // WARNING: |this| may be deleted at this point.
    366       return;
    367 
    368     case INPUT_EVENT_ACK_STATE_UNKNOWN: {
    369       if (input_event.type == WebKit::WebInputEvent::MouseMove) {
    370         // Since this mouse-move event has been consumed, there will be no ACKs.
    371         // So reset the state here so that future mouse-move events do reach the
    372         // renderer.
    373         mouse_move_pending_ = false;
    374       } else if (input_event.type == WebKit::WebInputEvent::MouseWheel) {
    375         // Reset the wheel-event state when appropriate.
    376         mouse_wheel_pending_ = false;
    377       } else if (WebInputEvent::isGestureEventType(input_event.type) &&
    378                  gesture_event_filter_->HasQueuedGestureEvents()) {
    379         // If the gesture-event filter has queued gesture events, that implies
    380         // it's awaiting an ack for the event. Since the event is being dropped,
    381         // it is never sent to the renderer, and so it won't receive any ACKs.
    382         // So send the ACK to the gesture event filter immediately, and mark it
    383         // as having been processed.
    384         gesture_event_filter_->ProcessGestureAck(true, input_event.type);
    385       } else if (WebInputEvent::isTouchEventType(input_event.type)) {
    386         // During an overscroll gesture initiated by touch-scrolling, the
    387         // touch-events do not reset or contribute to the overscroll gesture.
    388         // However, the touch-events are not sent to the renderer. So send an
    389         // ACK to the touch-event queue immediately. Mark the event as not
    390         // processed, to make sure that the touch-scroll gesture that initiated
    391         // the overscroll is updated properly.
    392         touch_event_queue_->ProcessTouchAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
    393                                             latency_info);
    394       }
    395       return;
    396     }
    397 
    398     // Proceed as normal.
    399     case INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
    400       break;
    401   };
    402 
    403   // Transmit any pending wheel events on a non-wheel event. This ensures that
    404   // the renderer receives the final PhaseEnded wheel event, which is necessary
    405   // to terminate rubber-banding, for example.
    406   if (input_event.type != WebInputEvent::MouseWheel) {
    407     for (size_t i = 0; i < coalesced_mouse_wheel_events_.size(); ++i) {
    408       SendWebInputEvent(coalesced_mouse_wheel_events_[i].event,
    409                        coalesced_mouse_wheel_events_[i].latency,
    410                       false);
    411     }
    412     coalesced_mouse_wheel_events_.clear();
    413   }
    414 
    415   SendWebInputEvent(input_event, latency_info, is_keyboard_shortcut);
    416 
    417   // Any input event cancels a pending mouse move event.
    418   next_mouse_move_.reset();
    419 }
    420 
    421 void ImmediateInputRouter::OnInputEventAck(
    422     WebInputEvent::Type event_type,
    423     InputEventAckState ack_result,
    424     const ui::LatencyInfo& latency_info) {
    425   // Log the time delta for processing an input event.
    426   TimeDelta delta = TimeTicks::Now() - input_event_start_time_;
    427   UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta);
    428 
    429   client_->DecrementInFlightEventCount();
    430 
    431   ProcessInputEventAck(event_type, ack_result, latency_info);
    432 }
    433 
    434 void ImmediateInputRouter::OnMsgMoveCaretAck() {
    435   move_caret_pending_ = false;
    436   if (next_move_caret_)
    437     SendMoveCaret(next_move_caret_.release());
    438 }
    439 
    440 void ImmediateInputRouter::OnSelectRangeAck() {
    441   select_range_pending_ = false;
    442   if (next_selection_range_)
    443     SendSelectRange(next_selection_range_.release());
    444 }
    445 
    446 void ImmediateInputRouter::OnHasTouchEventHandlers(bool has_handlers) {
    447  if (has_touch_handler_ == has_handlers)
    448     return;
    449   has_touch_handler_ = has_handlers;
    450   if (!has_handlers)
    451     touch_event_queue_->FlushQueue();
    452   client_->OnHasTouchEventHandlers(has_handlers);
    453 }
    454 
    455 void ImmediateInputRouter::ProcessInputEventAck(
    456     WebInputEvent::Type event_type,
    457     InputEventAckState ack_result,
    458     const ui::LatencyInfo& latency_info) {
    459   TRACE_EVENT1("input", "ImmediateInputRouter::ProcessInputEventAck",
    460                "ack", GetEventAckName(ack_result));
    461 
    462   int type = static_cast<int>(event_type);
    463   if (type < WebInputEvent::Undefined) {
    464     client_->OnUnexpectedEventAck(true);
    465   } else if (type == WebInputEvent::MouseMove) {
    466     mouse_move_pending_ = false;
    467 
    468     // now, we can send the next mouse move event
    469     if (next_mouse_move_) {
    470       DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove);
    471       scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move
    472           = next_mouse_move_.Pass();
    473       SendMouseEvent(*next_mouse_move);
    474     }
    475   } else if (WebInputEvent::isKeyboardEventType(type)) {
    476     ProcessKeyboardAck(type, ack_result);
    477   } else if (type == WebInputEvent::MouseWheel) {
    478     ProcessWheelAck(ack_result);
    479   } else if (WebInputEvent::isTouchEventType(type)) {
    480     ProcessTouchAck(ack_result, latency_info);
    481   } else if (WebInputEvent::isGestureEventType(type)) {
    482     ProcessGestureAck(type, ack_result);
    483   }
    484 
    485   // WARNING: |this| may be deleted at this point.
    486 
    487   // This is used only for testing, and the other end does not use the
    488   // source object.  On linux, specifying
    489   // Source<RenderWidgetHost> results in a very strange
    490   // runtime error in the epilogue of the enclosing
    491   // (ProcessInputEventAck) method, but not on other platforms; using
    492   // 'void' instead is just as safe (since NotificationSource
    493   // is not actually typesafe) and avoids this error.
    494   NotificationService::current()->Notify(
    495       NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
    496       Source<void>(this),
    497       Details<int>(&type));
    498 }
    499 
    500 void ImmediateInputRouter::ProcessKeyboardAck(
    501     int type,
    502     InputEventAckState ack_result) {
    503   if (key_queue_.empty()) {
    504     LOG(ERROR) << "Got a KeyEvent back from the renderer but we "
    505                << "don't seem to have sent it to the renderer!";
    506   } else if (key_queue_.front().type != type) {
    507     LOG(ERROR) << "We seem to have a different key type sent from "
    508                << "the renderer. (" << key_queue_.front().type << " vs. "
    509                << type << "). Ignoring event.";
    510 
    511     // Something must be wrong. Clear the |key_queue_| and char event
    512     // suppression so that we can resume from the error.
    513     key_queue_.clear();
    514     client_->OnUnexpectedEventAck(false);
    515   } else {
    516     NativeWebKeyboardEvent front_item = key_queue_.front();
    517     key_queue_.pop_front();
    518 
    519     client_->OnKeyboardEventAck(front_item, ack_result);
    520 
    521     // WARNING: This ImmediateInputRouter can be deallocated at this point
    522     // (i.e.  in the case of Ctrl+W, where the call to
    523     // HandleKeyboardEvent destroys this ImmediateInputRouter).
    524   }
    525 }
    526 
    527 void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) {
    528   mouse_wheel_pending_ = false;
    529 
    530   // Process the unhandled wheel event here before calling
    531   // ForwardWheelEventWithLatencyInfo() since it will mutate
    532   // current_wheel_event_.
    533   client_->OnWheelEventAck(current_wheel_event_.event, ack_result);
    534 
    535   // Now send the next (coalesced) mouse wheel event.
    536   if (!coalesced_mouse_wheel_events_.empty()) {
    537     MouseWheelEventWithLatencyInfo next_wheel_event =
    538         coalesced_mouse_wheel_events_.front();
    539     coalesced_mouse_wheel_events_.pop_front();
    540     SendWheelEvent(next_wheel_event);
    541   }
    542 }
    543 
    544 void ImmediateInputRouter::ProcessGestureAck(int type,
    545                                              InputEventAckState ack_result) {
    546   const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
    547   client_->OnGestureEventAck(
    548       gesture_event_filter_->GetGestureEventAwaitingAck(), ack_result);
    549   gesture_event_filter_->ProcessGestureAck(processed, type);
    550 }
    551 
    552 void ImmediateInputRouter::ProcessTouchAck(
    553     InputEventAckState ack_result,
    554     const ui::LatencyInfo& latency_info) {
    555   // |touch_event_queue_| will forward to OnTouchEventAck when appropriate.
    556   touch_event_queue_->ProcessTouchAck(ack_result, latency_info);
    557 }
    558 
    559 }  // namespace content
    560