OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/input_router_impl.h" | 5 #include "content/browser/renderer_host/input/input_router_impl.h" |
6 | 6 |
| 7 #include <math.h> |
| 8 |
7 #include "base/auto_reset.h" | 9 #include "base/auto_reset.h" |
8 #include "base/command_line.h" | 10 #include "base/command_line.h" |
9 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
10 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
11 #include "content/browser/renderer_host/input/gesture_event_queue.h" | 13 #include "content/browser/renderer_host/input/gesture_event_queue.h" |
12 #include "content/browser/renderer_host/input/input_ack_handler.h" | 14 #include "content/browser/renderer_host/input/input_ack_handler.h" |
13 #include "content/browser/renderer_host/input/input_router_client.h" | 15 #include "content/browser/renderer_host/input/input_router_client.h" |
14 #include "content/browser/renderer_host/input/touch_event_queue.h" | 16 #include "content/browser/renderer_host/input/touch_event_queue.h" |
15 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controlle
r.h" | 17 #include "content/browser/renderer_host/input/touchpad_tap_suppression_controlle
r.h" |
16 #include "content/browser/renderer_host/overscroll_controller.h" | 18 #include "content/browser/renderer_host/overscroll_controller.h" |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 if (mouse_event.event.type == WebInputEvent::MouseUp && | 175 if (mouse_event.event.type == WebInputEvent::MouseUp && |
174 gesture_event_queue_.GetTouchpadTapSuppressionController()-> | 176 gesture_event_queue_.GetTouchpadTapSuppressionController()-> |
175 ShouldSuppressMouseUp()) | 177 ShouldSuppressMouseUp()) |
176 return; | 178 return; |
177 | 179 |
178 SendMouseEventImmediately(mouse_event); | 180 SendMouseEventImmediately(mouse_event); |
179 } | 181 } |
180 | 182 |
181 void InputRouterImpl::SendWheelEvent( | 183 void InputRouterImpl::SendWheelEvent( |
182 const MouseWheelEventWithLatencyInfo& wheel_event) { | 184 const MouseWheelEventWithLatencyInfo& wheel_event) { |
183 // If there's already a mouse wheel event waiting to be sent to the renderer, | 185 SendWheelEvent(QueuedWheelEvent(wheel_event, false)); |
184 // add the new deltas to that event. Not doing so (e.g., by dropping the old | 186 } |
185 // event, as for mouse moves) results in very slow scrolling on the Mac (on | 187 |
186 // which many, very small wheel events are sent). | 188 void InputRouterImpl::SendWheelEvent(const QueuedWheelEvent& wheel_event) { |
187 if (mouse_wheel_pending_) { | 189 if (mouse_wheel_pending_) { |
| 190 // If there's already a mouse wheel event waiting to be sent to the |
| 191 // renderer, add the new deltas to that event. Not doing so (e.g., by |
| 192 // dropping the old event, as for mouse moves) results in very slow |
| 193 // scrolling on the Mac (on which many, very small wheel events are sent). |
| 194 // Note that we can't coalesce wheel events for pinches because the GEQ |
| 195 // expects one ACK for each (but it's fine to coalesce non-pinch wheels |
| 196 // into a pinch one). Note that the GestureEventQueue ensures we only |
| 197 // ever have a single pinch event queued here. |
188 if (coalesced_mouse_wheel_events_.empty() || | 198 if (coalesced_mouse_wheel_events_.empty() || |
189 !coalesced_mouse_wheel_events_.back().CanCoalesceWith(wheel_event)) { | 199 wheel_event.synthesized_from_pinch || |
| 200 !coalesced_mouse_wheel_events_.back().event.CanCoalesceWith( |
| 201 wheel_event.event)) { |
190 coalesced_mouse_wheel_events_.push_back(wheel_event); | 202 coalesced_mouse_wheel_events_.push_back(wheel_event); |
191 } else { | 203 } else { |
192 coalesced_mouse_wheel_events_.back().CoalesceWith(wheel_event); | 204 coalesced_mouse_wheel_events_.back().event.CoalesceWith( |
| 205 wheel_event.event); |
193 } | 206 } |
194 return; | 207 return; |
195 } | 208 } |
| 209 |
196 mouse_wheel_pending_ = true; | 210 mouse_wheel_pending_ = true; |
197 current_wheel_event_ = wheel_event; | 211 current_wheel_event_ = wheel_event; |
198 | 212 |
199 HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", | 213 HISTOGRAM_COUNTS_100("Renderer.WheelQueueSize", |
200 coalesced_mouse_wheel_events_.size()); | 214 coalesced_mouse_wheel_events_.size()); |
201 | 215 |
202 FilterAndSendWebInputEvent(wheel_event.event, wheel_event.latency, false); | 216 FilterAndSendWebInputEvent( |
| 217 wheel_event.event.event, wheel_event.event.latency, false); |
203 } | 218 } |
204 | 219 |
205 void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, | 220 void InputRouterImpl::SendKeyboardEvent(const NativeWebKeyboardEvent& key_event, |
206 const ui::LatencyInfo& latency_info, | 221 const ui::LatencyInfo& latency_info, |
207 bool is_keyboard_shortcut) { | 222 bool is_keyboard_shortcut) { |
208 // Put all WebKeyboardEvent objects in a queue since we can't trust the | 223 // Put all WebKeyboardEvent objects in a queue since we can't trust the |
209 // renderer and we need to give something to the HandleKeyboardEvent | 224 // renderer and we need to give something to the HandleKeyboardEvent |
210 // handler. | 225 // handler. |
211 key_queue_.push_back(key_event); | 226 key_queue_.push_back(key_event); |
212 HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); | 227 HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size()); |
(...skipping 15 matching lines...) Expand all Loading... |
228 touch_event_queue_.OnGestureScrollEvent(gesture_event); | 243 touch_event_queue_.OnGestureScrollEvent(gesture_event); |
229 | 244 |
230 if (!IsInOverscrollGesture() && | 245 if (!IsInOverscrollGesture() && |
231 !gesture_event_queue_.ShouldForward(gesture_event)) { | 246 !gesture_event_queue_.ShouldForward(gesture_event)) { |
232 OverscrollController* controller = client_->GetOverscrollController(); | 247 OverscrollController* controller = client_->GetOverscrollController(); |
233 if (controller) | 248 if (controller) |
234 controller->DiscardingGestureEvent(gesture_event.event); | 249 controller->DiscardingGestureEvent(gesture_event.event); |
235 return; | 250 return; |
236 } | 251 } |
237 | 252 |
238 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); | 253 SendGestureEventImmediately(gesture_event); |
239 } | 254 } |
240 | 255 |
241 void InputRouterImpl::SendTouchEvent( | 256 void InputRouterImpl::SendTouchEvent( |
242 const TouchEventWithLatencyInfo& touch_event) { | 257 const TouchEventWithLatencyInfo& touch_event) { |
243 touch_event_queue_.QueueEvent(touch_event); | 258 touch_event_queue_.QueueEvent(touch_event); |
244 } | 259 } |
245 | 260 |
246 // Forwards MouseEvent without passing it through | 261 // Forwards MouseEvent without passing it through |
247 // TouchpadTapSuppressionController. | 262 // TouchpadTapSuppressionController. |
248 void InputRouterImpl::SendMouseEventImmediately( | 263 void InputRouterImpl::SendMouseEventImmediately( |
(...skipping 25 matching lines...) Expand all Loading... |
274 // sequence. This is a desirable side-effect, giving the renderer a chance | 289 // sequence. This is a desirable side-effect, giving the renderer a chance |
275 // to send a touch-action response without racing against the ack timeout. | 290 // to send a touch-action response without racing against the ack timeout. |
276 UpdateTouchAckTimeoutEnabled(); | 291 UpdateTouchAckTimeoutEnabled(); |
277 } | 292 } |
278 | 293 |
279 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); | 294 FilterAndSendWebInputEvent(touch_event.event, touch_event.latency, false); |
280 } | 295 } |
281 | 296 |
282 void InputRouterImpl::SendGestureEventImmediately( | 297 void InputRouterImpl::SendGestureEventImmediately( |
283 const GestureEventWithLatencyInfo& gesture_event) { | 298 const GestureEventWithLatencyInfo& gesture_event) { |
| 299 if (gesture_event.event.type == WebInputEvent::GesturePinchUpdate && |
| 300 gesture_event.event.sourceDevice == WebGestureEvent::Touchpad) { |
| 301 SendSyntheticWheelEventForPinch(gesture_event); |
| 302 return; |
| 303 } |
| 304 |
284 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); | 305 FilterAndSendWebInputEvent(gesture_event.event, gesture_event.latency, false); |
285 } | 306 } |
286 | 307 |
287 const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const { | 308 const NativeWebKeyboardEvent* InputRouterImpl::GetLastKeyboardEvent() const { |
288 if (key_queue_.empty()) | 309 if (key_queue_.empty()) |
289 return NULL; | 310 return NULL; |
290 return &key_queue_.front(); | 311 return &key_queue_.front(); |
291 } | 312 } |
292 | 313 |
293 bool InputRouterImpl::ShouldForwardTouchEvent() const { | 314 bool InputRouterImpl::ShouldForwardTouchEvent() const { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 | 392 |
372 void InputRouterImpl::FilterAndSendWebInputEvent( | 393 void InputRouterImpl::FilterAndSendWebInputEvent( |
373 const WebInputEvent& input_event, | 394 const WebInputEvent& input_event, |
374 const ui::LatencyInfo& latency_info, | 395 const ui::LatencyInfo& latency_info, |
375 bool is_keyboard_shortcut) { | 396 bool is_keyboard_shortcut) { |
376 TRACE_EVENT1("input", | 397 TRACE_EVENT1("input", |
377 "InputRouterImpl::FilterAndSendWebInputEvent", | 398 "InputRouterImpl::FilterAndSendWebInputEvent", |
378 "type", | 399 "type", |
379 WebInputEventTraits::GetName(input_event.type)); | 400 WebInputEventTraits::GetName(input_event.type)); |
380 | 401 |
381 // Transmit any pending wheel events on a non-wheel event. This ensures that | |
382 // final PhaseEnded wheel event is received, which is necessary to terminate | |
383 // rubber-banding, for example. | |
384 if (input_event.type != WebInputEvent::MouseWheel) { | |
385 WheelEventQueue mouse_wheel_events; | |
386 mouse_wheel_events.swap(coalesced_mouse_wheel_events_); | |
387 for (size_t i = 0; i < mouse_wheel_events.size(); ++i) { | |
388 OfferToHandlers(mouse_wheel_events[i].event, | |
389 mouse_wheel_events[i].latency, | |
390 false); | |
391 } | |
392 } | |
393 | |
394 // Any input event cancels a pending mouse move event. | 402 // Any input event cancels a pending mouse move event. |
395 next_mouse_move_.reset(); | 403 next_mouse_move_.reset(); |
396 | 404 |
397 OfferToHandlers(input_event, latency_info, is_keyboard_shortcut); | 405 OfferToHandlers(input_event, latency_info, is_keyboard_shortcut); |
398 } | 406 } |
399 | 407 |
400 void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, | 408 void InputRouterImpl::OfferToHandlers(const WebInputEvent& input_event, |
401 const ui::LatencyInfo& latency_info, | 409 const ui::LatencyInfo& latency_info, |
402 bool is_keyboard_shortcut) { | 410 bool is_keyboard_shortcut) { |
403 // Trackpad pinch gestures are not yet handled by the renderer. | |
404 // TODO(rbyers): Send mousewheel for trackpad pinch - crbug.com/289887. | |
405 if (input_event.type == WebInputEvent::GesturePinchUpdate && | |
406 static_cast<const WebGestureEvent&>(input_event).sourceDevice == | |
407 WebGestureEvent::Touchpad) { | |
408 ProcessInputEventAck(input_event.type, | |
409 INPUT_EVENT_ACK_STATE_NOT_CONSUMED, | |
410 latency_info, | |
411 ACK_SOURCE_NONE); | |
412 return; | |
413 } | |
414 | |
415 if (OfferToOverscrollController(input_event, latency_info)) | 411 if (OfferToOverscrollController(input_event, latency_info)) |
416 return; | 412 return; |
417 | 413 |
418 if (OfferToClient(input_event, latency_info)) | 414 if (OfferToClient(input_event, latency_info)) |
419 return; | 415 return; |
420 | 416 |
421 OfferToRenderer(input_event, latency_info, is_keyboard_shortcut); | 417 OfferToRenderer(input_event, latency_info, is_keyboard_shortcut); |
422 | 418 |
423 // Touch events should always indicate in the event whether they are | 419 // Touch events should always indicate in the event whether they are |
424 // cancelable (respect ACK disposition) or not. | 420 // cancelable (respect ACK disposition) or not. |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 // or in-flight event count metrics. | 504 // or in-flight event count metrics. |
509 if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) { | 505 if (!WebInputEventTraits::IgnoresAckDisposition(input_event)) { |
510 input_event_start_time_ = TimeTicks::Now(); | 506 input_event_start_time_ = TimeTicks::Now(); |
511 client_->IncrementInFlightEventCount(); | 507 client_->IncrementInFlightEventCount(); |
512 } | 508 } |
513 return true; | 509 return true; |
514 } | 510 } |
515 return false; | 511 return false; |
516 } | 512 } |
517 | 513 |
| 514 void InputRouterImpl::SendSyntheticWheelEventForPinch( |
| 515 const GestureEventWithLatencyInfo& pinch_event) { |
| 516 // We match typical trackpad behavior on Windows by sending fake wheel events |
| 517 // with the ctrl modifier set when we see trackpad pinch gestures. Ideally |
| 518 // we'd someday get a standard 'pinch' event and send that instead. |
| 519 |
| 520 WebMouseWheelEvent wheelEvent; |
| 521 wheelEvent.type = WebInputEvent::MouseWheel; |
| 522 wheelEvent.timeStampSeconds = pinch_event.event.timeStampSeconds; |
| 523 wheelEvent.windowX = wheelEvent.x = pinch_event.event.x; |
| 524 wheelEvent.windowY = wheelEvent.y = pinch_event.event.y; |
| 525 wheelEvent.globalX = pinch_event.event.globalX; |
| 526 wheelEvent.globalY = pinch_event.event.globalY; |
| 527 wheelEvent.modifiers = |
| 528 pinch_event.event.modifiers | WebInputEvent::ControlKey; |
| 529 wheelEvent.deltaX = 0; |
| 530 // The function to convert scales to deltaY values is designed to be |
| 531 // compatible with websites existing use of wheel events, and with existing |
| 532 // Windows trackpad behavior. In particular, we want: |
| 533 // - deltas should accumulate via addition: f(s1*s2)==f(s1)+f(s2) |
| 534 // - deltas should invert via negation: f(1/s) == -f(s) |
| 535 // - zoom in should be positive: f(s) > 0 iff s > 1 |
| 536 // - magnitude roughly matches wheels: f(2) > 25 && f(2) < 100 |
| 537 // - a formula that's relatively easy to use from JavaScript |
| 538 // Note that 'wheel' event deltaY values have their sign inverted. So to |
| 539 // convert a wheel deltaY back to a scale use Math.exp(-deltaY/100). |
| 540 DCHECK_GT(pinch_event.event.data.pinchUpdate.scale, 0); |
| 541 wheelEvent.deltaY = 100.0f * log(pinch_event.event.data.pinchUpdate.scale); |
| 542 wheelEvent.hasPreciseScrollingDeltas = true; |
| 543 wheelEvent.wheelTicksX = 0; |
| 544 wheelEvent.wheelTicksY = |
| 545 pinch_event.event.data.pinchUpdate.scale > 1 ? 1 : -1; |
| 546 |
| 547 SendWheelEvent(QueuedWheelEvent( |
| 548 MouseWheelEventWithLatencyInfo(wheelEvent, pinch_event.latency), true)); |
| 549 } |
| 550 |
518 void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type, | 551 void InputRouterImpl::OnInputEventAck(WebInputEvent::Type event_type, |
519 InputEventAckState ack_result, | 552 InputEventAckState ack_result, |
520 const ui::LatencyInfo& latency_info) { | 553 const ui::LatencyInfo& latency_info) { |
521 client_->DecrementInFlightEventCount(); | 554 client_->DecrementInFlightEventCount(); |
522 | 555 |
523 // Log the time delta for processing an input event. | 556 // Log the time delta for processing an input event. |
524 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; | 557 TimeDelta delta = TimeTicks::Now() - input_event_start_time_; |
525 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); | 558 UMA_HISTOGRAM_TIMES("MPArch.IIR_InputEventDelta", delta); |
526 | 559 |
527 ProcessInputEventAck(event_type, ack_result, latency_info, RENDERER); | 560 ProcessInputEventAck(event_type, ack_result, latency_info, RENDERER); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
640 if (next_mouse_move_) { | 673 if (next_mouse_move_) { |
641 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); | 674 DCHECK(next_mouse_move_->event.type == WebInputEvent::MouseMove); |
642 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move | 675 scoped_ptr<MouseEventWithLatencyInfo> next_mouse_move |
643 = next_mouse_move_.Pass(); | 676 = next_mouse_move_.Pass(); |
644 SendMouseEvent(*next_mouse_move); | 677 SendMouseEvent(*next_mouse_move); |
645 } | 678 } |
646 } | 679 } |
647 | 680 |
648 void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result, | 681 void InputRouterImpl::ProcessWheelAck(InputEventAckState ack_result, |
649 const ui::LatencyInfo& latency) { | 682 const ui::LatencyInfo& latency) { |
650 ProcessAckForOverscroll(current_wheel_event_.event, ack_result); | |
651 | |
652 // TODO(miletus): Add renderer side latency to each uncoalesced mouse | 683 // TODO(miletus): Add renderer side latency to each uncoalesced mouse |
653 // wheel event and add terminal component to each of them. | 684 // wheel event and add terminal component to each of them. |
654 current_wheel_event_.latency.AddNewLatencyFrom(latency); | 685 current_wheel_event_.event.latency.AddNewLatencyFrom(latency); |
655 // Process the unhandled wheel event here before calling SendWheelEvent() | 686 |
656 // since it will mutate current_wheel_event_. | 687 if (current_wheel_event_.synthesized_from_pinch) { |
657 ack_handler_->OnWheelEventAck(current_wheel_event_, ack_result); | 688 // Ack the GesturePinchUpdate event that generated this wheel event. |
| 689 ProcessInputEventAck(WebInputEvent::GesturePinchUpdate, |
| 690 ack_result, |
| 691 current_wheel_event_.event.latency, |
| 692 current_ack_source_); |
| 693 } else { |
| 694 // Process the unhandled wheel event here before calling SendWheelEvent() |
| 695 // since it will mutate current_wheel_event_. |
| 696 ProcessAckForOverscroll(current_wheel_event_.event.event, ack_result); |
| 697 ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result); |
| 698 } |
| 699 |
| 700 // Mark the wheel event complete only after the ACKs have been handled above. |
| 701 // For example, ACKing the GesturePinchUpdate could cause another |
| 702 // GesturePinchUpdate to be sent, which should queue a wheel event rather than |
| 703 // send it immediately. |
658 mouse_wheel_pending_ = false; | 704 mouse_wheel_pending_ = false; |
659 | 705 |
660 // Now send the next (coalesced) mouse wheel event. | 706 // Send the next (coalesced or synthetic) mouse wheel event. |
661 if (!coalesced_mouse_wheel_events_.empty()) { | 707 if (!coalesced_mouse_wheel_events_.empty()) { |
662 MouseWheelEventWithLatencyInfo next_wheel_event = | 708 QueuedWheelEvent next_wheel_event = coalesced_mouse_wheel_events_.front(); |
663 coalesced_mouse_wheel_events_.front(); | |
664 coalesced_mouse_wheel_events_.pop_front(); | 709 coalesced_mouse_wheel_events_.pop_front(); |
665 SendWheelEvent(next_wheel_event); | 710 SendWheelEvent(next_wheel_event); |
666 } | 711 } |
667 } | 712 } |
668 | 713 |
669 void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type, | 714 void InputRouterImpl::ProcessGestureAck(WebInputEvent::Type type, |
670 InputEventAckState ack_result, | 715 InputEventAckState ack_result, |
671 const ui::LatencyInfo& latency) { | 716 const ui::LatencyInfo& latency) { |
672 // If |ack_result| originated from the overscroll controller, only | 717 // If |ack_result| originated from the overscroll controller, only |
673 // feed |gesture_event_queue_| the ack if it was expecting one. | 718 // feed |gesture_event_queue_| the ack if it was expecting one. |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
745 mouse_wheel_pending_ || | 790 mouse_wheel_pending_ || |
746 select_range_pending_ || | 791 select_range_pending_ || |
747 move_caret_pending_; | 792 move_caret_pending_; |
748 } | 793 } |
749 | 794 |
750 bool InputRouterImpl::IsInOverscrollGesture() const { | 795 bool InputRouterImpl::IsInOverscrollGesture() const { |
751 OverscrollController* controller = client_->GetOverscrollController(); | 796 OverscrollController* controller = client_->GetOverscrollController(); |
752 return controller && controller->overscroll_mode() != OVERSCROLL_NONE; | 797 return controller && controller->overscroll_mode() != OVERSCROLL_NONE; |
753 } | 798 } |
754 | 799 |
| 800 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent() |
| 801 : synthesized_from_pinch(false) { |
| 802 } |
| 803 |
| 804 InputRouterImpl::QueuedWheelEvent::QueuedWheelEvent( |
| 805 const MouseWheelEventWithLatencyInfo& event, |
| 806 bool synthesized_from_pinch) |
| 807 : event(event), synthesized_from_pinch(synthesized_from_pinch) { |
| 808 } |
| 809 |
| 810 InputRouterImpl::QueuedWheelEvent::~QueuedWheelEvent() { |
| 811 } |
| 812 |
755 } // namespace content | 813 } // namespace content |
OLD | NEW |