input_router_impl.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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 "base/auto_reset.h"
8#include "base/command_line.h"
9#include "base/metrics/histogram.h"
10#include "base/strings/string_number_conversions.h"
11#include "content/browser/renderer_host/input/gesture_event_queue.h"
12#include "content/browser/renderer_host/input/input_ack_handler.h"
13#include "content/browser/renderer_host/input/input_router_client.h"
14#include "content/browser/renderer_host/input/touch_event_queue.h"
15#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
16#include "content/browser/renderer_host/input/web_touch_event_traits.h"
17#include "content/browser/renderer_host/overscroll_controller.h"
18#include "content/common/content_constants_internal.h"
19#include "content/common/edit_command.h"
20#include "content/common/input/touch_action.h"
21#include "content/common/input_messages.h"
22#include "content/common/view_messages.h"
23#include "content/port/common/input_event_ack_state.h"
24#include "content/public/browser/notification_service.h"
25#include "content/public/browser/notification_types.h"
26#include "content/public/browser/user_metrics.h"
27#include "content/public/common/content_switches.h"
28#include "ipc/ipc_sender.h"
29#include "ui/events/event.h"
30#include "ui/events/keycodes/keyboard_codes.h"
31
32#if defined(OS_ANDROID)
33#include "ui/gfx/android/view_configuration.h"
34#include "ui/gfx/screen.h"
35#else
36#include "ui/events/gestures/gesture_configuration.h"
37#endif
38
39using base::Time;
40using base::TimeDelta;
41using base::TimeTicks;
42using blink::WebGestureEvent;
43using blink::WebInputEvent;
44using blink::WebKeyboardEvent;
45using blink::WebMouseEvent;
46using blink::WebMouseWheelEvent;
47
48namespace content {
49namespace {
50
51// TODO(jdduke): Instead of relying on command line flags or conditional
52// conditional compilation here, we should instead use an InputRouter::Settings
53// construct, supplied and customized by the RenderWidgetHostView. See
54// crbug.com/343917.
55bool GetTouchAckTimeoutDelay(base::TimeDelta* touch_ack_timeout_delay) {
56  CommandLine* parsed_command_line = CommandLine::ForCurrentProcess();
57  if (!parsed_command_line->HasSwitch(switches::kTouchAckTimeoutDelayMs))
58    return false;
59
60  std::string timeout_string = parsed_command_line->GetSwitchValueASCII(
61      switches::kTouchAckTimeoutDelayMs);
62  size_t timeout_ms;
63  if (!base::StringToSizeT(timeout_string, &timeout_ms))
64    return false;
65
66  *touch_ack_timeout_delay = base::TimeDelta::FromMilliseconds(timeout_ms);
67  return true;
68}
69
70#if defined(OS_ANDROID)
71double GetTouchMoveSlopSuppressionLengthDips() {
72  const double touch_slop_length_pixels =
73      static_cast<double>(gfx::ViewConfiguration::GetTouchSlopInPixels());
74  const double device_scale_factor =
75      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().device_scale_factor();
76  return touch_slop_length_pixels / device_scale_factor;
77}
78#elif defined(USE_AURA)
79double GetTouchMoveSlopSuppressionLengthDips() {
80  return ui::GestureConfiguration::max_touch_move_in_pixels_for_click();
81}
82#else
83double GetTouchMoveSlopSuppressionLengthDips() {
84  return 0;
85}
86#endif
87
88TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() {
89  std::string modeString = CommandLine::ForCurrentProcess()->
90      GetSwitchValueASCII(switches::kTouchScrollingMode);
91  if (modeString == switches::kTouchScrollingModeSyncTouchmove)
92    return TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE;
93  if (modeString == switches::kTouchScrollingModeAbsorbTouchmove)
94    return TouchEventQueue::TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE;
95  if (modeString == switches::kTouchScrollingModeTouchcancel)
96    return TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL;
97  if (modeString != "")
98    LOG(ERROR) << "Invalid --touch-scrolling-mode option: " << modeString;
99  return TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT;
100}
101
102GestureEventWithLatencyInfo MakeGestureEvent(WebInputEvent::Type type,
103                                             double timestamp_seconds,
104                                             int x,
105                                             int y,
106                                             int modifiers,
107                                             const ui::LatencyInfo& latency) {
108  WebGestureEvent result;
109
110  result.type = type;
111  result.x = x;
112  result.y = y;
113  result.sourceDevice = WebGestureEvent::Touchscreen;
114  result.timeStampSeconds = timestamp_seconds;
115  result.modifiers = modifiers;
116
117  return GestureEventWithLatencyInfo(result, latency);
118}
119
120const char* GetEventAckName(InputEventAckState ack_result) {
121  switch(ack_result) {
122    case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN";
123    case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED";
124    case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED";
125    case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS";
126    case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED";
127  }
128  DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName.";
129  return "";
130}
131
132} // namespace
133
134InputRouterImpl::InputRouterImpl(IPC::Sender* sender,
135                                 InputRouterClient* client,
136                                 InputAckHandler* ack_handler,
137                                 int routing_id)
138    : sender_(sender),
139      client_(client),
140      ack_handler_(ack_handler),
141      routing_id_(routing_id),
142      select_range_pending_(false),
143      move_caret_pending_(false),
144      mouse_move_pending_(false),
145      mouse_wheel_pending_(false),
146      touch_ack_timeout_supported_(false),
147      current_view_flags_(0),
148      current_ack_source_(ACK_SOURCE_NONE),
149      gesture_event_queue_(new GestureEventQueue(this, this)) {
150  DCHECK(sender);
151  DCHECK(client);
152  DCHECK(ack_handler);
153  touch_event_queue_.reset(new TouchEventQueue(
154      this, GetTouchScrollingMode(), GetTouchMoveSlopSuppressionLengthDips()));
155  touch_ack_timeout_supported_ =
156      GetTouchAckTimeoutDelay(&touch_ack_timeout_delay_);
157  UpdateTouchAckTimeoutEnabled();
158}
159
160InputRouterImpl::~InputRouterImpl() {}
161
162void InputRouterImpl::Flush() {}
163
164bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) {
165  DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart);
166  switch (message->type()) {
167    // Check for types that require an ACK.
168    case InputMsg_SelectRange::ID:
169      return SendSelectRange(message.Pass());
170    case InputMsg_MoveCaret::ID:
171      return SendMoveCaret(message.Pass());
172    case InputMsg_HandleInputEvent::ID:
173      NOTREACHED() << "WebInputEvents should never be sent via SendInput.";
174      return false;
175    default:
176      return Send(message.release());
177  }
178}
179
180void InputRouterImpl::SendMouseEvent(
181    const MouseEventWithLatencyInfo& mouse_event) {
182  // Order is important here; we need to convert all MouseEvents before they
183  // propagate further, e.g., to the tap suppression controller.
184  if (CommandLine::ForCurrentProcess()->HasSwitch(
185          switches::kSimulateTouchScreenWithMouse)) {
186    SimulateTouchGestureWithMouse(mouse_event);
187    return;
188  }
189
190  if (mouse_event.event.type == WebInputEvent::MouseDown &&
191      gesture_event_queue_->GetTouchpadTapSuppressionController()->
192          ShouldDeferMouseDown(mouse_event))
193      return;
194  if (mouse_event.event.type == WebInputEvent::MouseUp &&
195      gesture_event_queue_->GetTouchpadTapSuppressionController()->
196          ShouldSuppressMouseUp())
197      return;
198
199  SendMouseEventImmediately(mouse_event);
200}
201
202void InputRouterImpl::SendWheelEvent(
203    const MouseWheelEventWithLatencyInfo& wheel_event) {
204  // If there's already a mouse wheel event waiting to be sent to the renderer,
205  // add the new deltas to that event. Not doing so (e.g., by dropping the old
206  // event, as for mouse moves) results in very slow scrolling on the Mac (on
207  // which many, very small wheel events are sent).
208  if (mouse_wheel_pending_) {
209    if (coalesced_mouse_wheel_events_.empty() ||
210        !coalesced_mouse_wheel_events_.back().CanCoalesceWith(wheel_event)) {
211      coalesced_mouse_wheel_events_.push_back(wheel_event);
212    } else {
213      coalesced_mouse_wheel_events_.back().CoalesceWith(wheel_event);
214    }
215    return;
216  }
217  mouse_wheel_pending_ = true;
218  current_wheel_event_ = wheel_event;
219
220  HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize",
221                       coalesced_mouse_wheel_events_.size());
222
223  FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false);
224}
225
226void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event,
227                                        const ui::LatencyInfo& latency_info,
228                                        bool is_keyboard_shortcut) {
229  // Put all WebKeyboardEvent objects in a queue since we can't trust the
230  // renderer and we need to give something to the HandleKeyboardEvent
231  // handler.
232  key_queue_.push_back(key_event);
233  HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size());
234
235  gesture_event_queue_->FlingHasBeenHalted();
236
237  // Only forward the non-native portions of our event.
238  FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut);
239}
240
241void InputRouterImpl::SendGestureEvent(
242    const GestureEventWithLatencyInfo& original_gesture_event) {
243  event_stream_validator_.OnEvent(original_gesture_event.event);
244  GestureEventWithLatencyInfo gesture_event(original_gesture_event);
245
246  if (touch_action_filter_.FilterGestureEvent(&gesture_event.event))
247    return;
248
249  touch_event_queue_->OnGestureScrollEvent(gesture_event);
250
251  if (!IsInOverscrollGesture() &&
252      !gesture_event_queue_->ShouldForward(gesture_event)) {
253    OverscrollController* controller = client_->GetOverscrollController();
254    if (controller)
255      controller->DiscardingGestureEvent(gesture_event.event);
256    return;
257  }
258
259  FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
260}
261
262void InputRouterImpl::SendTouchEvent(
263    const TouchEventWithLatencyInfo& touch_event) {
264  touch_event_queue_->QueueEvent(touch_event);
265}
266
267// Forwards MouseEvent without passing it through
268// TouchpadTapSuppressionController.
269void InputRouterImpl::SendMouseEventImmediately(
270    const MouseEventWithLatencyInfo& mouse_event) {
271  // Avoid spamming the renderer with mouse move events.  It is important
272  // to note that WM_MOUSEMOVE events are anyways synthetic, but since our
273  // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way
274  // more WM_MOUSEMOVE events than we wish to send to the renderer.
275  if (mouse_event.event.type == WebInputEvent::MouseMove) {
276    if (mouse_move_pending_) {
277      if (!next_mouse_move_)
278        next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event));
279      else
280        next_mouse_move_->CoalesceWith(mouse_event);
281      return;
282    }
283    mouse_move_pending_ = true;
284  }
285
286  FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false);
287}
288
289void InputRouterImpl::SendTouchEventImmediately(
290    const TouchEventWithLatencyInfo& touch_event) {
291  if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) {
292    touch_action_filter_.ResetTouchAction();
293    // Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling
294    // the timeout here will not take effect until the *following* touch
295    // sequence.  This is a desirable side-effect, giving the renderer a chance
296    // to send a touch-action response without racing against the ack timeout.
297    UpdateTouchAckTimeoutEnabled();
298  }
299
300  FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false);
301}
302
303void InputRouterImpl::SendGestureEventImmediately(
304    const GestureEventWithLatencyInfo& gesture_event) {
305  FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false);
306}
307
308const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const {
309  if (key_queue_.empty())
310    return NULL;
311  return &key_queue_.front();
312}
313
314bool InputRouterImpl::ShouldForwardTouchEvent() const {
315  // Always send a touch event if the renderer has a touch-event handler or
316  // there are pending touch events.
317  return touch_event_queue_->has_handlers() || !touch_event_queue_->empty();
318}
319
320void InputRouterImpl::OnViewUpdated(int view_flags) {
321  current_view_flags_ = view_flags;
322
323  // A fixed page scale or mobile viewport should disable the touch ack timeout.
324  UpdateTouchAckTimeoutEnabled();
325}
326
327bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) {
328  bool handled = true;
329  bool message_is_ok = true;
330  IPC_BEGIN_MESSAGE_MAP_EX(InputRouterImpl, message, message_is_ok)
331    IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck)
332    IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck)
333    IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck)
334    IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
335                        OnHasTouchEventHandlers)
336    IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction,
337                        OnSetTouchAction)
338    IPC_MESSAGE_UNHANDLED(handled = false)
339  IPC_END_MESSAGE_MAP()
340
341  if (!message_is_ok)
342    ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
343
344  return handled;
345}
346
347void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event,
348                                      InputEventAckState ack_result) {
349  // Touchstart events sent to the renderer indicate a new touch sequence, but
350  // in some cases we may filter out sending the touchstart - catch those here.
351  if (WebTouchEventTraits::IsTouchSequenceStart(event.event) &&
352      ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
353    touch_action_filter_.ResetTouchAction();
354    UpdateTouchAckTimeoutEnabled();
355  }
356  ack_handler_->OnTouchEventAck(event, ack_result);
357}
358
359void InputRouterImpl::OnGestureEventAck(
360    const GestureEventWithLatencyInfo& event,
361    InputEventAckState ack_result) {
362  touch_event_queue_->OnGestureEventAck(event, ack_result);
363  ProcessAckForOverscroll(event.event, ack_result);
364  ack_handler_->OnGestureEventAck(event, ack_result);
365}
366
367bool InputRouterImpl::SendSelectRange(scoped_ptr<IPC::Message> message) {
368  DCHECK(message->type() == InputMsg_SelectRange::ID);
369  if (select_range_pending_) {
370    next_selection_range_ = message.Pass();
371    return true;
372  }
373
374  select_range_pending_ = true;
375  return Send(message.release());
376}
377
378bool InputRouterImpl::SendMoveCaret(scoped_ptr<IPC::Message> message) {
379  DCHECK(message->type() == InputMsg_MoveCaret::ID);
380  if (move_caret_pending_) {
381    next_move_caret_ = message.Pass();
382    return true;
383  }
384
385  move_caret_pending_ = true;
386  return Send(message.release());
387}
388
389bool InputRouterImpl::Send(IPC::Message* message) {
390  return sender_->Send(message);
391}
392
393void InputRouterImpl::FilterAndSendWebInputEvent(
394    const WebInputEvent& input_event,
395    const ui::LatencyInfo& latency_info,
396    bool is_keyboard_shortcut) {
397  TRACE_EVENT1("input",
398               "InputRouterImpl::FilterAndSendWebInputEvent",
399               "type",
400               WebInputEventTraits::GetName(input_event.type));
401
402  // Transmit any pending wheel events on a non-wheel event. This ensures that
403  // final PhaseEnded wheel event is received, which is necessary to terminate
404  // rubber-banding, for example.
405   if (input_event.type != WebInputEvent::MouseWheel) {
406    WheelEventQueue mouse_wheel_events;
407    mouse_wheel_events.swap(coalesced_mouse_wheel_events_);
408    for (size_t i = 0; i < mouse_wheel_events.size(); ++i) {
409      OfferToHandlers(mouse_wheel_events[i].event,
410                      mouse_wheel_events[i].latency,
411                      false);
412     }
413  }
414
415  // Any input event cancels a pending mouse move event.
416  next_mouse_move_.reset();
417
418  OfferToHandlers(input_event, latency_info, is_keyboard_shortcut);
419}
420
421void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event,
422                                      const ui::LatencyInfo& latency_info,
423                                      bool is_keyboard_shortcut) {
424  // Trackpad pinch gestures are not yet handled by the renderer.
425  // TODO(rbyers): Send mousewheel for trackpad pinch - crbug.com/289887.
426  if (input_event.type == WebInputEvent::GesturePinchUpdate &&
427      static_cast<const WebGestureEvent&>(input_event).sourceDevice ==
428          WebGestureEvent::Touchpad) {
429    ProcessInputEventAck(input_event.type,
430                         INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
431                         latency_info,
432                         ACK_SOURCE_NONE);
433    return;
434  }
435
436  if (OfferToOverscrollController(input_event, latency_info))
437    return;
438
439  if (OfferToClient(input_event, latency_info))
440    return;
441
442  OfferToRenderer(input_event, latency_info, is_keyboard_shortcut);
443
444  // If we don't care about the ack disposition, send the ack immediately.
445  if (WebInputEventTraits::IgnoresAckDisposition(input_event.type)) {
446    ProcessInputEventAck(input_event.type,
447                         INPUT_EVENT_ACK_STATE_IGNORED,
448                         latency_info,
449                         IGNORING_DISPOSITION);
450  }
451}
452
453bool InputRouterImpl::OfferToOverscrollController(
454    const WebInputEvent& input_event,
455    const ui::LatencyInfo& latency_info) {
456  OverscrollController* controller = client_->GetOverscrollController();
457  if (!controller)
458    return false;
459
460  OverscrollController::Disposition disposition =
461      controller->DispatchEvent(input_event, latency_info);
462
463  bool consumed = disposition == OverscrollController::CONSUMED;
464
465  if (disposition == OverscrollController::SHOULD_FORWARD_TO_GESTURE_QUEUE) {
466    DCHECK(WebInputEvent::isGestureEventType(input_event.type));
467    const blink::WebGestureEvent& gesture_event =
468        static_cast<const blink::WebGestureEvent&>(input_event);
469    // An ACK is expected for the event, so mark it as consumed.
470    consumed = !gesture_event_queue_->ShouldForward(
471        GestureEventWithLatencyInfo(gesture_event, latency_info));
472  }
473
474  if (consumed) {
475    InputEventAckState overscroll_ack =
476        WebInputEvent::isTouchEventType(input_event.type) ?
477            INPUT_EVENT_ACK_STATE_NOT_CONSUMED : INPUT_EVENT_ACK_STATE_CONSUMED;
478    ProcessInputEventAck(input_event.type,
479                         overscroll_ack,
480                         latency_info,
481                         OVERSCROLL_CONTROLLER);
482    // WARNING: |this| may be deleted at this point.
483  }
484
485  return consumed;
486}
487
488bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event,
489                                    const ui::LatencyInfo& latency_info) {
490  bool consumed = false;
491
492  InputEventAckState filter_ack =
493      client_->FilterInputEvent(input_event, latency_info);
494  switch (filter_ack) {
495    case INPUT_EVENT_ACK_STATE_CONSUMED:
496    case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS:
497      // Send the ACK and early exit.
498      next_mouse_move_.reset();
499      ProcessInputEventAck(input_event.type, filter_ack, latency_info, CLIENT);
500      // WARNING: |this| may be deleted at this point.
501      consumed = true;
502      break;
503    case INPUT_EVENT_ACK_STATE_UNKNOWN:
504      // Simply drop the event.
505      consumed = true;
506      break;
507    default:
508      break;
509  }
510
511  return consumed;
512}
513
514bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
515                                      const ui::LatencyInfo& latency_info,
516                                      bool is_keyboard_shortcut) {
517  if (Send(new InputMsg_HandleInputEvent(
518          routing_id(), &input_event, latency_info, is_keyboard_shortcut))) {
519    // Ack messages for ignored ack event types are not required, and might
520    // never be sent by the renderer. Consequently, such event types should not
521    // affect event timing or in-flight event count metrics.
522    if (!WebInputEventTraits::IgnoresAckDisposition(input_event.type)) {
523      input_event_start_time_ = TimeTicks::Now();
524      client_->IncrementInFlightEventCount();
525    }
526    return true;
527  }
528  return false;
529}
530
531void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type,
532                                      InputEventAckState ack_result,
533                                      const ui::LatencyInfo& latency_info) {
534  // A synthetic ack will already have been sent for this event, and it should
535  // not affect event timing or in-flight count metrics.
536  if (WebInputEventTraits::IgnoresAckDisposition(event_type))
537    return;
538
539  client_->DecrementInFlightEventCount();
540
541  // Log the time delta for processing an input event.
542  TimeDelta delta = TimeTicks::Now() - input_event_start_time_;
543  UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta);
544
545  ProcessInputEventAck(event_type, ack_result, latency_info, RENDERER);
546  // WARNING: |this| may be deleted at this point.
547
548  // This is used only for testing, and the other end does not use the
549  // source object.  On linux, specifying
550  // Source<RenderWidgetHost> results in a very strange
551  // runtime error in the epilogue of the enclosing
552  // (ProcessInputEventAck) method, but not on other platforms; using
553  // 'void' instead is just as safe (since NotificationSource
554  // is not actually typesafe) and avoids this error.
555  int type = static_cast<int>(event_type);
556  NotificationService::current()->Notify(
557      NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
558      Source<void>(this),
559      Details<int>(&type));
560}
561
562void InputRouterImpl::OnMsgMoveCaretAck() {
563  move_caret_pending_ = false;
564  if (next_move_caret_)
565    SendMoveCaret(next_move_caret_.Pass());
566}
567
568void InputRouterImpl::OnSelectRangeAck() {
569  select_range_pending_ = false;
570  if (next_selection_range_)
571    SendSelectRange(next_selection_range_.Pass());
572}
573
574void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) {
575  TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers",
576               "has_handlers", has_handlers);
577
578  touch_event_queue_->OnHasTouchEventHandlers(has_handlers);
579  client_->OnHasTouchEventHandlers(has_handlers);
580}
581
582void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) {
583  // Synthetic touchstart events should get filtered out in RenderWidget.
584  DCHECK(touch_event_queue_->IsPendingAckTouchStart());
585  TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction",
586               "action", touch_action);
587
588  touch_action_filter_.OnSetTouchAction(touch_action);
589
590  // TOUCH_ACTION_NONE should disable the touch ack timeout.
591  UpdateTouchAckTimeoutEnabled();
592}
593
594void InputRouterImpl::ProcessInputEventAck(
595    WebInputEvent::Type event_type,
596    InputEventAckState ack_result,
597    const ui::LatencyInfo& latency_info,
598    AckSource ack_source) {
599  TRACE_EVENT2("input", "InputRouterImpl::ProcessInputEventAck",
600               "type", WebInputEventTraits::GetName(event_type),
601               "ack", GetEventAckName(ack_result));
602
603  // Note: The keyboard ack must be treated carefully, as it may result in
604  // synchronous destruction of |this|. Handling immediately guards against
605  // future references to |this|, as with |auto_reset_current_ack_source| below.
606  if (WebInputEvent::isKeyboardEventType(event_type)) {
607    ProcessKeyboardAck(event_type, ack_result);
608    // WARNING: |this| may be deleted at this point.
609    return;
610  }
611
612  base::AutoReset<AckSource> auto_reset_current_ack_source(
613      &current_ack_source_, ack_source);
614
615  if (WebInputEvent::isMouseEventType(event_type)) {
616    ProcessMouseAck(event_type, ack_result);
617  } else if (event_type == WebInputEvent::MouseWheel) {
618    ProcessWheelAck(ack_result, latency_info);
619  } else if (WebInputEvent::isTouchEventType(event_type)) {
620    ProcessTouchAck(ack_result, latency_info);
621  } else if (WebInputEvent::isGestureEventType(event_type)) {
622    ProcessGestureAck(event_type, ack_result, latency_info);
623  } else if (event_type != WebInputEvent::Undefined) {
624    ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
625  }
626}
627
628void InputRouterImpl::ProcessKeyboardAck(blink::WebInputEvent::Type type,
629                                         InputEventAckState ack_result) {
630  if (key_queue_.empty()) {
631    ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK);
632  } else if (key_queue_.front().type != type) {
633    // Something must be wrong. Clear the |key_queue_| and char event
634    // suppression so that we can resume from the error.
635    key_queue_.clear();
636    ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE);
637  } else {
638    NativeWebKeyboardEvent front_item = key_queue_.front();
639    key_queue_.pop_front();
640
641    ack_handler_->OnKeyboardEventAck(front_item, ack_result);
642    // WARNING: This InputRouterImpl can be deallocated at this point
643    // (i.e.  in the case of Ctrl+W, where the call to
644    // HandleKeyboardEvent destroys this InputRouterImpl).
645    // TODO(jdduke): crbug.com/274029 - Make ack-triggered shutdown async.
646  }
647}
648
649void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type,
650                                      InputEventAckState ack_result) {
651  if (type != WebInputEvent::MouseMove)
652    return;
653
654  mouse_move_pending_ = false;
655
656  if (next_mouse_move_) {
657    DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove);
658    scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move
659        = next_mouse_move_.Pass();
660    SendMouseEvent(*next_mouse_move);
661  }
662}
663
664void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result,
665                                      const ui::LatencyInfo& latency) {
666  ProcessAckForOverscroll(current_wheel_event_.event, ack_result);
667
668  // TODO(miletus): Add renderer side latency to each uncoalesced mouse
669  // wheel event and add terminal component to each of them.
670  current_wheel_event_.latency.AddNewLatencyFrom(latency);
671  // Process the unhandled wheel event here before calling SendWheelEvent()
672  // since it will mutate current_wheel_event_.
673  ack_handler_->OnWheelEventAck(current_wheel_event_, ack_result);
674  mouse_wheel_pending_ = false;
675
676  // Now send the next (coalesced) mouse wheel event.
677  if (!coalesced_mouse_wheel_events_.empty()) {
678    MouseWheelEventWithLatencyInfo next_wheel_event =
679        coalesced_mouse_wheel_events_.front();
680    coalesced_mouse_wheel_events_.pop_front();
681    SendWheelEvent(next_wheel_event);
682  }
683}
684
685void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type,
686                                        InputEventAckState ack_result,
687                                        const ui::LatencyInfo& latency) {
688  // If |ack_result| originated from the overscroll controller, only
689  // feed |gesture_event_queue_| the ack if it was expecting one.
690  if (current_ack_source_ == OVERSCROLL_CONTROLLER &&
691      !gesture_event_queue_->HasQueuedGestureEvents()) {
692    return;
693  }
694
695  // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate.
696  gesture_event_queue_->ProcessGestureAck(ack_result, type, latency);
697}
698
699void InputRouterImpl::ProcessTouchAck(
700    InputEventAckState ack_result,
701    const ui::LatencyInfo& latency) {
702  // |touch_event_queue_| will forward to OnTouchEventAck when appropriate.
703  touch_event_queue_->ProcessTouchAck(ack_result, latency);
704}
705
706void InputRouterImpl::ProcessAckForOverscroll(const WebInputEvent& event,
707                                              InputEventAckState ack_result) {
708  // Acks sent from the overscroll controller need not be fed back into the
709  // overscroll controller.
710  if (current_ack_source_ == OVERSCROLL_CONTROLLER)
711    return;
712
713  OverscrollController* controller = client_->GetOverscrollController();
714  if (!controller)
715    return;
716
717  controller->ReceivedEventACK(
718      event, (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result));
719}
720
721void InputRouterImpl::SimulateTouchGestureWithMouse(
722    const MouseEventWithLatencyInfo& event) {
723  const WebMouseEvent& mouse_event = event.event;
724  int x = mouse_event.x, y = mouse_event.y;
725  float dx = mouse_event.movementX, dy = mouse_event.movementY;
726  static int startX = 0, startY = 0;
727
728  switch (mouse_event.button) {
729    case WebMouseEvent::ButtonLeft:
730      if (mouse_event.type == WebInputEvent::MouseDown) {
731        startX = x;
732        startY = y;
733        SendGestureEvent(MakeGestureEvent(
734            WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds,
735            x, y, 0, event.latency));
736      }
737      if (dx != 0 || dy != 0) {
738        GestureEventWithLatencyInfo gesture_event = MakeGestureEvent(
739            WebInputEvent::GestureScrollUpdate, mouse_event.timeStampSeconds,
740            x, y, 0, event.latency);
741        gesture_event.event.data.scrollUpdate.deltaX = dx;
742        gesture_event.event.data.scrollUpdate.deltaY = dy;
743        SendGestureEvent(gesture_event);
744      }
745      if (mouse_event.type == WebInputEvent::MouseUp) {
746        SendGestureEvent(MakeGestureEvent(
747            WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds,
748            x, y, 0, event.latency));
749      }
750      break;
751    case WebMouseEvent::ButtonMiddle:
752      if (mouse_event.type == WebInputEvent::MouseDown) {
753        startX = x;
754        startY = y;
755        SendGestureEvent(MakeGestureEvent(
756            WebInputEvent::GestureShowPress, mouse_event.timeStampSeconds,
757            x, y, 0, event.latency));
758        SendGestureEvent(MakeGestureEvent(
759            WebInputEvent::GestureTapDown, mouse_event.timeStampSeconds,
760            x, y, 0, event.latency));
761      }
762      if (mouse_event.type == WebInputEvent::MouseUp) {
763        SendGestureEvent(MakeGestureEvent(
764            WebInputEvent::GestureTap, mouse_event.timeStampSeconds,
765            x, y, 0, event.latency));
766      }
767      break;
768    case WebMouseEvent::ButtonRight:
769      if (mouse_event.type == WebInputEvent::MouseDown) {
770        startX = x;
771        startY = y;
772        SendGestureEvent(MakeGestureEvent(
773            WebInputEvent::GestureScrollBegin, mouse_event.timeStampSeconds,
774            x, y, 0, event.latency));
775        SendGestureEvent(MakeGestureEvent(
776            WebInputEvent::GesturePinchBegin, mouse_event.timeStampSeconds,
777            x, y, 0, event.latency));
778      }
779      if (dx != 0 || dy != 0) {
780        dx = pow(dy < 0 ? 0.998f : 1.002f, fabs(dy));
781        GestureEventWithLatencyInfo gesture_event = MakeGestureEvent(
782            WebInputEvent::GesturePinchUpdate, mouse_event.timeStampSeconds,
783            startX, startY, 0, event.latency);
784        gesture_event.event.data.pinchUpdate.scale = dx;
785        SendGestureEvent(gesture_event);
786      }
787      if (mouse_event.type == WebInputEvent::MouseUp) {
788        SendGestureEvent(MakeGestureEvent(
789            WebInputEvent::GesturePinchEnd, mouse_event.timeStampSeconds,
790            x, y, 0, event.latency));
791        SendGestureEvent(MakeGestureEvent(
792            WebInputEvent::GestureScrollEnd, mouse_event.timeStampSeconds,
793            x, y, 0, event.latency));
794      }
795      break;
796    case WebMouseEvent::ButtonNone:
797      break;
798  }
799}
800
801void InputRouterImpl::UpdateTouchAckTimeoutEnabled() {
802  if (!touch_ack_timeout_supported_) {
803    touch_event_queue_->SetAckTimeoutEnabled(false, base::TimeDelta());
804    return;
805  }
806
807  // Mobile sites tend to be well-behaved with respect to touch handling, so
808  // they have less need for the touch timeout fallback.
809  const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0;
810  const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0;
811
812  // TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves
813  // little purpose. It's also a strong signal that touch handling is critical
814  // to page functionality, so the timeout could do more harm than good.
815  const bool touch_action_none =
816      touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE;
817
818  const bool touch_ack_timeout_enabled = !fixed_page_scale &&
819                                         !mobile_viewport &&
820                                         !touch_action_none;
821  touch_event_queue_->SetAckTimeoutEnabled(touch_ack_timeout_enabled,
822                                           touch_ack_timeout_delay_);
823}
824
825bool InputRouterImpl::IsInOverscrollGesture() const {
826  OverscrollController* controller = client_->GetOverscrollController();
827  return controller && controller->overscroll_mode() != OVERSCROLL_NONE;
828}
829
830}  // namespace content
831