1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_layers_GestureEventListener_h
8 #define mozilla_layers_GestureEventListener_h
9 
10 #include "InputData.h"  // for MultiTouchInput, etc
11 #include "Units.h"
12 #include "mozilla/EventForwards.h"  // for nsEventStatus
13 #include "mozilla/RefPtr.h"         // for RefPtr
14 #include "nsISupportsImpl.h"
15 #include "nsTArray.h"  // for nsTArray
16 
17 namespace mozilla {
18 
19 class CancelableRunnable;
20 
21 namespace layers {
22 
23 class AsyncPanZoomController;
24 
25 /**
26  * Platform-non-specific, generalized gesture event listener. This class
27  * intercepts all touches events on their way to AsyncPanZoomController and
28  * determines whether or not they are part of a gesture.
29  *
30  * For example, seeing that two fingers are on the screen means that the user
31  * wants to do a pinch gesture, so we don't forward the touches along to
32  * AsyncPanZoomController since it will think that they are just trying to pan
33  * the screen. Instead, we generate a PinchGestureInput and send that. If the
34  * touch event is not part of a gesture, we just return nsEventStatus_eIgnore
35  * and AsyncPanZoomController is expected to handle it.
36  */
37 class GestureEventListener final {
38  public:
39   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener)
40 
41   explicit GestureEventListener(
42       AsyncPanZoomController* aAsyncPanZoomController);
43 
44   // --------------------------------------------------------------------------
45   // These methods must only be called on the controller/UI thread.
46   //
47 
48   /**
49    * General input handler for a touch event. If the touch event is not a part
50    * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise,
51    * it gets consumed here and never forwarded along.
52    */
53   nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
54 
55   /**
56    * Returns the identifier of the touch in the last touch event processed by
57    * this GestureEventListener. This should only be called when the last touch
58    * event contained only one touch.
59    */
60   int32_t GetLastTouchIdentifier() const;
61 
62   /**
63    * Function used to disable long tap gestures.
64    *
65    * On slow running tests, drags and touch events can be misinterpreted
66    * as a long tap. This allows tests to disable long tap gesture detection.
67    */
68   static void SetLongTapEnabled(bool aLongTapEnabled);
69   static bool IsLongTapEnabled();
70 
71  private:
72   // Private destructor, to discourage deletion outside of Release():
73   ~GestureEventListener();
74 
75   /**
76    * States of GEL finite-state machine.
77    */
78   enum GestureState {
79     // This is the initial and final state of any gesture.
80     // In this state there's no gesture going on, and we don't think we're
81     // about to enter one.
82     // Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN,
83     // GESTURE_MULTI_TOUCH_DOWN.
84     GESTURE_NONE,
85 
86     // A touch start with a single touch point has just happened.
87     // After having gotten into this state we start timers for MAX_TAP_TIME and
88     // StaticPrefs::ui_click_hold_context_menus_delay().
89     // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
90     //                      GESTURE_FIRST_SINGLE_TOUCH_UP,
91     //                      GESTURE_LONG_TOUCH_DOWN,
92     //                      GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN.
93     GESTURE_FIRST_SINGLE_TOUCH_DOWN,
94 
95     // While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got
96     // triggered. Now we'll trigger either a single tap if a user lifts her
97     // finger or a long tap if StaticPrefs::ui_click_hold_context_menus_delay()
98     // happens first.
99     // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
100     //                      GESTURE_LONG_TOUCH_DOWN.
101     GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN,
102 
103     // A user put her finger down and lifted it up quickly enough.
104     // After having gotten into this state we clear the timer for MAX_TAP_TIME.
105     // Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
106     //                      GESTURE_MULTI_TOUCH_DOWN.
107     GESTURE_FIRST_SINGLE_TOUCH_UP,
108 
109     // A user put down her finger again right after a single tap thus the
110     // gesture can't be a single tap, but rather a double tap. But we're
111     // still not sure about that until the user lifts her finger again.
112     // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_ONE_TOUCH_PINCH,
113     //                      GESTURE_NONE.
114     GESTURE_SECOND_SINGLE_TOUCH_DOWN,
115 
116     // A long touch has happened, but the user still keeps her finger down.
117     // We'll trigger a "long tap up" event when the finger is up.
118     // Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
119     GESTURE_LONG_TOUCH_DOWN,
120 
121     // We have detected that two or more fingers are on the screen, but there
122     // hasn't been enough movement yet to make us start actually zooming the
123     // screen.
124     // Allowed next states: GESTURE_PINCH, GESTURE_NONE
125     GESTURE_MULTI_TOUCH_DOWN,
126 
127     // There are two or more fingers on the screen, and the user has already
128     // pinched enough for us to start zooming the screen.
129     // Allowed next states: GESTURE_NONE
130     GESTURE_PINCH,
131 
132     // The user has double tapped, but not lifted her finger, and moved her
133     // finger more than PINCH_START_THRESHOLD.
134     // Allowed next states: GESTURE_NONE.
135     GESTURE_ONE_TOUCH_PINCH
136   };
137 
138   /**
139    * These HandleInput* functions comprise input alphabet of the GEL
140    * finite-state machine triggering state transitions.
141    */
142   nsEventStatus HandleInputTouchSingleStart();
143   nsEventStatus HandleInputTouchMultiStart();
144   nsEventStatus HandleInputTouchEnd();
145   nsEventStatus HandleInputTouchMove();
146   nsEventStatus HandleInputTouchCancel();
147   void HandleInputTimeoutLongTap();
148   void HandleInputTimeoutMaxTap(bool aDuringFastFling);
149 
150   void EnterFirstSingleTouchDown();
151 
152   void TriggerSingleTapConfirmedEvent();
153 
154   bool MoveDistanceExceeds(ScreenCoord aThreshold) const;
155   bool MoveDistanceIsLarge() const;
156   bool SecondTapIsFar() const;
157 
158   /**
159    * Returns current vertical span, counting from the where the gesture first
160    * began (after a brief delay detecting the gesture from first touch).
161    */
162   ScreenCoord GetYSpanFromGestureStartPoint();
163 
164   /**
165    * Do actual state transition and reset substates.
166    */
167   void SetState(GestureState aState);
168 
169   RefPtr<AsyncPanZoomController> mAsyncPanZoomController;
170 
171   /**
172    * Array containing all active touches. When a touch happens it, gets added to
173    * this array, even if we choose not to handle it. When it ends, we remove it.
174    * We need to maintain this array in order to detect the end of the
175    * "multitouch" states because touch start events contain all current touches,
176    * but touch end events contain only those touches that have gone.
177    */
178   nsTArray<SingleTouchData> mTouches;
179 
180   /**
181    * Current state we're dealing with.
182    */
183   GestureState mState;
184 
185   /**
186    * Total change in span since we detected a pinch gesture. Only used when we
187    * are in the |GESTURE_WAITING_PINCH| state and need to know how far zoomed
188    * out we are compared to our original pinch span. Note that this does _not_
189    * continue to be updated once we jump into the |GESTURE_PINCH| state.
190    */
191   ScreenCoord mSpanChange;
192 
193   /**
194    * Previous span calculated for the purposes of setting inside a
195    * PinchGestureInput.
196    */
197   ScreenCoord mPreviousSpan;
198 
199   /* Properties similar to mSpanChange and mPreviousSpan, but for the focus */
200   ScreenCoord mFocusChange;
201   ScreenPoint mPreviousFocus;
202 
203   /**
204    * Cached copy of the last touch input.
205    */
206   MultiTouchInput mLastTouchInput;
207 
208   /**
209    * Cached copy of the last tap gesture input.
210    * In the situation when we have a tap followed by a pinch we lose info
211    * about tap since we keep only last input and to dispatch it correctly
212    * we save last tap copy into this variable.
213    * For more info see bug 947892.
214    */
215   MultiTouchInput mLastTapInput;
216 
217   /**
218    * Position of the last touch that exceeds the GetTouchStartTolerance when
219    * performing a one-touch-pinch gesture; using the mTouchStartPosition is
220    * slightly inaccurate because by the time the touch position has passed
221    * the threshold for the gesture, there is already a span that the zoom
222    * is calculated from, instead of starting at 1.0 when the threshold gets
223    * passed.
224    */
225   ScreenPoint mOneTouchPinchStartPosition;
226 
227   /**
228    * Position of the last touch starting. This is only valid during an attempt
229    * to determine if a touch is a tap. If a touch point moves away from
230    * mTouchStartPosition to the distance greater than
231    * AsyncPanZoomController::GetTouchStartTolerance() while in
232    * GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN
233    * or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is
234    * not tap.
235    */
236   ScreenPoint mTouchStartPosition;
237 
238   /**
239    * We store the window/GeckoView's display offset as well, so we can
240    * track the user's physical touch movements in absolute display coordinates.
241    */
242   ExternalPoint mTouchStartOffset;
243 
244   /**
245    * Task used to timeout a long tap. This gets posted to the UI thread such
246    * that it runs a time when a single tap happens. We cache it so that
247    * we can cancel it if any other touch event happens.
248    *
249    * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN
250    * and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states.
251    *
252    * CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set
253    * it to null.
254    */
255   RefPtr<CancelableRunnable> mLongTapTimeoutTask;
256   void CancelLongTapTimeoutTask();
257   void CreateLongTapTimeoutTask();
258 
259   /**
260    * Task used to timeout a single tap or a double tap.
261    *
262    * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN,
263    * GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
264    *
265    * CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
266    * it to null.
267    */
268   RefPtr<CancelableRunnable> mMaxTapTimeoutTask;
269   void CancelMaxTapTimeoutTask();
270   void CreateMaxTapTimeoutTask();
271 
272   /**
273    * Tracks whether the single-tap event was already sent to content. This is
274    * needed because it affects how the double-tap gesture, if detected, is
275    * handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP
276    * and GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is
277    * stored in a Maybe which is set to Nothing() at all other times.
278    */
279   Maybe<bool> mSingleTapSent;
280 };
281 
282 }  // namespace layers
283 }  // namespace mozilla
284 
285 #endif
286