Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et 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 : #include "GestureEventListener.h"
8 : #include <math.h> // for fabsf
9 : #include <stddef.h> // for size_t
10 : #include "AsyncPanZoomController.h" // for AsyncPanZoomController
11 : #include "InputBlockState.h" // for TouchBlockState
12 : #include "base/task.h" // for CancelableTask, etc
13 : #include "gfxPrefs.h" // for gfxPrefs
14 : #include "InputBlockState.h" // for TouchBlockState
15 : #include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
16 : #include "nsDebug.h" // for NS_WARNING
17 : #include "nsMathUtils.h" // for NS_hypot
18 :
19 : #define GEL_LOG(...)
20 : // #define GEL_LOG(...) printf_stderr("GEL: " __VA_ARGS__)
21 :
22 : namespace mozilla {
23 : namespace layers {
24 :
25 : /**
26 : * Maximum time for a touch on the screen and corresponding lift of the finger
27 : * to be considered a tap. This also applies to double taps, except that it is
28 : * used twice.
29 : */
30 : static const uint32_t MAX_TAP_TIME = 300;
31 :
32 : /**
33 : * Amount of span or focus change needed to take us from the GESTURE_WAITING_PINCH
34 : * state to the GESTURE_PINCH state. This is measured as either a change in distance
35 : * between the fingers used to compute the span ratio, or the a change in
36 : * position of the focus point between the two fingers.
37 : */
38 : static const float PINCH_START_THRESHOLD = 35.0f;
39 :
40 : /**
41 : * Determines how fast a one touch pinch zooms in and out. The greater the
42 : * value, the faster it zooms.
43 : */
44 : static const float ONE_TOUCH_PINCH_SPEED = 0.005f;
45 :
46 : static bool sLongTapEnabled = true;
47 :
48 0 : ParentLayerPoint GetCurrentFocus(const MultiTouchInput& aEvent)
49 : {
50 0 : const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
51 0 : const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
52 0 : return (firstTouch + secondTouch) / 2;
53 : }
54 :
55 0 : ParentLayerCoord GetCurrentSpan(const MultiTouchInput& aEvent)
56 : {
57 0 : const ParentLayerPoint& firstTouch = aEvent.mTouches[0].mLocalScreenPoint;
58 0 : const ParentLayerPoint& secondTouch = aEvent.mTouches[1].mLocalScreenPoint;
59 0 : ParentLayerPoint delta = secondTouch - firstTouch;
60 0 : return delta.Length();
61 : }
62 :
63 0 : ParentLayerCoord GestureEventListener::GetYSpanFromStartPoint()
64 : {
65 0 : const ParentLayerPoint start = mTouchStartPosition;
66 0 : const ParentLayerPoint& current = mTouches[0].mLocalScreenPoint;
67 0 : return current.y - start.y;
68 : }
69 :
70 0 : TapGestureInput CreateTapEvent(const MultiTouchInput& aTouch, TapGestureInput::TapGestureType aType)
71 : {
72 : return TapGestureInput(aType,
73 0 : aTouch.mTime,
74 : aTouch.mTimeStamp,
75 0 : aTouch.mTouches[0].mScreenPoint,
76 0 : aTouch.modifiers);
77 : }
78 :
79 2 : GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
80 : : mAsyncPanZoomController(aAsyncPanZoomController),
81 : mState(GESTURE_NONE),
82 : mSpanChange(0.0f),
83 : mPreviousSpan(0.0f),
84 : mFocusChange(0.0f),
85 : mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
86 : mLastTapInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0),
87 : mLongTapTimeoutTask(nullptr),
88 2 : mMaxTapTimeoutTask(nullptr)
89 : {
90 2 : }
91 :
92 0 : GestureEventListener::~GestureEventListener()
93 : {
94 0 : }
95 :
96 0 : nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
97 : {
98 : GEL_LOG("Receiving event type %d with %" PRIuSIZE " touches in state %d\n", aEvent.mType, aEvent.mTouches.Length(), mState);
99 :
100 0 : nsEventStatus rv = nsEventStatus_eIgnore;
101 :
102 : // Cache the current event since it may become the single or long tap that we
103 : // send.
104 0 : mLastTouchInput = aEvent;
105 :
106 0 : switch (aEvent.mType) {
107 : case MultiTouchInput::MULTITOUCH_START:
108 0 : mTouches.Clear();
109 : // Cache every touch.
110 0 : for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
111 0 : mTouches.AppendElement(aEvent.mTouches[i]);
112 : }
113 :
114 0 : if (aEvent.mTouches.Length() == 1) {
115 0 : rv = HandleInputTouchSingleStart();
116 : } else {
117 0 : rv = HandleInputTouchMultiStart();
118 : }
119 0 : break;
120 : case MultiTouchInput::MULTITOUCH_MOVE:
121 : // Update the screen points of the cached touches.
122 0 : for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
123 0 : for (size_t j = 0; j < mTouches.Length(); j++) {
124 0 : if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
125 0 : mTouches[j].mScreenPoint = aEvent.mTouches[i].mScreenPoint;
126 0 : mTouches[j].mLocalScreenPoint = aEvent.mTouches[i].mLocalScreenPoint;
127 : }
128 : }
129 : }
130 0 : rv = HandleInputTouchMove();
131 0 : break;
132 : case MultiTouchInput::MULTITOUCH_END:
133 : // Remove the cache of the touch that ended.
134 0 : for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
135 0 : for (size_t j = 0; j < mTouches.Length(); j++) {
136 0 : if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
137 0 : mTouches.RemoveElementAt(j);
138 0 : break;
139 : }
140 : }
141 : }
142 :
143 0 : rv = HandleInputTouchEnd();
144 0 : break;
145 : case MultiTouchInput::MULTITOUCH_CANCEL:
146 0 : mTouches.Clear();
147 0 : rv = HandleInputTouchCancel();
148 0 : break;
149 : }
150 :
151 0 : return rv;
152 : }
153 :
154 0 : int32_t GestureEventListener::GetLastTouchIdentifier() const
155 : {
156 0 : if (mTouches.Length() != 1) {
157 : NS_WARNING("GetLastTouchIdentifier() called when last touch event "
158 0 : "did not have one touch");
159 : }
160 0 : return mTouches.IsEmpty() ? -1 : mTouches[0].mIdentifier;
161 : }
162 :
163 : /* static */
164 0 : void GestureEventListener::SetLongTapEnabled(bool aLongTapEnabled)
165 : {
166 0 : sLongTapEnabled = aLongTapEnabled;
167 0 : }
168 :
169 0 : nsEventStatus GestureEventListener::HandleInputTouchSingleStart()
170 : {
171 0 : switch (mState) {
172 : case GESTURE_NONE:
173 0 : SetState(GESTURE_FIRST_SINGLE_TOUCH_DOWN);
174 0 : mTouchStartPosition = mLastTouchInput.mTouches[0].mLocalScreenPoint;
175 :
176 0 : if (sLongTapEnabled) {
177 0 : CreateLongTapTimeoutTask();
178 : }
179 0 : CreateMaxTapTimeoutTask();
180 0 : break;
181 : case GESTURE_FIRST_SINGLE_TOUCH_UP:
182 0 : SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
183 0 : break;
184 : default:
185 0 : NS_WARNING("Unhandled state upon single touch start");
186 0 : SetState(GESTURE_NONE);
187 0 : break;
188 : }
189 :
190 0 : return nsEventStatus_eIgnore;
191 : }
192 :
193 0 : nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
194 : {
195 0 : nsEventStatus rv = nsEventStatus_eIgnore;
196 :
197 0 : switch (mState) {
198 : case GESTURE_NONE:
199 0 : SetState(GESTURE_MULTI_TOUCH_DOWN);
200 0 : break;
201 : case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
202 0 : CancelLongTapTimeoutTask();
203 0 : CancelMaxTapTimeoutTask();
204 0 : SetState(GESTURE_MULTI_TOUCH_DOWN);
205 : // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
206 0 : rv = nsEventStatus_eConsumeNoDefault;
207 0 : break;
208 : case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
209 0 : CancelLongTapTimeoutTask();
210 0 : SetState(GESTURE_MULTI_TOUCH_DOWN);
211 : // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
212 0 : rv = nsEventStatus_eConsumeNoDefault;
213 0 : break;
214 : case GESTURE_FIRST_SINGLE_TOUCH_UP:
215 : case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
216 : // Cancel wait for double tap
217 0 : CancelMaxTapTimeoutTask();
218 0 : MOZ_ASSERT(mSingleTapSent.isSome());
219 0 : if (!mSingleTapSent.value()) {
220 0 : TriggerSingleTapConfirmedEvent();
221 : }
222 0 : mSingleTapSent = Nothing();
223 0 : SetState(GESTURE_MULTI_TOUCH_DOWN);
224 : // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
225 0 : rv = nsEventStatus_eConsumeNoDefault;
226 0 : break;
227 : case GESTURE_LONG_TOUCH_DOWN:
228 0 : SetState(GESTURE_MULTI_TOUCH_DOWN);
229 0 : break;
230 : case GESTURE_MULTI_TOUCH_DOWN:
231 : case GESTURE_PINCH:
232 : // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
233 0 : rv = nsEventStatus_eConsumeNoDefault;
234 0 : break;
235 : default:
236 0 : NS_WARNING("Unhandled state upon multitouch start");
237 0 : SetState(GESTURE_NONE);
238 0 : break;
239 : }
240 :
241 0 : return rv;
242 : }
243 :
244 0 : bool GestureEventListener::MoveDistanceIsLarge()
245 : {
246 0 : const ParentLayerPoint start = mLastTouchInput.mTouches[0].mLocalScreenPoint;
247 0 : ParentLayerPoint delta = start - mTouchStartPosition;
248 0 : ScreenPoint screenDelta = mAsyncPanZoomController->ToScreenCoordinates(delta, start);
249 0 : return (screenDelta.Length() > AsyncPanZoomController::GetTouchStartTolerance());
250 : }
251 :
252 0 : nsEventStatus GestureEventListener::HandleInputTouchMove()
253 : {
254 0 : nsEventStatus rv = nsEventStatus_eIgnore;
255 :
256 0 : switch (mState) {
257 : case GESTURE_NONE:
258 : // Ignore this input signal as the corresponding events get handled by APZC
259 0 : break;
260 :
261 : case GESTURE_LONG_TOUCH_DOWN:
262 0 : if (MoveDistanceIsLarge()) {
263 : // So that we don't fire a long-tap-up if the user moves around after a
264 : // long-tap
265 0 : SetState(GESTURE_NONE);
266 : }
267 0 : break;
268 :
269 : case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
270 : case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
271 : // If we move too much, bail out of the tap.
272 0 : if (MoveDistanceIsLarge()) {
273 0 : CancelLongTapTimeoutTask();
274 0 : CancelMaxTapTimeoutTask();
275 0 : mSingleTapSent = Nothing();
276 0 : SetState(GESTURE_NONE);
277 : }
278 0 : break;
279 : }
280 :
281 : // The user has performed a double tap, but not lifted her finger.
282 : case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
283 : // If touch has moved noticeably (within MAX_TAP_TIME), change state.
284 0 : if (MoveDistanceIsLarge()) {
285 0 : CancelLongTapTimeoutTask();
286 0 : CancelMaxTapTimeoutTask();
287 0 : mSingleTapSent = Nothing();
288 0 : if (!gfxPrefs::APZOneTouchPinchEnabled()) {
289 : // If the one-touch-pinch feature is disabled, bail out of the double-
290 : // tap gesture instead.
291 0 : SetState(GESTURE_NONE);
292 0 : break;
293 : }
294 :
295 0 : SetState(GESTURE_ONE_TOUCH_PINCH);
296 :
297 0 : ParentLayerCoord currentSpan = 1.0f;
298 0 : ParentLayerPoint currentFocus = mTouchStartPosition;
299 :
300 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
301 : mLastTouchInput.mTime,
302 : mLastTouchInput.mTimeStamp,
303 : currentFocus,
304 : currentSpan,
305 : currentSpan,
306 0 : mLastTouchInput.modifiers);
307 :
308 0 : rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
309 :
310 0 : mPreviousSpan = currentSpan;
311 0 : mPreviousFocus = currentFocus;
312 : }
313 0 : break;
314 : }
315 :
316 : case GESTURE_MULTI_TOUCH_DOWN: {
317 0 : if (mLastTouchInput.mTouches.Length() < 2) {
318 0 : NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
319 0 : break;
320 : }
321 :
322 0 : ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
323 0 : ParentLayerPoint currentFocus = GetCurrentFocus(mLastTouchInput);
324 :
325 0 : mSpanChange += fabsf(currentSpan - mPreviousSpan);
326 0 : mFocusChange += (currentFocus - mPreviousFocus).Length();
327 0 : if (mSpanChange > PINCH_START_THRESHOLD ||
328 0 : mFocusChange > PINCH_START_THRESHOLD) {
329 0 : SetState(GESTURE_PINCH);
330 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
331 : mLastTouchInput.mTime,
332 : mLastTouchInput.mTimeStamp,
333 : currentFocus,
334 : currentSpan,
335 : currentSpan,
336 0 : mLastTouchInput.modifiers);
337 :
338 0 : rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
339 : } else {
340 : // Prevent APZC::OnTouchMove from processing a move event when two
341 : // touches are active
342 0 : rv = nsEventStatus_eConsumeNoDefault;
343 : }
344 :
345 0 : mPreviousSpan = currentSpan;
346 0 : mPreviousFocus = currentFocus;
347 0 : break;
348 : }
349 :
350 : case GESTURE_PINCH: {
351 0 : if (mLastTouchInput.mTouches.Length() < 2) {
352 0 : NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state");
353 : // Prevent APZC::OnTouchMove() from handling this wrong input
354 0 : rv = nsEventStatus_eConsumeNoDefault;
355 0 : break;
356 : }
357 :
358 0 : ParentLayerCoord currentSpan = GetCurrentSpan(mLastTouchInput);
359 :
360 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
361 : mLastTouchInput.mTime,
362 : mLastTouchInput.mTimeStamp,
363 0 : GetCurrentFocus(mLastTouchInput),
364 : currentSpan,
365 : mPreviousSpan,
366 0 : mLastTouchInput.modifiers);
367 :
368 0 : rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
369 0 : mPreviousSpan = currentSpan;
370 :
371 0 : break;
372 : }
373 :
374 : case GESTURE_ONE_TOUCH_PINCH: {
375 0 : ParentLayerCoord currentSpan = GetYSpanFromStartPoint();
376 0 : float effectiveSpan = 1.0f + (fabsf(currentSpan.value) * ONE_TOUCH_PINCH_SPEED);
377 0 : ParentLayerPoint currentFocus = mTouchStartPosition;
378 :
379 : // Invert zoom.
380 0 : if (currentSpan.value < 0) {
381 0 : effectiveSpan = 1.0f / effectiveSpan;
382 : }
383 :
384 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
385 : mLastTouchInput.mTime,
386 : mLastTouchInput.mTimeStamp,
387 : currentFocus,
388 : effectiveSpan,
389 : mPreviousSpan,
390 0 : mLastTouchInput.modifiers);
391 :
392 0 : rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
393 0 : mPreviousSpan = effectiveSpan;
394 :
395 0 : break;
396 : }
397 :
398 : default:
399 0 : NS_WARNING("Unhandled state upon touch move");
400 0 : SetState(GESTURE_NONE);
401 0 : break;
402 : }
403 :
404 0 : return rv;
405 : }
406 :
407 0 : nsEventStatus GestureEventListener::HandleInputTouchEnd()
408 : {
409 : // We intentionally do not pass apzc return statuses up since
410 : // it may cause apzc stay in the touching state even after
411 : // gestures are completed (please see Bug 1013378 for reference).
412 :
413 0 : nsEventStatus rv = nsEventStatus_eIgnore;
414 :
415 0 : switch (mState) {
416 : case GESTURE_NONE:
417 : // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
418 0 : break;
419 :
420 : case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
421 0 : CancelLongTapTimeoutTask();
422 0 : CancelMaxTapTimeoutTask();
423 0 : nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(
424 0 : CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP));
425 0 : mSingleTapSent = Some(tapupStatus != nsEventStatus_eIgnore);
426 0 : SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
427 0 : CreateMaxTapTimeoutTask();
428 0 : break;
429 : }
430 :
431 : case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
432 0 : CancelMaxTapTimeoutTask();
433 0 : MOZ_ASSERT(mSingleTapSent.isSome());
434 0 : mAsyncPanZoomController->HandleGestureEvent(
435 0 : CreateTapEvent(mLastTouchInput,
436 0 : mSingleTapSent.value() ? TapGestureInput::TAPGESTURE_SECOND
437 0 : : TapGestureInput::TAPGESTURE_DOUBLE));
438 0 : mSingleTapSent = Nothing();
439 0 : SetState(GESTURE_NONE);
440 0 : break;
441 : }
442 :
443 : case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
444 0 : CancelLongTapTimeoutTask();
445 0 : SetState(GESTURE_NONE);
446 0 : TriggerSingleTapConfirmedEvent();
447 0 : break;
448 :
449 : case GESTURE_LONG_TOUCH_DOWN: {
450 0 : SetState(GESTURE_NONE);
451 0 : mAsyncPanZoomController->HandleGestureEvent(
452 0 : CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG_UP));
453 0 : break;
454 : }
455 :
456 : case GESTURE_MULTI_TOUCH_DOWN:
457 0 : if (mTouches.Length() < 2) {
458 0 : SetState(GESTURE_NONE);
459 : }
460 0 : break;
461 :
462 : case GESTURE_PINCH:
463 0 : if (mTouches.Length() < 2) {
464 0 : SetState(GESTURE_NONE);
465 0 : ParentLayerPoint point(-1, -1);
466 0 : if (mTouches.Length() == 1) {
467 : // As user still keeps one finger down the event's focus point should
468 : // contain meaningful data.
469 0 : point = mTouches[0].mLocalScreenPoint;
470 : }
471 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
472 : mLastTouchInput.mTime,
473 : mLastTouchInput.mTimeStamp,
474 : point,
475 : 1.0f,
476 : 1.0f,
477 0 : mLastTouchInput.modifiers);
478 0 : mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
479 : }
480 :
481 0 : rv = nsEventStatus_eConsumeNoDefault;
482 :
483 0 : break;
484 :
485 : case GESTURE_ONE_TOUCH_PINCH: {
486 0 : SetState(GESTURE_NONE);
487 0 : ParentLayerPoint point(-1, -1);
488 : PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
489 : mLastTouchInput.mTime,
490 : mLastTouchInput.mTimeStamp,
491 : point,
492 : 1.0f,
493 : 1.0f,
494 0 : mLastTouchInput.modifiers);
495 0 : mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
496 :
497 0 : rv = nsEventStatus_eConsumeNoDefault;
498 :
499 0 : break;
500 : }
501 :
502 : default:
503 0 : NS_WARNING("Unhandled state upon touch end");
504 0 : SetState(GESTURE_NONE);
505 0 : break;
506 : }
507 :
508 0 : return rv;
509 : }
510 :
511 0 : nsEventStatus GestureEventListener::HandleInputTouchCancel()
512 : {
513 0 : mSingleTapSent = Nothing();
514 0 : SetState(GESTURE_NONE);
515 0 : CancelMaxTapTimeoutTask();
516 0 : CancelLongTapTimeoutTask();
517 0 : return nsEventStatus_eIgnore;
518 : }
519 :
520 0 : void GestureEventListener::HandleInputTimeoutLongTap()
521 : {
522 : GEL_LOG("Running long-tap timeout task in state %d\n", mState);
523 :
524 0 : mLongTapTimeoutTask = nullptr;
525 :
526 0 : switch (mState) {
527 : case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
528 : // just in case MAX_TAP_TIME > ContextMenuDelay cancel MAX_TAP timer
529 : // and fall through
530 0 : CancelMaxTapTimeoutTask();
531 : MOZ_FALLTHROUGH;
532 : case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
533 0 : SetState(GESTURE_LONG_TOUCH_DOWN);
534 0 : mAsyncPanZoomController->HandleGestureEvent(
535 0 : CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG));
536 0 : break;
537 : }
538 : default:
539 0 : NS_WARNING("Unhandled state upon long tap timeout");
540 0 : SetState(GESTURE_NONE);
541 0 : break;
542 : }
543 0 : }
544 :
545 0 : void GestureEventListener::HandleInputTimeoutMaxTap(bool aDuringFastFling)
546 : {
547 : GEL_LOG("Running max-tap timeout task in state %d\n", mState);
548 :
549 0 : mMaxTapTimeoutTask = nullptr;
550 :
551 0 : if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) {
552 0 : SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN);
553 0 : } else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP ||
554 0 : mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
555 0 : MOZ_ASSERT(mSingleTapSent.isSome());
556 0 : if (!aDuringFastFling && !mSingleTapSent.value()) {
557 0 : TriggerSingleTapConfirmedEvent();
558 : }
559 0 : mSingleTapSent = Nothing();
560 0 : SetState(GESTURE_NONE);
561 : } else {
562 0 : NS_WARNING("Unhandled state upon MAX_TAP timeout");
563 0 : SetState(GESTURE_NONE);
564 : }
565 0 : }
566 :
567 0 : void GestureEventListener::TriggerSingleTapConfirmedEvent()
568 : {
569 0 : mAsyncPanZoomController->HandleGestureEvent(
570 0 : CreateTapEvent(mLastTapInput, TapGestureInput::TAPGESTURE_CONFIRMED));
571 0 : }
572 :
573 0 : void GestureEventListener::SetState(GestureState aState)
574 : {
575 0 : mState = aState;
576 :
577 0 : if (mState == GESTURE_NONE) {
578 0 : mSpanChange = 0.0f;
579 0 : mPreviousSpan = 0.0f;
580 0 : mFocusChange = 0.0f;
581 0 : } else if (mState == GESTURE_MULTI_TOUCH_DOWN) {
582 0 : mPreviousSpan = GetCurrentSpan(mLastTouchInput);
583 0 : mPreviousFocus = GetCurrentFocus(mLastTouchInput);
584 : }
585 0 : }
586 :
587 0 : void GestureEventListener::CancelLongTapTimeoutTask()
588 : {
589 0 : if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
590 : // being in this state means the task has been canceled already
591 0 : return;
592 : }
593 :
594 0 : if (mLongTapTimeoutTask) {
595 0 : mLongTapTimeoutTask->Cancel();
596 0 : mLongTapTimeoutTask = nullptr;
597 : }
598 : }
599 :
600 0 : void GestureEventListener::CreateLongTapTimeoutTask()
601 : {
602 0 : RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod(
603 : "layers::GestureEventListener::HandleInputTimeoutLongTap",
604 : this,
605 0 : &GestureEventListener::HandleInputTimeoutLongTap);
606 :
607 0 : mLongTapTimeoutTask = task;
608 0 : mAsyncPanZoomController->PostDelayedTask(
609 0 : task.forget(),
610 0 : gfxPrefs::UiClickHoldContextMenusDelay());
611 0 : }
612 :
613 0 : void GestureEventListener::CancelMaxTapTimeoutTask()
614 : {
615 0 : if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) {
616 : // being in this state means the timer has just been triggered
617 0 : return;
618 : }
619 :
620 0 : if (mMaxTapTimeoutTask) {
621 0 : mMaxTapTimeoutTask->Cancel();
622 0 : mMaxTapTimeoutTask = nullptr;
623 : }
624 : }
625 :
626 0 : void GestureEventListener::CreateMaxTapTimeoutTask()
627 : {
628 0 : mLastTapInput = mLastTouchInput;
629 :
630 0 : TouchBlockState* block = mAsyncPanZoomController->GetInputQueue()->GetCurrentTouchBlock();
631 0 : MOZ_ASSERT(block);
632 0 : RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<bool>(
633 : "layers::GestureEventListener::HandleInputTimeoutMaxTap",
634 : this,
635 : &GestureEventListener::HandleInputTimeoutMaxTap,
636 0 : block->IsDuringFastFling());
637 :
638 0 : mMaxTapTimeoutTask = task;
639 0 : mAsyncPanZoomController->PostDelayedTask(
640 0 : task.forget(),
641 0 : MAX_TAP_TIME);
642 0 : }
643 :
644 : } // namespace layers
645 : } // namespace mozilla
|