mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 985541 - Turn GestureEventListener into Finite-state machine. r=kats,drs
This commit is contained in:
parent
af6b166a2e
commit
5fb4778e0a
@ -594,6 +594,18 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: NS_WARNING("Unhandled input event"); break;
|
||||
}
|
||||
|
||||
mLastEventTime = aEvent.mTime;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::HandleGestureEvent(const InputData& aEvent)
|
||||
{
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
switch (aEvent.mInputType) {
|
||||
case PINCHGESTURE_INPUT: {
|
||||
const PinchGestureInput& pinchGestureInput = aEvent.AsPinchGestureInput();
|
||||
switch (pinchGestureInput.mType) {
|
||||
|
@ -255,6 +255,15 @@ public:
|
||||
*/
|
||||
nsEventStatus HandleInputEvent(const InputData& aEvent);
|
||||
|
||||
/**
|
||||
* Handler for gesture events.
|
||||
* Currently some gestures are detected in GestureEventListener that calls
|
||||
* APZC back through this handler in order to avoid recursive calls to
|
||||
* APZC::HandleInputEvent() which is supposed to do the work for
|
||||
* ReceiveInputEvent().
|
||||
*/
|
||||
nsEventStatus HandleGestureEvent(const InputData& aEvent);
|
||||
|
||||
/**
|
||||
* Populates the provided object (if non-null) with the scrollable guid of this apzc.
|
||||
*/
|
||||
|
@ -8,12 +8,9 @@
|
||||
#include <math.h> // for fabsf
|
||||
#include <stddef.h> // for size_t
|
||||
#include "AsyncPanZoomController.h" // for AsyncPanZoomController
|
||||
#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
|
||||
#include "base/task.h" // for CancelableTask, etc
|
||||
#include "gfxPrefs.h" // for gfxPrefs
|
||||
#include "mozilla/gfx/BasePoint.h" // for BasePoint
|
||||
#include "mozilla/mozalloc.h" // for operator new
|
||||
#include "nsDebug.h" // for NS_WARN_IF_FALSE
|
||||
#include "nsDebug.h" // for NS_WARNING
|
||||
#include "nsMathUtils.h" // for NS_hypot
|
||||
|
||||
namespace mozilla {
|
||||
@ -34,12 +31,26 @@ static const uint32_t MAX_TAP_TIME = 300;
|
||||
*/
|
||||
static const float PINCH_START_THRESHOLD = 35.0f;
|
||||
|
||||
ScreenPoint GetCurrentFocus(const MultiTouchInput& aEvent)
|
||||
{
|
||||
const ScreenIntPoint& firstTouch = aEvent.mTouches[0].mScreenPoint,
|
||||
secondTouch = aEvent.mTouches[1].mScreenPoint;
|
||||
return ScreenPoint(firstTouch + secondTouch) / 2;
|
||||
}
|
||||
|
||||
float GetCurrentSpan(const MultiTouchInput& aEvent)
|
||||
{
|
||||
const ScreenIntPoint& firstTouch = aEvent.mTouches[0].mScreenPoint,
|
||||
secondTouch = aEvent.mTouches[1].mScreenPoint;
|
||||
ScreenIntPoint delta = secondTouch - firstTouch;
|
||||
return float(NS_hypot(delta.x, delta.y));
|
||||
}
|
||||
|
||||
GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: mAsyncPanZoomController(aAsyncPanZoomController),
|
||||
mState(GESTURE_NONE),
|
||||
mSpanChange(0.0f),
|
||||
mTapStartTime(0),
|
||||
mLastTapEndTime(0),
|
||||
mPreviousSpan(0.0f),
|
||||
mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0, 0)
|
||||
{
|
||||
}
|
||||
@ -50,352 +61,401 @@ GestureEventListener::~GestureEventListener()
|
||||
|
||||
nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
// Cache the current event since it may become the single or long tap that we
|
||||
// send.
|
||||
mLastTouchInput = aEvent;
|
||||
|
||||
switch (aEvent.mType)
|
||||
{
|
||||
switch (aEvent.mType) {
|
||||
case MultiTouchInput::MULTITOUCH_START:
|
||||
case MultiTouchInput::MULTITOUCH_ENTER: {
|
||||
case MultiTouchInput::MULTITOUCH_ENTER:
|
||||
mTouches.Clear();
|
||||
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
|
||||
mTouches.AppendElement(aEvent.mTouches[i]);
|
||||
}
|
||||
|
||||
if (aEvent.mTouches.Length() == 1) {
|
||||
rv = HandleInputTouchSingleStart();
|
||||
} else {
|
||||
rv = HandleInputTouchMultiStart();
|
||||
}
|
||||
break;
|
||||
case MultiTouchInput::MULTITOUCH_MOVE:
|
||||
rv = HandleInputTouchMove();
|
||||
break;
|
||||
case MultiTouchInput::MULTITOUCH_END:
|
||||
case MultiTouchInput::MULTITOUCH_LEAVE:
|
||||
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
|
||||
bool foundAlreadyExistingTouch = false;
|
||||
for (size_t j = 0; j < mTouches.Length(); j++) {
|
||||
if (mTouches[j].mIdentifier == aEvent.mTouches[i].mIdentifier) {
|
||||
foundAlreadyExistingTouch = true;
|
||||
if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
|
||||
mTouches.RemoveElementAt(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a touch in our list that matches this, then add it.
|
||||
if (!foundAlreadyExistingTouch) {
|
||||
mTouches.AppendElement(aEvent.mTouches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t length = mTouches.Length();
|
||||
if (length == 1) {
|
||||
mTapStartTime = aEvent.mTime;
|
||||
mTouchStartPosition = aEvent.mTouches[0].mScreenPoint;
|
||||
if (mState == GESTURE_NONE) {
|
||||
mState = GESTURE_WAITING_SINGLE_TAP;
|
||||
|
||||
mLongTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap);
|
||||
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mLongTapTimeoutTask,
|
||||
gfxPrefs::UiClickHoldContextMenusDelay());
|
||||
}
|
||||
} else if (length == 2) {
|
||||
// Another finger has been added; it can't be a tap anymore.
|
||||
HandleTapCancel(aEvent);
|
||||
}
|
||||
|
||||
rv = HandleInputTouchEnd();
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_MOVE: {
|
||||
// If we move too much, bail out of the tap.
|
||||
ScreenIntPoint delta = aEvent.mTouches[0].mScreenPoint - mTouchStartPosition;
|
||||
if (mTouches.Length() == 1 &&
|
||||
NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance())
|
||||
{
|
||||
HandleTapCancel(aEvent);
|
||||
}
|
||||
|
||||
size_t eventTouchesMatched = 0;
|
||||
for (size_t i = 0; i < mTouches.Length(); i++) {
|
||||
bool isTouchRemoved = true;
|
||||
for (size_t j = 0; j < aEvent.mTouches.Length(); j++) {
|
||||
if (mTouches[i].mIdentifier == aEvent.mTouches[j].mIdentifier) {
|
||||
eventTouchesMatched++;
|
||||
isTouchRemoved = false;
|
||||
mTouches[i] = aEvent.mTouches[j];
|
||||
}
|
||||
}
|
||||
if (isTouchRemoved) {
|
||||
// this touch point was lifted, so remove it from our list
|
||||
mTouches.RemoveElementAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARN_IF_FALSE(eventTouchesMatched == aEvent.mTouches.Length(), "Touch moved, but not in list");
|
||||
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_END:
|
||||
case MultiTouchInput::MULTITOUCH_LEAVE: {
|
||||
for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
|
||||
bool foundAlreadyExistingTouch = false;
|
||||
for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
|
||||
if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
|
||||
foundAlreadyExistingTouch = true;
|
||||
mTouches.RemoveElementAt(j);
|
||||
}
|
||||
}
|
||||
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
|
||||
}
|
||||
|
||||
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
|
||||
CancelDoubleTapTimeoutTask();
|
||||
if (mTapStartTime - mLastTapEndTime > MAX_TAP_TIME ||
|
||||
aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
|
||||
// Either the time between taps or the last tap took too long
|
||||
// confirm previous tap and handle current tap seperately
|
||||
TimeoutDoubleTap();
|
||||
mState = GESTURE_WAITING_SINGLE_TAP;
|
||||
} else {
|
||||
// We were waiting for a double tap and it has arrived.
|
||||
HandleDoubleTap(aEvent);
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (mState == GESTURE_LONG_TAP_UP) {
|
||||
HandleLongTapUpEvent(aEvent);
|
||||
mState = GESTURE_NONE;
|
||||
} else if (mState == GESTURE_WAITING_SINGLE_TAP &&
|
||||
aEvent.mTime - mTapStartTime > MAX_TAP_TIME) {
|
||||
// Extended taps are immediately dispatched as single taps
|
||||
CancelLongTapTimeoutTask();
|
||||
HandleSingleTapConfirmedEvent(aEvent);
|
||||
mState = GESTURE_NONE;
|
||||
} else if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
CancelLongTapTimeoutTask();
|
||||
nsEventStatus tapupEvent = HandleSingleTapUpEvent(aEvent);
|
||||
|
||||
if (tapupEvent == nsEventStatus_eIgnore) {
|
||||
// We were not waiting for anything but a single tap has happened that
|
||||
// may turn into a double tap. Wait a while and if it doesn't turn into
|
||||
// a double tap, send a single tap instead.
|
||||
mState = GESTURE_WAITING_DOUBLE_TAP;
|
||||
|
||||
mDoubleTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);
|
||||
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mDoubleTapTimeoutTask,
|
||||
MAX_TAP_TIME);
|
||||
|
||||
} else if (tapupEvent == nsEventStatus_eConsumeNoDefault) {
|
||||
// We sent the tapup into content without waiting for a double tap
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
mLastTapEndTime = aEvent.mTime;
|
||||
|
||||
if (!mTouches.Length()) {
|
||||
mSpanChange = 0.0f;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MultiTouchInput::MULTITOUCH_CANCEL:
|
||||
// FIXME: we should probably clear a bunch of gesture state here
|
||||
break;
|
||||
}
|
||||
|
||||
return HandlePinchGestureEvent(aEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
if (aEvent.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
|
||||
mTouches.Clear();
|
||||
mState = GESTURE_NONE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mTouches.Length() > 1) {
|
||||
const ScreenIntPoint& firstTouch = mTouches[0].mScreenPoint,
|
||||
secondTouch = mTouches[1].mScreenPoint;
|
||||
ScreenPoint focusPoint = ScreenPoint(firstTouch + secondTouch) / 2;
|
||||
ScreenIntPoint delta = secondTouch - firstTouch;
|
||||
float currentSpan = float(NS_hypot(delta.x, delta.y));
|
||||
|
||||
switch (mState) {
|
||||
case GESTURE_NONE:
|
||||
mPreviousSpan = currentSpan;
|
||||
mState = GESTURE_WAITING_PINCH;
|
||||
// Deliberately fall through. If the user pinched and took their fingers
|
||||
// off the screen such that they still had 1 left on it, we want there to
|
||||
// be no resistance. We should only reset |mSpanChange| once all fingers
|
||||
// are off the screen.
|
||||
case GESTURE_WAITING_PINCH: {
|
||||
mSpanChange += fabsf(currentSpan - mPreviousSpan);
|
||||
if (mSpanChange > PINCH_START_THRESHOLD) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
|
||||
aEvent.mTime,
|
||||
focusPoint,
|
||||
currentSpan,
|
||||
currentSpan,
|
||||
aEvent.modifiers);
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = GESTURE_PINCH;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GESTURE_PINCH: {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
aEvent.mTime,
|
||||
focusPoint,
|
||||
currentSpan,
|
||||
mPreviousSpan,
|
||||
aEvent.modifiers);
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// What?
|
||||
break;
|
||||
}
|
||||
|
||||
mPreviousSpan = currentSpan;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mState == GESTURE_PINCH) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
|
||||
aEvent.mTime,
|
||||
ScreenPoint(),
|
||||
1.0f,
|
||||
1.0f,
|
||||
aEvent.modifiers);
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = GESTURE_NONE;
|
||||
|
||||
// If the user left a finger on the screen, spoof a touch start event and
|
||||
// send it to APZC so that they can continue panning from that point.
|
||||
if (mTouches.Length() == 1) {
|
||||
MultiTouchInput touchEvent(MultiTouchInput::MULTITOUCH_START,
|
||||
aEvent.mTime,
|
||||
aEvent.modifiers);
|
||||
touchEvent.mTouches.AppendElement(mTouches[0]);
|
||||
mAsyncPanZoomController->HandleInputEvent(touchEvent);
|
||||
|
||||
// The spoofed touch start will get back to GEL and make us enter the
|
||||
// GESTURE_WAITING_SINGLE_TAP state, but this isn't a new touch, so there
|
||||
// is no condition under which this touch should turn into any tap.
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mState == GESTURE_WAITING_PINCH) {
|
||||
mState = GESTURE_NONE;
|
||||
rv = HandleInputTouchCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleSingleTapUpEvent(const MultiTouchInput& aEvent)
|
||||
nsEventStatus GestureEventListener::HandleInputTouchSingleStart()
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
switch (mState) {
|
||||
case GESTURE_NONE:
|
||||
SetState(GESTURE_FIRST_SINGLE_TOUCH_DOWN);
|
||||
mTouchStartPosition = mLastTouchInput.mTouches[0].mScreenPoint;
|
||||
|
||||
nsEventStatus GestureEventListener::HandleSingleTapConfirmedEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleLongTapEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleLongTapUpEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG_UP, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEvent)
|
||||
{
|
||||
mTapStartTime = 0;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case GESTURE_WAITING_SINGLE_TAP:
|
||||
CancelLongTapTimeoutTask();
|
||||
mState = GESTURE_NONE;
|
||||
CreateLongTapTimeoutTask();
|
||||
CreateMaxTapTimeoutTask();
|
||||
break;
|
||||
|
||||
case GESTURE_WAITING_DOUBLE_TAP:
|
||||
case GESTURE_LONG_TAP_UP:
|
||||
mState = GESTURE_NONE;
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_UP:
|
||||
SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unhandled state upon single touch start");
|
||||
SetState(GESTURE_NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleDoubleTap(const MultiTouchInput& aEvent)
|
||||
nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_DOUBLE, aEvent.mTime,
|
||||
aEvent.mTouches[0].mScreenPoint, aEvent.modifiers);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
switch (mState) {
|
||||
case GESTURE_NONE:
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
break;
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
|
||||
CancelLongTapTimeoutTask();
|
||||
CancelMaxTapTimeoutTask();
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
|
||||
CancelLongTapTimeoutTask();
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_UP:
|
||||
// Cancel wait for double tap
|
||||
CancelMaxTapTimeoutTask();
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
|
||||
// Cancel wait for single tap
|
||||
CancelMaxTapTimeoutTask();
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
case GESTURE_LONG_TOUCH_DOWN:
|
||||
SetState(GESTURE_MULTI_TOUCH_DOWN);
|
||||
break;
|
||||
case GESTURE_MULTI_TOUCH_DOWN:
|
||||
case GESTURE_PINCH:
|
||||
// Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Unhandled state upon multitouch start");
|
||||
SetState(GESTURE_NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GestureEventListener::TimeoutDoubleTap()
|
||||
nsEventStatus GestureEventListener::HandleInputTouchMove()
|
||||
{
|
||||
mDoubleTapTimeoutTask = nullptr;
|
||||
// If we haven't gotten another tap by now, reset the state and treat it as a
|
||||
// single tap. It couldn't have been a double tap.
|
||||
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
|
||||
mState = GESTURE_NONE;
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
HandleSingleTapConfirmedEvent(mLastTouchInput);
|
||||
switch (mState) {
|
||||
case GESTURE_NONE:
|
||||
case GESTURE_LONG_TOUCH_DOWN:
|
||||
// Ignore this input signal as the corresponding events get handled by APZC
|
||||
break;
|
||||
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
|
||||
case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
|
||||
// If we move too much, bail out of the tap.
|
||||
ScreenIntPoint delta = mLastTouchInput.mTouches[0].mScreenPoint - mTouchStartPosition;
|
||||
if (NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance()) {
|
||||
CancelLongTapTimeoutTask();
|
||||
CancelMaxTapTimeoutTask();
|
||||
SetState(GESTURE_NONE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GESTURE_MULTI_TOUCH_DOWN: {
|
||||
if (mLastTouchInput.mTouches.Length() < 2) {
|
||||
NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state");
|
||||
break;
|
||||
}
|
||||
|
||||
float currentSpan = GetCurrentSpan(mLastTouchInput);
|
||||
|
||||
mSpanChange += fabsf(currentSpan - mPreviousSpan);
|
||||
if (mSpanChange > PINCH_START_THRESHOLD) {
|
||||
SetState(GESTURE_PINCH);
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
|
||||
mLastTouchInput.mTime,
|
||||
GetCurrentFocus(mLastTouchInput),
|
||||
currentSpan,
|
||||
currentSpan,
|
||||
mLastTouchInput.modifiers);
|
||||
|
||||
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
|
||||
}
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
mPreviousSpan = currentSpan;
|
||||
break;
|
||||
}
|
||||
|
||||
case GESTURE_PINCH: {
|
||||
if (mLastTouchInput.mTouches.Length() < 2) {
|
||||
NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state");
|
||||
// Prevent APZC::OnTouchMove() from handling this wrong input
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
}
|
||||
|
||||
float currentSpan = GetCurrentSpan(mLastTouchInput);
|
||||
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
mLastTouchInput.mTime,
|
||||
GetCurrentFocus(mLastTouchInput),
|
||||
currentSpan,
|
||||
mPreviousSpan,
|
||||
mLastTouchInput.modifiers);
|
||||
|
||||
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
mPreviousSpan = currentSpan;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_WARNING("Unhandled state upon touch move");
|
||||
SetState(GESTURE_NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GestureEventListener::CancelDoubleTapTimeoutTask() {
|
||||
if (mDoubleTapTimeoutTask) {
|
||||
mDoubleTapTimeoutTask->Cancel();
|
||||
mDoubleTapTimeoutTask = nullptr;
|
||||
nsEventStatus GestureEventListener::HandleInputTouchEnd()
|
||||
{
|
||||
nsEventStatus rv = nsEventStatus_eIgnore;
|
||||
|
||||
switch (mState) {
|
||||
case GESTURE_NONE:
|
||||
// GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
|
||||
break;
|
||||
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
|
||||
CancelLongTapTimeoutTask();
|
||||
CancelMaxTapTimeoutTask();
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP,
|
||||
mLastTouchInput.mTime,
|
||||
mLastTouchInput.mTouches[0].mScreenPoint,
|
||||
mLastTouchInput.modifiers);
|
||||
nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(tapEvent);
|
||||
if (tapupStatus == nsEventStatus_eIgnore) {
|
||||
SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
|
||||
CreateMaxTapTimeoutTask();
|
||||
} else {
|
||||
// We sent the tapup into content without waiting for a double tap
|
||||
SetState(GESTURE_NONE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
|
||||
CancelMaxTapTimeoutTask();
|
||||
SetState(GESTURE_NONE);
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_DOUBLE,
|
||||
mLastTouchInput.mTime,
|
||||
mLastTouchInput.mTouches[0].mScreenPoint,
|
||||
mLastTouchInput.modifiers);
|
||||
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
|
||||
CancelLongTapTimeoutTask();
|
||||
SetState(GESTURE_NONE);
|
||||
TriggerSingleTapConfirmedEvent();
|
||||
break;
|
||||
|
||||
case GESTURE_LONG_TOUCH_DOWN: {
|
||||
SetState(GESTURE_NONE);
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG_UP,
|
||||
mLastTouchInput.mTime,
|
||||
mLastTouchInput.mTouches[0].mScreenPoint,
|
||||
mLastTouchInput.modifiers);
|
||||
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
case GESTURE_MULTI_TOUCH_DOWN:
|
||||
if (mTouches.Length() < 2) {
|
||||
SetState(GESTURE_NONE);
|
||||
}
|
||||
break;
|
||||
|
||||
case GESTURE_PINCH:
|
||||
if (mTouches.Length() < 2) {
|
||||
SetState(GESTURE_NONE);
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
|
||||
mLastTouchInput.mTime,
|
||||
ScreenPoint(),
|
||||
1.0f,
|
||||
1.0f,
|
||||
mLastTouchInput.modifiers);
|
||||
mAsyncPanZoomController->HandleGestureEvent(pinchEvent);
|
||||
}
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_WARNING("Unhandled state upon touch end");
|
||||
SetState(GESTURE_NONE);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GestureEventListener::TimeoutLongTap()
|
||||
nsEventStatus GestureEventListener::HandleInputTouchCancel()
|
||||
{
|
||||
SetState(GESTURE_NONE);
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
void GestureEventListener::HandleInputTimeoutLongTap()
|
||||
{
|
||||
mLongTapTimeoutTask = nullptr;
|
||||
// If the tap has not been released, this is a long press.
|
||||
if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
mState = GESTURE_LONG_TAP_UP;
|
||||
|
||||
HandleLongTapEvent(mLastTouchInput);
|
||||
switch (mState) {
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_DOWN:
|
||||
// just in case MAX_TAP_TIME > ContextMenuDelay cancel MAX_TAP timer
|
||||
// and fall through
|
||||
CancelMaxTapTimeoutTask();
|
||||
case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: {
|
||||
SetState(GESTURE_LONG_TOUCH_DOWN);
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG,
|
||||
mLastTouchInput.mTime,
|
||||
mLastTouchInput.mTouches[0].mScreenPoint,
|
||||
mLastTouchInput.modifiers);
|
||||
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_WARNING("Unhandled state upon long tap timeout");
|
||||
SetState(GESTURE_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GestureEventListener::CancelLongTapTimeoutTask() {
|
||||
void GestureEventListener::HandleInputTimeoutMaxTap()
|
||||
{
|
||||
mMaxTapTimeoutTask = nullptr;
|
||||
|
||||
if (mState == GESTURE_FIRST_SINGLE_TOUCH_DOWN) {
|
||||
SetState(GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN);
|
||||
} else if (mState == GESTURE_FIRST_SINGLE_TOUCH_UP ||
|
||||
mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
|
||||
SetState(GESTURE_NONE);
|
||||
TriggerSingleTapConfirmedEvent();
|
||||
} else {
|
||||
NS_WARNING("Unhandled state upon MAX_TAP timeout");
|
||||
SetState(GESTURE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void GestureEventListener::TriggerSingleTapConfirmedEvent()
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED,
|
||||
mLastTouchInput.mTime,
|
||||
mLastTouchInput.mTouches[0].mScreenPoint,
|
||||
mLastTouchInput.modifiers);
|
||||
mAsyncPanZoomController->HandleGestureEvent(tapEvent);
|
||||
}
|
||||
|
||||
void GestureEventListener::SetState(GestureState aState)
|
||||
{
|
||||
mState = aState;
|
||||
|
||||
if (mState == GESTURE_NONE) {
|
||||
mSpanChange = 0.0f;
|
||||
mPreviousSpan = 0.0f;
|
||||
} else if (mState == GESTURE_MULTI_TOUCH_DOWN) {
|
||||
mPreviousSpan = GetCurrentSpan(mLastTouchInput);
|
||||
}
|
||||
}
|
||||
|
||||
void GestureEventListener::CancelLongTapTimeoutTask()
|
||||
{
|
||||
if (mState == GESTURE_SECOND_SINGLE_TOUCH_DOWN) {
|
||||
// being in this state means the task has been canceled already
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLongTapTimeoutTask) {
|
||||
mLongTapTimeoutTask->Cancel();
|
||||
mLongTapTimeoutTask = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncPanZoomController* GestureEventListener::GetAsyncPanZoomController() {
|
||||
return mAsyncPanZoomController;
|
||||
void GestureEventListener::CreateLongTapTimeoutTask()
|
||||
{
|
||||
mLongTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::HandleInputTimeoutLongTap);
|
||||
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mLongTapTimeoutTask,
|
||||
gfxPrefs::UiClickHoldContextMenusDelay());
|
||||
}
|
||||
|
||||
void GestureEventListener::CancelGesture() {
|
||||
mTouches.Clear();
|
||||
mState = GESTURE_NONE;
|
||||
void GestureEventListener::CancelMaxTapTimeoutTask()
|
||||
{
|
||||
if (mState == GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN) {
|
||||
// being in this state means the timer has just been triggered
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMaxTapTimeoutTask) {
|
||||
mMaxTapTimeoutTask->Cancel();
|
||||
mMaxTapTimeoutTask = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GestureEventListener::CreateMaxTapTimeoutTask()
|
||||
{
|
||||
mMaxTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::HandleInputTimeoutMaxTap);
|
||||
|
||||
mAsyncPanZoomController->PostDelayedTask(
|
||||
mMaxTapTimeoutTask,
|
||||
MAX_TAP_TIME);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,10 +7,8 @@
|
||||
#ifndef mozilla_layers_GestureEventListener_h
|
||||
#define mozilla_layers_GestureEventListener_h
|
||||
|
||||
#include <stdint.h> // for uint64_t
|
||||
#include "InputData.h" // for MultiTouchInput, etc
|
||||
#include "Units.h" // for ScreenIntPoint
|
||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
|
||||
#include "mozilla/EventForwards.h" // for nsEventStatus
|
||||
#include "nsAutoPtr.h" // for nsRefPtr
|
||||
#include "nsISupportsImpl.h"
|
||||
@ -56,118 +54,95 @@ public:
|
||||
*/
|
||||
nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Cancels any currently active gesture. May not properly handle situations
|
||||
* that require extra work at the gesture's end, like a pinch which only
|
||||
* requests a repaint once it has ended.
|
||||
*/
|
||||
void CancelGesture();
|
||||
|
||||
/**
|
||||
* Returns the AsyncPanZoomController stored on this class and used for
|
||||
* callbacks.
|
||||
*/
|
||||
AsyncPanZoomController* GetAsyncPanZoomController();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* States of GEL finite-state machine.
|
||||
*/
|
||||
enum GestureState {
|
||||
// There's no gesture going on, and we don't think we're about to enter one.
|
||||
// This is the initial and final state of any gesture.
|
||||
// In this state there's no gesture going on, and we don't think we're
|
||||
// about to enter one.
|
||||
// Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_MULTI_TOUCH_DOWN.
|
||||
GESTURE_NONE,
|
||||
|
||||
// A touch start with a single touch point has just happened.
|
||||
// After having gotten into this state we start timers for MAX_TAP_TIME and
|
||||
// gfxPrefs::UiClickHoldContextMenusDelay().
|
||||
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
|
||||
// GESTURE_FIRST_SINGLE_TOUCH_UP, GESTURE_LONG_TOUCH_DOWN,
|
||||
// GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN.
|
||||
GESTURE_FIRST_SINGLE_TOUCH_DOWN,
|
||||
|
||||
// While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got
|
||||
// triggered. Now we'll trigger either a single tap if a user lifts her
|
||||
// finger or a long tap if gfxPrefs::UiClickHoldContextMenusDelay() happens
|
||||
// first.
|
||||
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE,
|
||||
// GESTURE_LONG_TOUCH_DOWN.
|
||||
GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN,
|
||||
|
||||
// A user put her finger down and lifted it up quickly enough.
|
||||
// After having gotten into this state we clear the timer for MAX_TAP_TIME.
|
||||
// Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE,
|
||||
// GESTURE_MULTI_TOUCH_DOWN.
|
||||
GESTURE_FIRST_SINGLE_TOUCH_UP,
|
||||
|
||||
// A user put down her finger again right after a single tap thus the
|
||||
// gesture can't be a single tap, but rather a double tap. But we're
|
||||
// still not sure about that until the user lifts her finger again.
|
||||
// Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE.
|
||||
GESTURE_SECOND_SINGLE_TOUCH_DOWN,
|
||||
|
||||
// A long touch has happened, but the user still keeps her finger down.
|
||||
// We'll trigger a "long tap up" event when the finger is up.
|
||||
// Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN.
|
||||
GESTURE_LONG_TOUCH_DOWN,
|
||||
|
||||
// We have detected that two or more fingers are on the screen, but there
|
||||
// hasn't been enough movement yet to make us start actually zooming the
|
||||
// screen.
|
||||
GESTURE_WAITING_PINCH,
|
||||
// Allowed next states: GESTURE_PINCH, GESTURE_NONE
|
||||
GESTURE_MULTI_TOUCH_DOWN,
|
||||
|
||||
// There are two or more fingers on the screen, and the user has already
|
||||
// pinched enough for us to start zooming the screen.
|
||||
GESTURE_PINCH,
|
||||
// A touch start has happened and it may turn into a tap. We use this
|
||||
// because, if we put down two fingers and then lift them very quickly, this
|
||||
// may be mistaken for a tap.
|
||||
GESTURE_WAITING_SINGLE_TAP,
|
||||
// A single tap has happened for sure, and we're waiting for a second tap.
|
||||
GESTURE_WAITING_DOUBLE_TAP,
|
||||
// A long tap has happened, wait for the tap to be released in case we need
|
||||
// to fire a click event in the case the long tap was not handled.
|
||||
GESTURE_LONG_TAP_UP
|
||||
// Allowed next states: GESTURE_NONE
|
||||
GESTURE_PINCH
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to handle the event as a pinch event. If it is not a pinch event,
|
||||
* then we simply tell the next consumer to consume the event instead.
|
||||
* These HandleInput* functions comprise input alphabet of the GEL
|
||||
* finite-state machine triggering state transitions.
|
||||
*/
|
||||
nsEventStatus HandlePinchGestureEvent(const MultiTouchInput& aEvent);
|
||||
nsEventStatus HandleInputTouchSingleStart();
|
||||
nsEventStatus HandleInputTouchMultiStart();
|
||||
nsEventStatus HandleInputTouchEnd();
|
||||
nsEventStatus HandleInputTouchMove();
|
||||
nsEventStatus HandleInputTouchCancel();
|
||||
void HandleInputTimeoutLongTap();
|
||||
void HandleInputTimeoutMaxTap();
|
||||
|
||||
void TriggerSingleTapConfirmedEvent();
|
||||
|
||||
/**
|
||||
* Attempts to handle the event as a single tap event, which highlights links
|
||||
* before opening them. In general, this will not attempt to block the touch
|
||||
* event from being passed along to AsyncPanZoomController since APZC needs to
|
||||
* know about touches ending (and we only know if a touch was a tap once it
|
||||
* ends).
|
||||
* Do actual state transition and reset substates.
|
||||
*/
|
||||
nsEventStatus HandleSingleTapUpEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a single tap confirmation. This is what will actually
|
||||
* open links, etc. In general, this will not attempt to block the touch event
|
||||
* from being passed along to AsyncPanZoomController since APZC needs to know
|
||||
* about touches ending (and we only know if a touch was a tap once it ends).
|
||||
*/
|
||||
nsEventStatus HandleSingleTapConfirmedEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a long tap confirmation. This is what will use
|
||||
* for context menu.
|
||||
*/
|
||||
nsEventStatus HandleLongTapEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle release of long tap. This is used to fire click
|
||||
* events in the case the context menu was not invoked.
|
||||
*/
|
||||
nsEventStatus HandleLongTapUpEvent(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a tap event cancellation. This happens when we think
|
||||
* something was a tap but it actually wasn't. In general, this will not
|
||||
* attempt to block the touch event from being passed along to
|
||||
* AsyncPanZoomController since APZC needs to know about touches ending (and
|
||||
* we only know if a touch was a tap once it ends).
|
||||
*/
|
||||
nsEventStatus HandleTapCancel(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a double tap. This happens when we get two single taps
|
||||
* within a short time. In general, this will not attempt to block the touch
|
||||
* event from being passed along to AsyncPanZoomController since APZC needs to
|
||||
* know about touches ending (and we only know if a touch was a double tap
|
||||
* once it ends).
|
||||
*/
|
||||
nsEventStatus HandleDoubleTap(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Times out a single tap we think may be turned into a double tap. This will
|
||||
* also send a single tap if we're still in the "GESTURE_WAITING_DOUBLE_TAP"
|
||||
* state when this is called. This should be called a short time after a
|
||||
* single tap is detected, and the delay on it should be enough that the user
|
||||
* has time to tap again (to make a double tap).
|
||||
*/
|
||||
void TimeoutDoubleTap();
|
||||
/**
|
||||
* Times out a long tap. This should be called a 'long' time after a single
|
||||
* tap is detected.
|
||||
*/
|
||||
void TimeoutLongTap();
|
||||
void SetState(GestureState aState);
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
|
||||
|
||||
/**
|
||||
* Array containing all active touches. When a touch happens it, gets added to
|
||||
* this array, even if we choose not to handle it. When it ends, we remove it.
|
||||
* We need to maintain this array in order to detect the end of the
|
||||
* "multitouch" states because touch start events contain all current touches,
|
||||
* but touch end events contain only those touches that have gone.
|
||||
*/
|
||||
nsTArray<SingleTouchData> mTouches;
|
||||
|
||||
/**
|
||||
* Current gesture we're dealing with.
|
||||
* Current state we're dealing with.
|
||||
*/
|
||||
GestureState mState;
|
||||
|
||||
@ -186,56 +161,49 @@ protected:
|
||||
float mPreviousSpan;
|
||||
|
||||
/**
|
||||
* Stores the time a touch started, used for detecting a tap gesture. Only
|
||||
* valid when there's exactly one touch in mTouches. This is the time that the
|
||||
* first touch was inserted into the array. This is a uint64_t because it is
|
||||
* initialized from interactions with InputData, which stores its timestamps as
|
||||
* a uint64_t.
|
||||
*/
|
||||
uint64_t mTapStartTime;
|
||||
|
||||
/**
|
||||
* Stores the time the last tap ends (finger leaves the screen). This is used
|
||||
* when mDoubleTapTimeoutTask cannot be scheduled in time and consecutive
|
||||
* taps are falsely regarded as double taps.
|
||||
*/
|
||||
uint64_t mLastTapEndTime;
|
||||
|
||||
/**
|
||||
* Cached copy of the last touch input, only valid when in the
|
||||
* "GESTURE_WAITING_DOUBLE_TAP" state. This is used to forward along to
|
||||
* AsyncPanZoomController if a single tap needs to be sent (since it is sent
|
||||
* shortly after the user actually taps, since we need to wait for a double
|
||||
* tap).
|
||||
* Cached copy of the last touch input.
|
||||
*/
|
||||
MultiTouchInput mLastTouchInput;
|
||||
|
||||
/**
|
||||
* Task used to timeout a double tap. This gets posted to the UI thread such
|
||||
* that it runs a short time after a single tap happens. We cache it so that
|
||||
* we can cancel it if a double tap actually comes in.
|
||||
* CancelDoubleTapTimeoutTask: Cancel the mDoubleTapTimeoutTask and also set
|
||||
* it to null.
|
||||
* Position of the last touch starting. This is only valid during an attempt
|
||||
* to determine if a touch is a tap. If a touch point moves away from
|
||||
* mTouchStartPosition to the distance greater than
|
||||
* AsyncPanZoomController::GetTouchStartTolerance() while in
|
||||
* GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN
|
||||
* or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is
|
||||
* not tap.
|
||||
*/
|
||||
CancelableTask *mDoubleTapTimeoutTask;
|
||||
inline void CancelDoubleTapTimeoutTask();
|
||||
ScreenIntPoint mTouchStartPosition;
|
||||
|
||||
/**
|
||||
* Task used to timeout a long tap. This gets posted to the UI thread such
|
||||
* that it runs a time when a single tap happens. We cache it so that
|
||||
* we can cancel it if any other touch event happens.
|
||||
*
|
||||
* The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN
|
||||
* and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states.
|
||||
*
|
||||
* CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set
|
||||
* it to null.
|
||||
*/
|
||||
CancelableTask *mLongTapTimeoutTask;
|
||||
inline void CancelLongTapTimeoutTask();
|
||||
void CancelLongTapTimeoutTask();
|
||||
void CreateLongTapTimeoutTask();
|
||||
|
||||
/**
|
||||
* Position of the last touch starting. This is only valid during an attempt
|
||||
* to determine if a touch is a tap. This means that it is used in both the
|
||||
* "GESTURE_WAITING_SINGLE_TAP" and "GESTURE_WAITING_DOUBLE_TAP" states.
|
||||
* Task used to timeout a single tap or a double tap.
|
||||
*
|
||||
* The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN,
|
||||
* GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
|
||||
*
|
||||
* CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
|
||||
* it to null.
|
||||
*/
|
||||
ScreenIntPoint mTouchStartPosition;
|
||||
CancelableTask *mMaxTapTimeoutTask;
|
||||
void CancelMaxTapTimeoutTask();
|
||||
void CreateMaxTapTimeoutTask();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -65,39 +65,38 @@ public:
|
||||
class MockContentControllerDelayed : public MockContentController {
|
||||
public:
|
||||
MockContentControllerDelayed()
|
||||
: mCurrentTask(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void PostDelayedTask(Task* aTask, int aDelayMs) {
|
||||
// Ensure we're not clobbering an existing task
|
||||
EXPECT_TRUE(nullptr == mCurrentTask);
|
||||
mCurrentTask = aTask;
|
||||
mTaskQueue.AppendElement(aTask);
|
||||
}
|
||||
|
||||
void CheckHasDelayedTask() {
|
||||
EXPECT_TRUE(nullptr != mCurrentTask);
|
||||
EXPECT_TRUE(mTaskQueue.Length() > 0);
|
||||
}
|
||||
|
||||
void ClearDelayedTask() {
|
||||
mCurrentTask = nullptr;
|
||||
mTaskQueue.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
void DestroyOldestTask() {
|
||||
delete mTaskQueue[0];
|
||||
mTaskQueue.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
// Note that deleting mCurrentTask is important in order to
|
||||
// release the reference to the callee object. Without this
|
||||
// that object might be leaked. This is also why we don't
|
||||
// expose mCurrentTask to any users of MockContentControllerDelayed.
|
||||
// expose mTaskQueue to any users of MockContentControllerDelayed.
|
||||
void RunDelayedTask() {
|
||||
// Running mCurrentTask may call PostDelayedTask, so we should
|
||||
// keep a local copy of mCurrentTask and operate on that
|
||||
Task* local = mCurrentTask;
|
||||
mCurrentTask = nullptr;
|
||||
local->Run();
|
||||
delete local;
|
||||
mTaskQueue[0]->Run();
|
||||
delete mTaskQueue[0];
|
||||
mTaskQueue.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
private:
|
||||
Task *mCurrentTask;
|
||||
nsTArray<Task*> mTaskQueue;
|
||||
};
|
||||
|
||||
|
||||
@ -284,19 +283,19 @@ void DoPanTest(bool aShouldTriggerScroll, bool aShouldUseTouchAction, uint32_t a
|
||||
|
||||
static void
|
||||
ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale) {
|
||||
aApzc->HandleInputEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
|
||||
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
|
||||
0,
|
||||
ScreenPoint(aFocusX, aFocusY),
|
||||
10.0,
|
||||
10.0,
|
||||
0));
|
||||
aApzc->HandleInputEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
0,
|
||||
ScreenPoint(aFocusX, aFocusY),
|
||||
10.0 * aScale,
|
||||
10.0,
|
||||
0));
|
||||
aApzc->HandleInputEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
|
||||
aApzc->HandleGestureEvent(PinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
|
||||
0,
|
||||
ScreenPoint(aFocusX, aFocusY),
|
||||
// note: negative values here tell APZC
|
||||
@ -324,8 +323,10 @@ static nsEventStatus
|
||||
ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength, MockContentControllerDelayed* mcc = nullptr) {
|
||||
nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
|
||||
if (mcc != nullptr) {
|
||||
// There will be a delayed task posted for the long-tap timeout, but
|
||||
// if we were provided a non-null mcc we want to clear it.
|
||||
// There will be delayed tasks posted for the long-tap and MAX_TAP timeouts, but
|
||||
// if we were provided a non-null mcc we want to clear them.
|
||||
mcc->CheckHasDelayedTask();
|
||||
mcc->ClearDelayedTask();
|
||||
mcc->CheckHasDelayedTask();
|
||||
mcc->ClearDelayedTask();
|
||||
}
|
||||
@ -776,6 +777,16 @@ DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
|
||||
mcc->RunDelayedTask();
|
||||
check.Call("postHandleLongTap");
|
||||
|
||||
// Destroy pending MAX_TAP timeout task
|
||||
mcc->DestroyOldestTask();
|
||||
// There should be a TimeoutContentResponse task in the queue still
|
||||
// Clear the waiting-for-content timeout task, then send the signal that
|
||||
// content has handled this long tap. This takes the place of the
|
||||
// "contextmenu" event.
|
||||
mcc->CheckHasDelayedTask();
|
||||
mcc->ClearDelayedTask();
|
||||
apzc->ContentReceivedTouch(true);
|
||||
|
||||
time += 1000;
|
||||
|
||||
status = ApzcUp(apzc, 10, 10, time);
|
||||
@ -835,6 +846,8 @@ TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
|
||||
mcc->RunDelayedTask();
|
||||
check.Call("postHandleLongTap");
|
||||
|
||||
// Destroy pending MAX_TAP timeout task
|
||||
mcc->DestroyOldestTask();
|
||||
// Clear the waiting-for-content timeout task, then send the signal that
|
||||
// content has handled this long tap. This takes the place of the
|
||||
// "contextmenu" event.
|
||||
|
Loading…
Reference in New Issue
Block a user