• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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