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
16namespace ui {
17namespace {
18
19// Double-tap drag zoom sensitivity (speed).
20const float kDoubleTapDragZoomSpeed = 0.005f;
21
22const 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
40gfx::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
62GestureProvider::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
70GestureProvider::Config::~Config() {
71}
72
73// GestureProvider::GestureListener
74
75class GestureProvider::GestureListenerImpl
76    : public ScaleGestureDetector::ScaleGestureListener,
77      public GestureDetector::GestureListener,
78      public GestureDetector::DoubleTapListener {
79 public:
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
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
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.
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
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
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.
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
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
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
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
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
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
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.
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
460  virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE {
461    return scale_gesture_detector_.OnDoubleTap(e);
462  }
463
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
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
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
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
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
553  GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
554    return CreateGesture(GestureEventDetails(type), event);
555  }
556
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
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
598  void SetDoubleTapEnabled(bool enabled) {
599    DCHECK(!IsDoubleTapInProgress());
600    gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
601  }
602
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
611  bool IsDoubleTapInProgress() const {
612    return gesture_detector_.is_double_tapping() ||
613           (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
614  }
615
616  bool IsScrollInProgress() const { return scroll_event_sent_; }
617
618  bool IsPinchInProgress() const { return pinch_event_sent_; }
619
620 private:
621  bool IsScaleGestureDetectionInProgress() const {
622    return scale_gesture_detector_.IsInProgress();
623  }
624
625  bool InDoubleTapMode() const {
626    return scale_gesture_detector_.InDoubleTapMode();
627  }
628
629  bool IsDoubleTapEnabled() const {
630    return gesture_detector_.has_doubletap_listener();
631  }
632
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
678GestureProvider::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
692GestureProvider::~GestureProvider() {
693}
694
695bool 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
713void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
714  gesture_listener_->SetMultiTouchZoomEnabled(enabled);
715}
716
717void 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
724void 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
731bool GestureProvider::IsScrollInProgress() const {
732  return gesture_listener_->IsScrollInProgress();
733}
734
735bool GestureProvider::IsPinchInProgress() const {
736  return gesture_listener_->IsPinchInProgress();
737}
738
739bool GestureProvider::IsDoubleTapInProgress() const {
740  return gesture_listener_->IsDoubleTapInProgress();
741}
742
743bool 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
751void 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
784void 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
809void 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