Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(548)

Side by Side Diff: content/browser/renderer_host/input/input_router_impl.cc

Issue 250923004: Synthesize ctrl-wheel events on touchpad pinch (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ensure scales never coalesce to 0 or Infinity Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698