15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file. 45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/input/input_router_impl.h" 65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <math.h> 85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/auto_reset.h" 105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/command_line.h" 115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h" 125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/string_number_conversions.h" 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/input/gesture_event_queue.h" 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/input/input_ack_handler.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/input/input_router_client.h" 160de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)#include "content/browser/renderer_host/input/touch_event_queue.h" 175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h" 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/content_constants_internal.h" 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/edit_command.h" 20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "content/common/input/input_event_ack_state.h" 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/input/touch_action.h" 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/input/web_touch_event_traits.h" 235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/input_messages.h" 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/common/view_messages.h" 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/notification_service.h" 265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/notification_types.h" 275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "content/public/browser/user_metrics.h" 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "content/public/common/content_switches.h" 295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ipc/ipc_sender.h" 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/events/event.h" 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/events/keycodes/keyboard_codes.h" 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::Time; 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::TimeDelta; 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::TimeTicks; 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using blink::WebGestureEvent; 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using blink::WebInputEvent; 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using blink::WebKeyboardEvent; 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using blink::WebMouseEvent; 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using blink::WebMouseWheelEvent; 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace content { 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace { 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char* GetEventAckName(InputEventAckState ack_result) { 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) switch(ack_result) { 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case INPUT_EVENT_ACK_STATE_UNKNOWN: return "UNKNOWN"; 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case INPUT_EVENT_ACK_STATE_CONSUMED: return "CONSUMED"; 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED"; 50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS"; 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED"; 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName."; 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return ""; 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} // namespace 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)InputRouterImpl::Config::Config() { 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)InputRouterImpl::InputRouterImpl(IPC::Sender* sender, 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InputRouterClient* client, 645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) InputAckHandler* ack_handler, 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) int routing_id, 665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const Config& config) 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : sender_(sender), 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) client_(client), 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ack_handler_(ack_handler), 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) routing_id_(routing_id), 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) select_range_pending_(false), 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) move_caret_pending_(false), 735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mouse_move_pending_(false), 745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mouse_wheel_pending_(false), 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) current_view_flags_(0), 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) current_ack_source_(ACK_SOURCE_NONE), 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) flush_requested_(false), 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) touch_event_queue_(this, config.touch_config), 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gesture_event_queue_(this, this, config.gesture_config) { 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(sender); 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(client); 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(ack_handler); 835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) UpdateTouchAckTimeoutEnabled(); 845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)InputRouterImpl::~InputRouterImpl() {} 875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InputRouterImpl::Flush() { 89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) flush_requested_ = true; 905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu SignalFlushedIfNecessary(); 915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool InputRouterImpl::SendInput(scoped_ptr<IPC::Message> message) { 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) switch (message->type()) { 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Check for types that require an ACK. 975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case InputMsg_SelectRange::ID: 985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return SendSelectRange(message.Pass()); 995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case InputMsg_MoveCaret::ID: 1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return SendMoveCaret(message.Pass()); 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) case InputMsg_HandleInputEvent::ID: 1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) NOTREACHED() << "WebInputEvents should never be sent via SendInput."; 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return false; 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) default: 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Send(message.release()); 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InputRouterImpl::SendMouseEvent( 110010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) const MouseEventWithLatencyInfo& mouse_event) { 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (mouse_event.event.type == WebInputEvent::MouseDown && 1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gesture_event_queue_.GetTouchpadTapSuppressionController()-> 1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ShouldDeferMouseDown(mouse_event)) 1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (mouse_event.event.type == WebInputEvent::MouseUp && 1160de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles) gesture_event_queue_.GetTouchpadTapSuppressionController()-> 1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ShouldSuppressMouseUp()) 1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu SendMouseEventImmediately(mouse_event); 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InputRouterImpl::SendWheelEvent( 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const MouseWheelEventWithLatencyInfo& wheel_event) { 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SendWheelEvent(QueuedWheelEvent(wheel_event, false)); 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InputRouterImpl::SendWheelEvent(const QueuedWheelEvent& wheel_event) { 129 if (mouse_wheel_pending_) { 130 // If there's already a mouse wheel event waiting to be sent to the 131 // renderer, add the new deltas to that event. Not doing so (e.g., by 132 // dropping the old event, as for mouse moves) results in very slow 133 // scrolling on the Mac (on which many, very small wheel events are sent). 134 // Note that we can't coalesce wheel events for pinches because the GEQ 135 // expects one ACK for each (but it's fine to coalesce non-pinch wheels 136 // into a pinch one). Note that the GestureEventQueue ensures we only 137 // ever have a single pinch event queued here. 138 if (coalesced_mouse_wheel_events_.empty() || 139 wheel_event.synthesized_from_pinch || 140 !coalesced_mouse_wheel_events_.back().event.CanCoalesceWith( 141 wheel_event.event)) { 142 coalesced_mouse_wheel_events_.push_back(wheel_event); 143 } else { 144 coalesced_mouse_wheel_events_.back().event.CoalesceWith( 145 wheel_event.event); 146 } 147 return; 148 } 149 150 mouse_wheel_pending_ = true; 151 current_wheel_event_ = wheel_event; 152 153 LOCAL_HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", 154 coalesced_mouse_wheel_events_.size()); 155 156 FilterAndSendWebInputEvent( 157 wheel_event.event.event, wheel_event.event.latency, false); 158} 159 160void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, 161 const ui::LatencyInfo& latency_info, 162 bool is_keyboard_shortcut) { 163 // Put all WebKeyboardEvent objects in a queue since we can't trust the 164 // renderer and we need to give something to the HandleKeyboardEvent 165 // handler. 166 key_queue_.push_back(key_event); 167 LOCAL_HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); 168 169 gesture_event_queue_.FlingHasBeenHalted(); 170 171 // Only forward the non-native portions of our event. 172 FilterAndSendWebInputEvent(key_event, latency_info, is_keyboard_shortcut); 173} 174 175void InputRouterImpl::SendGestureEvent( 176 const GestureEventWithLatencyInfo& original_gesture_event) { 177 input_stream_validator_.Validate(original_gesture_event.event); 178 179 GestureEventWithLatencyInfo gesture_event(original_gesture_event); 180 181 if (touch_action_filter_.FilterGestureEvent(&gesture_event.event)) 182 return; 183 184 if (gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchscreen) 185 touch_event_queue_.OnGestureScrollEvent(gesture_event); 186 187 if (!gesture_event_queue_.ShouldForward(gesture_event)) 188 return; 189 190 SendGestureEventImmediately(gesture_event); 191} 192 193void InputRouterImpl::SendTouchEvent( 194 const TouchEventWithLatencyInfo& touch_event) { 195 input_stream_validator_.Validate(touch_event.event); 196 touch_event_queue_.QueueEvent(touch_event); 197} 198 199// Forwards MouseEvent without passing it through 200// TouchpadTapSuppressionController. 201void InputRouterImpl::SendMouseEventImmediately( 202 const MouseEventWithLatencyInfo& mouse_event) { 203 // Avoid spamming the renderer with mouse move events. It is important 204 // to note that WM_MOUSEMOVE events are anyways synthetic, but since our 205 // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way 206 // more WM_MOUSEMOVE events than we wish to send to the renderer. 207 if (mouse_event.event.type == WebInputEvent::MouseMove) { 208 if (mouse_move_pending_) { 209 if (!next_mouse_move_) 210 next_mouse_move_.reset(new MouseEventWithLatencyInfo(mouse_event)); 211 else 212 next_mouse_move_->CoalesceWith(mouse_event); 213 return; 214 } 215 mouse_move_pending_ = true; 216 } 217 218 FilterAndSendWebInputEvent(mouse_event.event, mouse_event.latency, false); 219} 220 221void InputRouterImpl::SendTouchEventImmediately( 222 const TouchEventWithLatencyInfo& touch_event) { 223 if (WebTouchEventTraits::IsTouchSequenceStart(touch_event.event)) { 224 touch_action_filter_.ResetTouchAction(); 225 // Note that if the previous touch-action was TOUCH_ACTION_NONE, enabling 226 // the timeout here will not take effect until the *following* touch 227 // sequence. This is a desirable side-effect, giving the renderer a chance 228 // to send a touch-action response without racing against the ack timeout. 229 UpdateTouchAckTimeoutEnabled(); 230 } 231 232 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); 233} 234 235void InputRouterImpl::SendGestureEventImmediately( 236 const GestureEventWithLatencyInfo& gesture_event) { 237 if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate && 238 gesture_event.event.sourceDevice == blink::WebGestureDeviceTouchpad) { 239 SendSyntheticWheelEventForPinch(gesture_event); 240 return; 241 } 242 243 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); 244} 245 246const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const { 247 if (key_queue_.empty()) 248 return NULL; 249 return &key_queue_.front(); 250} 251 252bool InputRouterImpl::ShouldForwardTouchEvent() const { 253 // Always send a touch event if the renderer has a touch-event handler or 254 // there are pending touch events. 255 return touch_event_queue_.has_handlers() || !touch_event_queue_.empty(); 256} 257 258void InputRouterImpl::OnViewUpdated(int view_flags) { 259 current_view_flags_ = view_flags; 260 261 // A fixed page scale or mobile viewport should disable the touch ack timeout. 262 UpdateTouchAckTimeoutEnabled(); 263} 264 265bool InputRouterImpl::OnMessageReceived(const IPC::Message& message) { 266 bool handled = true; 267 IPC_BEGIN_MESSAGE_MAP(InputRouterImpl, message) 268 IPC_MESSAGE_HANDLER(InputHostMsg_HandleInputEvent_ACK, OnInputEventAck) 269 IPC_MESSAGE_HANDLER(InputHostMsg_DidOverscroll, OnDidOverscroll) 270 IPC_MESSAGE_HANDLER(ViewHostMsg_MoveCaret_ACK, OnMsgMoveCaretAck) 271 IPC_MESSAGE_HANDLER(ViewHostMsg_SelectRange_ACK, OnSelectRangeAck) 272 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, 273 OnHasTouchEventHandlers) 274 IPC_MESSAGE_HANDLER(InputHostMsg_SetTouchAction, 275 OnSetTouchAction) 276 IPC_MESSAGE_UNHANDLED(handled = false) 277 IPC_END_MESSAGE_MAP() 278 279 return handled; 280} 281 282void InputRouterImpl::OnTouchEventAck(const TouchEventWithLatencyInfo& event, 283 InputEventAckState ack_result) { 284 // Touchstart events sent to the renderer indicate a new touch sequence, but 285 // in some cases we may filter out sending the touchstart - catch those here. 286 if (WebTouchEventTraits::IsTouchSequenceStart(event.event) && 287 ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) { 288 touch_action_filter_.ResetTouchAction(); 289 UpdateTouchAckTimeoutEnabled(); 290 } 291 ack_handler_->OnTouchEventAck(event, ack_result); 292} 293 294void InputRouterImpl::OnGestureEventAck( 295 const GestureEventWithLatencyInfo& event, 296 InputEventAckState ack_result) { 297 touch_event_queue_.OnGestureEventAck(event, ack_result); 298 ack_handler_->OnGestureEventAck(event, ack_result); 299} 300 301bool InputRouterImpl::SendSelectRange(scoped_ptr<IPC::Message> message) { 302 DCHECK(message->type() == InputMsg_SelectRange::ID); 303 if (select_range_pending_) { 304 next_selection_range_ = message.Pass(); 305 return true; 306 } 307 308 select_range_pending_ = true; 309 return Send(message.release()); 310} 311 312bool InputRouterImpl::SendMoveCaret(scoped_ptr<IPC::Message> message) { 313 DCHECK(message->type() == InputMsg_MoveCaret::ID); 314 if (move_caret_pending_) { 315 next_move_caret_ = message.Pass(); 316 return true; 317 } 318 319 move_caret_pending_ = true; 320 return Send(message.release()); 321} 322 323bool InputRouterImpl::Send(IPC::Message* message) { 324 return sender_->Send(message); 325} 326 327void InputRouterImpl::FilterAndSendWebInputEvent( 328 const WebInputEvent& input_event, 329 const ui::LatencyInfo& latency_info, 330 bool is_keyboard_shortcut) { 331 TRACE_EVENT1("input", 332 "InputRouterImpl::FilterAndSendWebInputEvent", 333 "type", 334 WebInputEventTraits::GetName(input_event.type)); 335 336 // Any input event cancels a pending mouse move event. 337 next_mouse_move_.reset(); 338 339 OfferToHandlers(input_event, latency_info, is_keyboard_shortcut); 340} 341 342void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, 343 const ui::LatencyInfo& latency_info, 344 bool is_keyboard_shortcut) { 345 output_stream_validator_.Validate(input_event); 346 347 if (OfferToClient(input_event, latency_info)) 348 return; 349 350 OfferToRenderer(input_event, latency_info, is_keyboard_shortcut); 351 352 // Touch events should always indicate in the event whether they are 353 // cancelable (respect ACK disposition) or not. 354 bool ignores_ack = WebInputEventTraits::IgnoresAckDisposition(input_event); 355 if (WebInputEvent::isTouchEventType(input_event.type)) { 356 DCHECK(!ignores_ack == 357 static_cast<const blink::WebTouchEvent&>(input_event).cancelable); 358 } 359 360 // If we don't care about the ack disposition, send the ack immediately. 361 if (ignores_ack) { 362 ProcessInputEventAck(input_event.type, 363 INPUT_EVENT_ACK_STATE_IGNORED, 364 latency_info, 365 IGNORING_DISPOSITION); 366 } 367} 368 369bool InputRouterImpl::OfferToClient(const WebInputEvent& input_event, 370 const ui::LatencyInfo& latency_info) { 371 bool consumed = false; 372 373 InputEventAckState filter_ack = 374 client_->FilterInputEvent(input_event, latency_info); 375 switch (filter_ack) { 376 case INPUT_EVENT_ACK_STATE_CONSUMED: 377 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: 378 // Send the ACK and early exit. 379 next_mouse_move_.reset(); 380 ProcessInputEventAck(input_event.type, filter_ack, latency_info, CLIENT); 381 // WARNING: |this| may be deleted at this point. 382 consumed = true; 383 break; 384 case INPUT_EVENT_ACK_STATE_UNKNOWN: 385 // Simply drop the event. 386 consumed = true; 387 break; 388 default: 389 break; 390 } 391 392 return consumed; 393} 394 395bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event, 396 const ui::LatencyInfo& latency_info, 397 bool is_keyboard_shortcut) { 398 if (Send(new InputMsg_HandleInputEvent( 399 routing_id(), &input_event, latency_info, is_keyboard_shortcut))) { 400 // Ack messages for ignored ack event types should never be sent by the 401 // renderer. Consequently, such event types should not affect event time 402 // or in-flight event count metrics. 403 if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) { 404 input_event_start_time_ = TimeTicks::Now(); 405 client_->IncrementInFlightEventCount(); 406 } 407 return true; 408 } 409 return false; 410} 411 412void InputRouterImpl::SendSyntheticWheelEventForPinch( 413 const GestureEventWithLatencyInfo& pinch_event) { 414 // We match typical trackpad behavior on Windows by sending fake wheel events 415 // with the ctrl modifier set when we see trackpad pinch gestures. Ideally 416 // we'd someday get a standard 'pinch' event and send that instead. 417 418 WebMouseWheelEvent wheelEvent; 419 wheelEvent.type = WebInputEvent::MouseWheel; 420 wheelEvent.timeStampSeconds = pinch_event.event.timeStampSeconds; 421 wheelEvent.windowX = wheelEvent.x = pinch_event.event.x; 422 wheelEvent.windowY = wheelEvent.y = pinch_event.event.y; 423 wheelEvent.globalX = pinch_event.event.globalX; 424 wheelEvent.globalY = pinch_event.event.globalY; 425 wheelEvent.modifiers = 426 pinch_event.event.modifiers | WebInputEvent::ControlKey; 427 wheelEvent.deltaX = 0; 428 // The function to convert scales to deltaY values is designed to be 429 // compatible with websites existing use of wheel events, and with existing 430 // Windows trackpad behavior. In particular, we want: 431 // - deltas should accumulate via addition: f(s1*s2)==f(s1)+f(s2) 432 // - deltas should invert via negation: f(1/s) == -f(s) 433 // - zoom in should be positive: f(s) > 0 iff s > 1 434 // - magnitude roughly matches wheels: f(2) > 25 && f(2) < 100 435 // - a formula that's relatively easy to use from JavaScript 436 // Note that 'wheel' event deltaY values have their sign inverted. So to 437 // convert a wheel deltaY back to a scale use Math.exp(-deltaY/100). 438 DCHECK_GT(pinch_event.event.data.pinchUpdate.scale, 0); 439 wheelEvent.deltaY = 100.0f * log(pinch_event.event.data.pinchUpdate.scale); 440 wheelEvent.hasPreciseScrollingDeltas = true; 441 wheelEvent.wheelTicksX = 0; 442 wheelEvent.wheelTicksY = 443 pinch_event.event.data.pinchUpdate.scale > 1 ? 1 : -1; 444 445 SendWheelEvent(QueuedWheelEvent( 446 MouseWheelEventWithLatencyInfo(wheelEvent, pinch_event.latency), true)); 447} 448 449void InputRouterImpl::OnInputEventAck( 450 const InputHostMsg_HandleInputEvent_ACK_Params& ack) { 451 client_->DecrementInFlightEventCount(); 452 453 // Log the time delta for processing an input event. 454 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; 455 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); 456 457 if (ack.overscroll) { 458 DCHECK(ack.type == WebInputEvent::MouseWheel || 459 ack.type == WebInputEvent::GestureScrollUpdate); 460 OnDidOverscroll(*ack.overscroll); 461 } 462 463 ProcessInputEventAck(ack.type, ack.state, ack.latency, RENDERER); 464 // WARNING: |this| may be deleted at this point. 465 466 // This is used only for testing, and the other end does not use the 467 // source object. On linux, specifying 468 // Source<RenderWidgetHost> results in a very strange 469 // runtime error in the epilogue of the enclosing 470 // (ProcessInputEventAck) method, but not on other platforms; using 471 // 'void' instead is just as safe (since NotificationSource 472 // is not actually typesafe) and avoids this error. 473 int type = static_cast<int>(ack.type); 474 NotificationService::current()->Notify( 475 NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK, 476 Source<void>(this), 477 Details<int>(&type)); 478} 479 480void InputRouterImpl::OnDidOverscroll(const DidOverscrollParams& params) { 481 client_->DidOverscroll(params); 482} 483 484void InputRouterImpl::OnMsgMoveCaretAck() { 485 move_caret_pending_ = false; 486 if (next_move_caret_) 487 SendMoveCaret(next_move_caret_.Pass()); 488} 489 490void InputRouterImpl::OnSelectRangeAck() { 491 select_range_pending_ = false; 492 if (next_selection_range_) 493 SendSelectRange(next_selection_range_.Pass()); 494} 495 496void InputRouterImpl::OnHasTouchEventHandlers(bool has_handlers) { 497 TRACE_EVENT1("input", "InputRouterImpl::OnHasTouchEventHandlers", 498 "has_handlers", has_handlers); 499 500 // Lack of a touch handler indicates that the page either has no touch-action 501 // modifiers or that all its touch-action modifiers are auto. Resetting the 502 // touch-action here allows forwarding of subsequent gestures even if the 503 // underlying touches never reach the router. 504 // TODO(jdduke): Reset touch-action only at the end of a touch sequence to 505 // prevent potentially strange mid-sequence behavior, crbug.com/375940. 506 if (!has_handlers) 507 touch_action_filter_.ResetTouchAction(); 508 509 touch_event_queue_.OnHasTouchEventHandlers(has_handlers); 510 client_->OnHasTouchEventHandlers(has_handlers); 511} 512 513void InputRouterImpl::OnSetTouchAction(TouchAction touch_action) { 514 // Synthetic touchstart events should get filtered out in RenderWidget. 515 DCHECK(touch_event_queue_.IsPendingAckTouchStart()); 516 TRACE_EVENT1("input", "InputRouterImpl::OnSetTouchAction", 517 "action", touch_action); 518 519 touch_action_filter_.OnSetTouchAction(touch_action); 520 521 // TOUCH_ACTION_NONE should disable the touch ack timeout. 522 UpdateTouchAckTimeoutEnabled(); 523} 524 525void InputRouterImpl::ProcessInputEventAck( 526 WebInputEvent::Type event_type, 527 InputEventAckState ack_result, 528 const ui::LatencyInfo& latency_info, 529 AckSource ack_source) { 530 TRACE_EVENT2("input", "InputRouterImpl::ProcessInputEventAck", 531 "type", WebInputEventTraits::GetName(event_type), 532 "ack", GetEventAckName(ack_result)); 533 534 // Note: The keyboard ack must be treated carefully, as it may result in 535 // synchronous destruction of |this|. Handling immediately guards against 536 // future references to |this|, as with |auto_reset_current_ack_source| below. 537 if (WebInputEvent::isKeyboardEventType(event_type)) { 538 ProcessKeyboardAck(event_type, ack_result); 539 // WARNING: |this| may be deleted at this point. 540 return; 541 } 542 543 base::AutoReset<AckSource> auto_reset_current_ack_source( 544 ¤t_ack_source_, ack_source); 545 546 if (WebInputEvent::isMouseEventType(event_type)) { 547 ProcessMouseAck(event_type, ack_result); 548 } else if (event_type == WebInputEvent::MouseWheel) { 549 ProcessWheelAck(ack_result, latency_info); 550 } else if (WebInputEvent::isTouchEventType(event_type)) { 551 ProcessTouchAck(ack_result, latency_info); 552 } else if (WebInputEvent::isGestureEventType(event_type)) { 553 ProcessGestureAck(event_type, ack_result, latency_info); 554 } else if (event_type != WebInputEvent::Undefined) { 555 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); 556 } 557 558 SignalFlushedIfNecessary(); 559} 560 561void InputRouterImpl::ProcessKeyboardAck(blink::WebInputEvent::Type type, 562 InputEventAckState ack_result) { 563 if (key_queue_.empty()) { 564 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); 565 } else if (key_queue_.front().type != type) { 566 // Something must be wrong. Clear the |key_queue_| and char event 567 // suppression so that we can resume from the error. 568 key_queue_.clear(); 569 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE); 570 } else { 571 NativeWebKeyboardEvent front_item = key_queue_.front(); 572 key_queue_.pop_front(); 573 574 ack_handler_->OnKeyboardEventAck(front_item, ack_result); 575 // WARNING: This InputRouterImpl can be deallocated at this point 576 // (i.e. in the case of Ctrl+W, where the call to 577 // HandleKeyboardEvent destroys this InputRouterImpl). 578 // TODO(jdduke): crbug.com/274029 - Make ack-triggered shutdown async. 579 } 580} 581 582void InputRouterImpl::ProcessMouseAck(blink::WebInputEvent::Type type, 583 InputEventAckState ack_result) { 584 if (type != WebInputEvent::MouseMove) 585 return; 586 587 DCHECK(mouse_move_pending_); 588 mouse_move_pending_ = false; 589 590 if (next_mouse_move_) { 591 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); 592 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move 593 = next_mouse_move_.Pass(); 594 SendMouseEvent(*next_mouse_move); 595 } 596} 597 598void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result, 599 const ui::LatencyInfo& latency) { 600 // TODO(miletus): Add renderer side latency to each uncoalesced mouse 601 // wheel event and add terminal component to each of them. 602 current_wheel_event_.event.latency.AddNewLatencyFrom(latency); 603 604 if (current_wheel_event_.synthesized_from_pinch) { 605 // Ack the GesturePinchUpdate event that generated this wheel event. 606 ProcessInputEventAck(WebInputEvent::GesturePinchUpdate, 607 ack_result, 608 current_wheel_event_.event.latency, 609 current_ack_source_); 610 } else { 611 // Process the unhandled wheel event here before calling SendWheelEvent() 612 // since it will mutate current_wheel_event_. 613 ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result); 614 } 615 616 // Mark the wheel event complete only after the ACKs have been handled above. 617 // For example, ACKing the GesturePinchUpdate could cause another 618 // GesturePinchUpdate to be sent, which should queue a wheel event rather than 619 // send it immediately. 620 mouse_wheel_pending_ = false; 621 622 // Send the next (coalesced or synthetic) mouse wheel event. 623 if (!coalesced_mouse_wheel_events_.empty()) { 624 QueuedWheelEvent next_wheel_event = coalesced_mouse_wheel_events_.front(); 625 coalesced_mouse_wheel_events_.pop_front(); 626 SendWheelEvent(next_wheel_event); 627 } 628} 629 630void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type, 631 InputEventAckState ack_result, 632 const ui::LatencyInfo& latency) { 633 if (!gesture_event_queue_.ExpectingGestureAck()) 634 return; 635 636 // |gesture_event_queue_| will forward to OnGestureEventAck when appropriate. 637 gesture_event_queue_.ProcessGestureAck(ack_result, type, latency); 638} 639 640void InputRouterImpl::ProcessTouchAck( 641 InputEventAckState ack_result, 642 const ui::LatencyInfo& latency) { 643 // |touch_event_queue_| will forward to OnTouchEventAck when appropriate. 644 touch_event_queue_.ProcessTouchAck(ack_result, latency); 645} 646 647void InputRouterImpl::UpdateTouchAckTimeoutEnabled() { 648 // Mobile sites tend to be well-behaved with respect to touch handling, so 649 // they have less need for the touch timeout fallback. 650 const bool fixed_page_scale = (current_view_flags_ & FIXED_PAGE_SCALE) != 0; 651 const bool mobile_viewport = (current_view_flags_ & MOBILE_VIEWPORT) != 0; 652 653 // TOUCH_ACTION_NONE will prevent scrolling, in which case the timeout serves 654 // little purpose. It's also a strong signal that touch handling is critical 655 // to page functionality, so the timeout could do more harm than good. 656 const bool touch_action_none = 657 touch_action_filter_.allowed_touch_action() == TOUCH_ACTION_NONE; 658 659 const bool touch_ack_timeout_enabled = !fixed_page_scale && 660 !mobile_viewport && 661 !touch_action_none; 662 touch_event_queue_.SetAckTimeoutEnabled(touch_ack_timeout_enabled); 663} 664 665void InputRouterImpl::SignalFlushedIfNecessary() { 666 if (!flush_requested_) 667 return; 668 669 if (HasPendingEvents()) 670 return; 671 672 flush_requested_ = false; 673 client_->DidFlush(); 674} 675 676bool InputRouterImpl::HasPendingEvents() const { 677 return !touch_event_queue_.empty() || 678 !gesture_event_queue_.empty() || 679 !key_queue_.empty() || 680 mouse_move_pending_ || 681 mouse_wheel_pending_ || 682 select_range_pending_ || 683 move_caret_pending_; 684} 685 686InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent() 687 : synthesized_from_pinch(false) { 688} 689 690InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent( 691 const MouseWheelEventWithLatencyInfo& event, 692 bool synthesized_from_pinch) 693 : event(event), synthesized_from_pinch(synthesized_from_pinch) { 694} 695 696InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() { 697} 698 699} // namespace content 700