1 // Copyright 2014 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 "ui/events/gesture_detection/gesture_provider.h"
6
7 #include <cmath>
8
9 #include "base/auto_reset.h"
10 #include "base/debug/trace_event.h"
11 #include "ui/events/event_constants.h"
12 #include "ui/events/gesture_detection/gesture_event_data.h"
13 #include "ui/events/gesture_detection/motion_event.h"
14 #include "ui/gfx/geometry/point_f.h"
15
16 namespace ui {
17 namespace {
18
19 // Double-tap drag zoom sensitivity (speed).
20 const float kDoubleTapDragZoomSpeed = 0.005f;
21
GetMotionEventActionName(MotionEvent::Action action)22 const char* GetMotionEventActionName(MotionEvent::Action action) {
23 switch (action) {
24 case MotionEvent::ACTION_POINTER_DOWN:
25 return "ACTION_POINTER_DOWN";
26 case MotionEvent::ACTION_POINTER_UP:
27 return "ACTION_POINTER_UP";
28 case MotionEvent::ACTION_DOWN:
29 return "ACTION_DOWN";
30 case MotionEvent::ACTION_UP:
31 return "ACTION_UP";
32 case MotionEvent::ACTION_CANCEL:
33 return "ACTION_CANCEL";
34 case MotionEvent::ACTION_MOVE:
35 return "ACTION_MOVE";
36 }
37 return "";
38 }
39
ClampBoundingBox(const gfx::RectF & bounds,float min_length,float max_length)40 gfx::RectF ClampBoundingBox(const gfx::RectF& bounds,
41 float min_length,
42 float max_length) {
43 float width = bounds.width();
44 float height = bounds.height();
45 if (min_length) {
46 width = std::max(min_length, width);
47 height = std::max(min_length, height);
48 }
49 if (max_length) {
50 width = std::min(max_length, width);
51 height = std::min(max_length, height);
52 }
53 const gfx::PointF center = bounds.CenterPoint();
54 return gfx::RectF(
55 center.x() - width / 2.f, center.y() - height / 2.f, width, height);
56 }
57
58 } // namespace
59
60 // GestureProvider:::Config
61
Config()62 GestureProvider::Config::Config()
63 : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
64 disable_click_delay(false),
65 gesture_begin_end_types_enabled(false),
66 min_gesture_bounds_length(0),
67 max_gesture_bounds_length(0) {
68 }
69
~Config()70 GestureProvider::Config::~Config() {
71 }
72
73 // GestureProvider::GestureListener
74
75 class GestureProvider::GestureListenerImpl
76 : public ScaleGestureDetector::ScaleGestureListener,
77 public GestureDetector::GestureListener,
78 public GestureDetector::DoubleTapListener {
79 public:
GestureListenerImpl(const GestureProvider::Config & config,GestureProviderClient * client)80 GestureListenerImpl(const GestureProvider::Config& config,
81 GestureProviderClient* client)
82 : config_(config),
83 client_(client),
84 gesture_detector_(config.gesture_detector_config, this, this),
85 scale_gesture_detector_(config.scale_gesture_detector_config, this),
86 snap_scroll_controller_(config.display),
87 ignore_multitouch_zoom_events_(false),
88 ignore_single_tap_(false),
89 pinch_event_sent_(false),
90 scroll_event_sent_(false),
91 max_diameter_before_show_press_(0),
92 show_press_event_sent_(false) {}
93
OnTouchEvent(const MotionEvent & event)94 void OnTouchEvent(const MotionEvent& event) {
95 const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
96 snap_scroll_controller_.SetSnapScrollingMode(event, in_scale_gesture);
97 if (in_scale_gesture)
98 SetIgnoreSingleTap(true);
99
100 const MotionEvent::Action action = event.GetAction();
101 if (action == MotionEvent::ACTION_DOWN) {
102 current_down_time_ = event.GetEventTime();
103 current_longpress_time_ = base::TimeTicks();
104 ignore_single_tap_ = false;
105 scroll_event_sent_ = false;
106 pinch_event_sent_ = false;
107 show_press_event_sent_ = false;
108 gesture_detector_.set_longpress_enabled(true);
109 tap_down_point_ = gfx::PointF(event.GetX(), event.GetY());
110 max_diameter_before_show_press_ = event.GetTouchMajor();
111 }
112
113 gesture_detector_.OnTouchEvent(event);
114 scale_gesture_detector_.OnTouchEvent(event);
115
116 if (action == MotionEvent::ACTION_UP ||
117 action == MotionEvent::ACTION_CANCEL) {
118 // Note: This call will have no effect if a fling was just generated, as
119 // |Fling()| will have already signalled an end to touch-scrolling.
120 if (scroll_event_sent_)
121 Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
122 current_down_time_ = base::TimeTicks();
123 } else if (action == MotionEvent::ACTION_MOVE) {
124 if (!show_press_event_sent_ && !scroll_event_sent_) {
125 max_diameter_before_show_press_ =
126 std::max(max_diameter_before_show_press_, event.GetTouchMajor());
127 }
128 }
129 }
130
Send(GestureEventData gesture)131 void Send(GestureEventData gesture) {
132 DCHECK(!gesture.time.is_null());
133 // The only valid events that should be sent without an active touch
134 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
135 // delay timing out.
136 DCHECK(!current_down_time_.is_null() || gesture.type() == ET_GESTURE_TAP ||
137 gesture.type() == ET_GESTURE_SHOW_PRESS ||
138 gesture.type() == ET_GESTURE_BEGIN ||
139 gesture.type() == ET_GESTURE_END);
140
141 if (gesture.primary_tool_type == MotionEvent::TOOL_TYPE_UNKNOWN ||
142 gesture.primary_tool_type == MotionEvent::TOOL_TYPE_FINGER) {
143 gesture.details.set_bounding_box(
144 ClampBoundingBox(gesture.details.bounding_box_f(),
145 config_.min_gesture_bounds_length,
146 config_.max_gesture_bounds_length));
147 }
148
149 switch (gesture.type()) {
150 case ET_GESTURE_LONG_PRESS:
151 DCHECK(!IsScaleGestureDetectionInProgress());
152 current_longpress_time_ = gesture.time;
153 break;
154 case ET_GESTURE_LONG_TAP:
155 current_longpress_time_ = base::TimeTicks();
156 break;
157 case ET_GESTURE_SCROLL_BEGIN:
158 DCHECK(!scroll_event_sent_);
159 scroll_event_sent_ = true;
160 break;
161 case ET_GESTURE_SCROLL_END:
162 DCHECK(scroll_event_sent_);
163 if (pinch_event_sent_)
164 Send(GestureEventData(ET_GESTURE_PINCH_END, gesture));
165 scroll_event_sent_ = false;
166 break;
167 case ET_SCROLL_FLING_START:
168 DCHECK(scroll_event_sent_);
169 scroll_event_sent_ = false;
170 break;
171 case ET_GESTURE_PINCH_BEGIN:
172 DCHECK(!pinch_event_sent_);
173 if (!scroll_event_sent_)
174 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture));
175 pinch_event_sent_ = true;
176 break;
177 case ET_GESTURE_PINCH_END:
178 DCHECK(pinch_event_sent_);
179 pinch_event_sent_ = false;
180 break;
181 case ET_GESTURE_SHOW_PRESS:
182 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
183 // will start before the press gesture fires (from GestureDetector), in
184 // which case the press should simply be dropped.
185 if (pinch_event_sent_ || scroll_event_sent_)
186 return;
187 default:
188 break;
189 };
190
191 client_->OnGestureEvent(gesture);
192 GestureTouchUMAHistogram::RecordGestureEvent(gesture);
193 }
194
195 // ScaleGestureDetector::ScaleGestureListener implementation.
OnScaleBegin(const ScaleGestureDetector & detector,const MotionEvent & e)196 virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
197 const MotionEvent& e) OVERRIDE {
198 if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode())
199 return false;
200 return true;
201 }
202
OnScaleEnd(const ScaleGestureDetector & detector,const MotionEvent & e)203 virtual void OnScaleEnd(const ScaleGestureDetector& detector,
204 const MotionEvent& e) OVERRIDE {
205 if (!pinch_event_sent_)
206 return;
207 Send(CreateGesture(ET_GESTURE_PINCH_END, e));
208 }
209
OnScale(const ScaleGestureDetector & detector,const MotionEvent & e)210 virtual bool OnScale(const ScaleGestureDetector& detector,
211 const MotionEvent& e) OVERRIDE {
212 if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode())
213 return false;
214 if (!pinch_event_sent_) {
215 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
216 e.GetId(),
217 e.GetToolType(),
218 detector.GetEventTime(),
219 detector.GetFocusX(),
220 detector.GetFocusY(),
221 detector.GetFocusX() + e.GetRawOffsetX(),
222 detector.GetFocusY() + e.GetRawOffsetY(),
223 e.GetPointerCount(),
224 GetBoundingBox(e, ET_GESTURE_PINCH_BEGIN),
225 e.GetFlags()));
226 }
227
228 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
229 config_.scale_gesture_detector_config.min_pinch_update_span_delta) {
230 return false;
231 }
232
233 float scale = detector.GetScaleFactor();
234 if (scale == 1)
235 return true;
236
237 if (detector.InDoubleTapMode()) {
238 // Relative changes in the double-tap scale factor computed by |detector|
239 // diminish as the touch moves away from the original double-tap focus.
240 // For historical reasons, Chrome has instead adopted a scale factor
241 // computation that is invariant to the focal distance, where
242 // the scale delta remains constant if the touch velocity is constant.
243 float dy =
244 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
245 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
246 : 1.0f - kDoubleTapDragZoomSpeed,
247 std::abs(dy));
248 }
249 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE);
250 pinch_details.set_scale(scale);
251 Send(CreateGesture(pinch_details,
252 e.GetId(),
253 e.GetToolType(),
254 detector.GetEventTime(),
255 detector.GetFocusX(),
256 detector.GetFocusY(),
257 detector.GetFocusX() + e.GetRawOffsetX(),
258 detector.GetFocusY() + e.GetRawOffsetY(),
259 e.GetPointerCount(),
260 GetBoundingBox(e, pinch_details.type()),
261 e.GetFlags()));
262 return true;
263 }
264
265 // GestureDetector::GestureListener implementation.
OnDown(const MotionEvent & e)266 virtual bool OnDown(const MotionEvent& e) OVERRIDE {
267 GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN);
268 Send(CreateGesture(tap_details, e));
269
270 // Return true to indicate that we want to handle touch.
271 return true;
272 }
273
OnScroll(const MotionEvent & e1,const MotionEvent & e2,float raw_distance_x,float raw_distance_y)274 virtual bool OnScroll(const MotionEvent& e1,
275 const MotionEvent& e2,
276 float raw_distance_x,
277 float raw_distance_y) OVERRIDE {
278 float distance_x = raw_distance_x;
279 float distance_y = raw_distance_y;
280 if (!scroll_event_sent_) {
281 // Remove the touch slop region from the first scroll event to avoid a
282 // jump.
283 double distance =
284 std::sqrt(distance_x * distance_x + distance_y * distance_y);
285 double epsilon = 1e-3;
286 if (distance > epsilon) {
287 double ratio =
288 std::max(0.,
289 distance - config_.gesture_detector_config.touch_slop) /
290 distance;
291 distance_x *= ratio;
292 distance_y *= ratio;
293 }
294
295 // Note that scroll start hints are in distance traveled, where
296 // scroll deltas are in the opposite direction.
297 GestureEventDetails scroll_details(
298 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
299
300 // Use the co-ordinates from the touch down, as these co-ordinates are
301 // used to determine which layer the scroll should affect.
302 Send(CreateGesture(scroll_details,
303 e2.GetId(),
304 e2.GetToolType(),
305 e2.GetEventTime(),
306 e1.GetX(),
307 e1.GetY(),
308 e1.GetRawX(),
309 e1.GetRawY(),
310 e2.GetPointerCount(),
311 GetBoundingBox(e2, scroll_details.type()),
312 e2.GetFlags()));
313 DCHECK(scroll_event_sent_);
314 }
315
316 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
317 if (snap_scroll_controller_.IsSnappingScrolls()) {
318 if (snap_scroll_controller_.IsSnapHorizontal())
319 distance_y = 0;
320 else
321 distance_x = 0;
322 }
323
324 if (distance_x || distance_y) {
325 GestureEventDetails scroll_details(
326 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
327 const gfx::RectF bounding_box = GetBoundingBox(e2, scroll_details.type());
328 const gfx::PointF center = bounding_box.CenterPoint();
329 const gfx::PointF raw_center =
330 center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
331 Send(CreateGesture(scroll_details,
332 e2.GetId(),
333 e2.GetToolType(),
334 e2.GetEventTime(),
335 center.x(),
336 center.y(),
337 raw_center.x(),
338 raw_center.y(),
339 e2.GetPointerCount(),
340 bounding_box,
341 e2.GetFlags()));
342 }
343
344 return true;
345 }
346
OnFling(const MotionEvent & e1,const MotionEvent & e2,float velocity_x,float velocity_y)347 virtual bool OnFling(const MotionEvent& e1,
348 const MotionEvent& e2,
349 float velocity_x,
350 float velocity_y) OVERRIDE {
351 if (snap_scroll_controller_.IsSnappingScrolls()) {
352 if (snap_scroll_controller_.IsSnapHorizontal()) {
353 velocity_y = 0;
354 } else {
355 velocity_x = 0;
356 }
357 }
358
359 if (!velocity_x && !velocity_y)
360 return true;
361
362 if (!scroll_event_sent_) {
363 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
364 // ET_SCROLL_FLING_START to send the fling to the correct target.
365 // The distance traveled in one second is a reasonable scroll start hint.
366 GestureEventDetails scroll_details(
367 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
368 Send(CreateGesture(scroll_details, e2));
369 }
370
371 GestureEventDetails fling_details(
372 ET_SCROLL_FLING_START, velocity_x, velocity_y);
373 Send(CreateGesture(fling_details, e2));
374 return true;
375 }
376
OnSwipe(const MotionEvent & e1,const MotionEvent & e2,float velocity_x,float velocity_y)377 virtual bool OnSwipe(const MotionEvent& e1,
378 const MotionEvent& e2,
379 float velocity_x,
380 float velocity_y) OVERRIDE {
381 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
382 Send(CreateGesture(swipe_details, e2));
383 return true;
384 }
385
OnTwoFingerTap(const MotionEvent & e1,const MotionEvent & e2)386 virtual bool OnTwoFingerTap(const MotionEvent& e1,
387 const MotionEvent& e2) OVERRIDE {
388 // The location of the two finger tap event should be the location of the
389 // primary pointer.
390 GestureEventDetails two_finger_tap_details(
391 ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor());
392 Send(CreateGesture(two_finger_tap_details,
393 e2.GetId(),
394 e2.GetToolType(),
395 e2.GetEventTime(),
396 e1.GetX(),
397 e1.GetY(),
398 e1.GetRawX(),
399 e1.GetRawY(),
400 e2.GetPointerCount(),
401 GetBoundingBox(e2, two_finger_tap_details.type()),
402 e2.GetFlags()));
403 return true;
404 }
405
OnShowPress(const MotionEvent & e)406 virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
407 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS);
408 show_press_event_sent_ = true;
409 Send(CreateGesture(show_press_details, e));
410 }
411
OnSingleTapUp(const MotionEvent & e)412 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
413 // This is a hack to address the issue where user hovers
414 // over a link for longer than double_tap_timeout_, then
415 // OnSingleTapConfirmed() is not triggered. But we still
416 // want to trigger the tap event at UP. So we override
417 // OnSingleTapUp() in this case. This assumes singleTapUp
418 // gets always called before singleTapConfirmed.
419 if (!ignore_single_tap_) {
420 if (e.GetEventTime() - current_down_time_ >
421 config_.gesture_detector_config.double_tap_timeout) {
422 return OnSingleTapConfirmed(e);
423 } else if (!IsDoubleTapEnabled() || config_.disable_click_delay) {
424 // If double-tap has been disabled, there is no need to wait
425 // for the double-tap timeout.
426 return OnSingleTapConfirmed(e);
427 } else {
428 // Notify Blink about this tapUp event anyway, when none of the above
429 // conditions applied.
430 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED, e));
431 }
432 }
433
434 if (e.GetAction() == MotionEvent::ACTION_UP &&
435 !current_longpress_time_.is_null() &&
436 !IsScaleGestureDetectionInProgress()) {
437 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP);
438 Send(CreateGesture(long_tap_details, e));
439 return true;
440 }
441
442 return false;
443 }
444
445 // GestureDetector::DoubleTapListener implementation.
OnSingleTapConfirmed(const MotionEvent & e)446 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE {
447 // Long taps in the edges of the screen have their events delayed by
448 // ContentViewHolder for tab swipe operations. As a consequence of the delay
449 // this method might be called after receiving the up event.
450 // These corner cases should be ignored.
451 if (ignore_single_tap_)
452 return true;
453
454 ignore_single_tap_ = true;
455
456 Send(CreateTapGesture(ET_GESTURE_TAP, e));
457 return true;
458 }
459
OnDoubleTap(const MotionEvent & e)460 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE {
461 return scale_gesture_detector_.OnDoubleTap(e);
462 }
463
OnDoubleTapEvent(const MotionEvent & e)464 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
465 switch (e.GetAction()) {
466 case MotionEvent::ACTION_DOWN:
467 gesture_detector_.set_longpress_enabled(false);
468 break;
469
470 case MotionEvent::ACTION_UP:
471 if (!IsPinchInProgress() && !IsScrollInProgress()) {
472 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e));
473 return true;
474 }
475 break;
476
477 default:
478 break;
479 }
480 return false;
481 }
482
OnLongPress(const MotionEvent & e)483 virtual void OnLongPress(const MotionEvent& e) OVERRIDE {
484 DCHECK(!IsDoubleTapInProgress());
485 SetIgnoreSingleTap(true);
486 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS);
487 Send(CreateGesture(long_press_details, e));
488 }
489
CreateGesture(const GestureEventDetails & details,int motion_event_id,MotionEvent::ToolType primary_tool_type,base::TimeTicks time,float x,float y,float raw_x,float raw_y,size_t touch_point_count,const gfx::RectF & bounding_box,int flags)490 GestureEventData CreateGesture(const GestureEventDetails& details,
491 int motion_event_id,
492 MotionEvent::ToolType primary_tool_type,
493 base::TimeTicks time,
494 float x,
495 float y,
496 float raw_x,
497 float raw_y,
498 size_t touch_point_count,
499 const gfx::RectF& bounding_box,
500 int flags) {
501 return GestureEventData(details,
502 motion_event_id,
503 primary_tool_type,
504 time,
505 x,
506 y,
507 raw_x,
508 raw_y,
509 touch_point_count,
510 bounding_box,
511 flags);
512 }
513
CreateGesture(EventType type,int motion_event_id,MotionEvent::ToolType primary_tool_type,base::TimeTicks time,float x,float y,float raw_x,float raw_y,size_t touch_point_count,const gfx::RectF & bounding_box,int flags)514 GestureEventData CreateGesture(EventType type,
515 int motion_event_id,
516 MotionEvent::ToolType primary_tool_type,
517 base::TimeTicks time,
518 float x,
519 float y,
520 float raw_x,
521 float raw_y,
522 size_t touch_point_count,
523 const gfx::RectF& bounding_box,
524 int flags) {
525 return GestureEventData(GestureEventDetails(type),
526 motion_event_id,
527 primary_tool_type,
528 time,
529 x,
530 y,
531 raw_x,
532 raw_y,
533 touch_point_count,
534 bounding_box,
535 flags);
536 }
537
CreateGesture(const GestureEventDetails & details,const MotionEvent & event)538 GestureEventData CreateGesture(const GestureEventDetails& details,
539 const MotionEvent& event) {
540 return GestureEventData(details,
541 event.GetId(),
542 event.GetToolType(),
543 event.GetEventTime(),
544 event.GetX(),
545 event.GetY(),
546 event.GetRawX(),
547 event.GetRawY(),
548 event.GetPointerCount(),
549 GetBoundingBox(event, details.type()),
550 event.GetFlags());
551 }
552
CreateGesture(EventType type,const MotionEvent & event)553 GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
554 return CreateGesture(GestureEventDetails(type), event);
555 }
556
CreateTapGesture(EventType type,const MotionEvent & event)557 GestureEventData CreateTapGesture(EventType type, const MotionEvent& event) {
558 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
559 // consistent with double tap behavior on a mobile viewport. See
560 // crbug.com/234986 for context.
561 GestureEventDetails details(type);
562 details.set_tap_count(1);
563 return CreateGesture(details, event);
564 }
565
GetBoundingBox(const MotionEvent & event,EventType type)566 gfx::RectF GetBoundingBox(const MotionEvent& event, EventType type) {
567 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
568 float left = std::numeric_limits<float>::max();
569 float top = std::numeric_limits<float>::max();
570 float right = -std::numeric_limits<float>::max();
571 float bottom = -std::numeric_limits<float>::max();
572 for (size_t i = 0; i < event.GetPointerCount(); ++i) {
573 float x, y, diameter;
574 // Only for the show press and tap events, the bounding box is calculated
575 // based on the touch start point and the maximum diameter before the
576 // show press event is sent.
577 if (type == ET_GESTURE_SHOW_PRESS || type == ET_GESTURE_TAP ||
578 type == ET_GESTURE_TAP_UNCONFIRMED) {
579 DCHECK_EQ(0U, i);
580 diameter = max_diameter_before_show_press_;
581 x = tap_down_point_.x();
582 y = tap_down_point_.y();
583 } else {
584 diameter = event.GetTouchMajor(i);
585 x = event.GetX(i);
586 y = event.GetY(i);
587 }
588 x = x - diameter / 2;
589 y = y - diameter / 2;
590 left = std::min(left, x);
591 right = std::max(right, x + diameter);
592 top = std::min(top, y);
593 bottom = std::max(bottom, y + diameter);
594 }
595 return gfx::RectF(left, top, right - left, bottom - top);
596 }
597
SetDoubleTapEnabled(bool enabled)598 void SetDoubleTapEnabled(bool enabled) {
599 DCHECK(!IsDoubleTapInProgress());
600 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
601 }
602
SetMultiTouchZoomEnabled(bool enabled)603 void SetMultiTouchZoomEnabled(bool enabled) {
604 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
605 // the detector from emitting further scale updates for the current touch
606 // sequence. Thus, if multitouch events are enabled in the middle of a
607 // gesture, it will only take effect with the next gesture.
608 ignore_multitouch_zoom_events_ = !enabled;
609 }
610
IsDoubleTapInProgress() const611 bool IsDoubleTapInProgress() const {
612 return gesture_detector_.is_double_tapping() ||
613 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
614 }
615
IsScrollInProgress() const616 bool IsScrollInProgress() const { return scroll_event_sent_; }
617
IsPinchInProgress() const618 bool IsPinchInProgress() const { return pinch_event_sent_; }
619
620 private:
IsScaleGestureDetectionInProgress() const621 bool IsScaleGestureDetectionInProgress() const {
622 return scale_gesture_detector_.IsInProgress();
623 }
624
InDoubleTapMode() const625 bool InDoubleTapMode() const {
626 return scale_gesture_detector_.InDoubleTapMode();
627 }
628
IsDoubleTapEnabled() const629 bool IsDoubleTapEnabled() const {
630 return gesture_detector_.has_doubletap_listener();
631 }
632
SetIgnoreSingleTap(bool value)633 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
634
635 const GestureProvider::Config config_;
636 GestureProviderClient* const client_;
637
638 GestureDetector gesture_detector_;
639 ScaleGestureDetector scale_gesture_detector_;
640 SnapScrollController snap_scroll_controller_;
641
642 base::TimeTicks current_down_time_;
643
644 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
645 // opened after a GESTURE_LONG_PRESS, this is used to insert a
646 // GESTURE_TAP_CANCEL for removing any ::active styling.
647 base::TimeTicks current_longpress_time_;
648
649 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
650 // zoom support is turned off.
651 bool ignore_multitouch_zoom_events_;
652
653 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
654 // always_in_tap_region_ is not reset. So when the last finger is up,
655 // |OnSingleTapUp()| will be mistakenly fired.
656 bool ignore_single_tap_;
657
658 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
659 // current touch sequence.
660 bool pinch_event_sent_;
661 bool scroll_event_sent_;
662
663 // Only track the maximum diameter before the show press event has been
664 // sent and a tap must still be possible for this touch sequence.
665 float max_diameter_before_show_press_;
666
667 gfx::PointF tap_down_point_;
668
669 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
670 // sequence.
671 bool show_press_event_sent_;
672
673 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
674 };
675
676 // GestureProvider
677
GestureProvider(const Config & config,GestureProviderClient * client)678 GestureProvider::GestureProvider(const Config& config,
679 GestureProviderClient* client)
680 : double_tap_support_for_page_(true),
681 double_tap_support_for_platform_(true),
682 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
683 DCHECK(client);
684 DCHECK(!config.min_gesture_bounds_length ||
685 !config.max_gesture_bounds_length ||
686 config.min_gesture_bounds_length <= config.max_gesture_bounds_length);
687 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
688 gesture_listener_.reset(new GestureListenerImpl(config, client));
689 UpdateDoubleTapDetectionSupport();
690 }
691
~GestureProvider()692 GestureProvider::~GestureProvider() {
693 }
694
OnTouchEvent(const MotionEvent & event)695 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
696 TRACE_EVENT1("input",
697 "GestureProvider::OnTouchEvent",
698 "action",
699 GetMotionEventActionName(event.GetAction()));
700
701 DCHECK_NE(0u, event.GetPointerCount());
702
703 if (!CanHandle(event))
704 return false;
705
706 OnTouchEventHandlingBegin(event);
707 gesture_listener_->OnTouchEvent(event);
708 OnTouchEventHandlingEnd(event);
709 uma_histogram_.RecordTouchEvent(event);
710 return true;
711 }
712
SetMultiTouchZoomSupportEnabled(bool enabled)713 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
714 gesture_listener_->SetMultiTouchZoomEnabled(enabled);
715 }
716
SetDoubleTapSupportForPlatformEnabled(bool enabled)717 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
718 if (double_tap_support_for_platform_ == enabled)
719 return;
720 double_tap_support_for_platform_ = enabled;
721 UpdateDoubleTapDetectionSupport();
722 }
723
SetDoubleTapSupportForPageEnabled(bool enabled)724 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
725 if (double_tap_support_for_page_ == enabled)
726 return;
727 double_tap_support_for_page_ = enabled;
728 UpdateDoubleTapDetectionSupport();
729 }
730
IsScrollInProgress() const731 bool GestureProvider::IsScrollInProgress() const {
732 return gesture_listener_->IsScrollInProgress();
733 }
734
IsPinchInProgress() const735 bool GestureProvider::IsPinchInProgress() const {
736 return gesture_listener_->IsPinchInProgress();
737 }
738
IsDoubleTapInProgress() const739 bool GestureProvider::IsDoubleTapInProgress() const {
740 return gesture_listener_->IsDoubleTapInProgress();
741 }
742
CanHandle(const MotionEvent & event) const743 bool GestureProvider::CanHandle(const MotionEvent& event) const {
744 // Aura requires one cancel event per touch point, whereas Android requires
745 // one cancel event per touch sequence. Thus we need to allow extra cancel
746 // events.
747 return current_down_event_ || event.GetAction() == MotionEvent::ACTION_DOWN ||
748 event.GetAction() == MotionEvent::ACTION_CANCEL;
749 }
750
OnTouchEventHandlingBegin(const MotionEvent & event)751 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
752 switch (event.GetAction()) {
753 case MotionEvent::ACTION_DOWN:
754 current_down_event_ = event.Clone();
755 if (gesture_begin_end_types_enabled_)
756 gesture_listener_->Send(
757 gesture_listener_->CreateGesture(ET_GESTURE_BEGIN, event));
758 break;
759 case MotionEvent::ACTION_POINTER_DOWN:
760 if (gesture_begin_end_types_enabled_) {
761 const int action_index = event.GetActionIndex();
762 gesture_listener_->Send(gesture_listener_->CreateGesture(
763 ET_GESTURE_BEGIN,
764 event.GetId(),
765 event.GetToolType(),
766 event.GetEventTime(),
767 event.GetX(action_index),
768 event.GetY(action_index),
769 event.GetRawX(action_index),
770 event.GetRawY(action_index),
771 event.GetPointerCount(),
772 gesture_listener_->GetBoundingBox(event, ET_GESTURE_BEGIN),
773 event.GetFlags()));
774 }
775 break;
776 case MotionEvent::ACTION_POINTER_UP:
777 case MotionEvent::ACTION_UP:
778 case MotionEvent::ACTION_CANCEL:
779 case MotionEvent::ACTION_MOVE:
780 break;
781 }
782 }
783
OnTouchEventHandlingEnd(const MotionEvent & event)784 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
785 switch (event.GetAction()) {
786 case MotionEvent::ACTION_UP:
787 case MotionEvent::ACTION_CANCEL: {
788 if (gesture_begin_end_types_enabled_)
789 gesture_listener_->Send(
790 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
791
792 current_down_event_.reset();
793
794 UpdateDoubleTapDetectionSupport();
795 break;
796 }
797 case MotionEvent::ACTION_POINTER_UP:
798 if (gesture_begin_end_types_enabled_)
799 gesture_listener_->Send(
800 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
801 break;
802 case MotionEvent::ACTION_DOWN:
803 case MotionEvent::ACTION_POINTER_DOWN:
804 case MotionEvent::ACTION_MOVE:
805 break;
806 }
807 }
808
UpdateDoubleTapDetectionSupport()809 void GestureProvider::UpdateDoubleTapDetectionSupport() {
810 // The GestureDetector requires that any provided DoubleTapListener remain
811 // attached to it for the duration of a touch sequence. Defer any potential
812 // null'ing of the listener until the sequence has ended.
813 if (current_down_event_)
814 return;
815
816 const bool double_tap_enabled =
817 double_tap_support_for_page_ && double_tap_support_for_platform_;
818 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
819 }
820
821 } // namespace ui
822