--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1775,22 +1775,30 @@ TabChild::RecvHandleTap(const GeckoConte
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
switch (aType) {
case GeckoContentController::TapType::eSingleTap:
if (mRemoteFrame) {
mRemoteFrame->SendTakeFocusForClickFromTap();
}
if (mGlobal && mTabChildGlobal) {
- mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid);
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
}
break;
case GeckoContentController::TapType::eDoubleTap:
HandleDoubleTap(point, aModifiers, aGuid);
break;
+ case GeckoContentController::TapType::eSecondTap:
+ if (mRemoteFrame) {
+ mRemoteFrame->SendTakeFocusForClickFromTap();
+ }
+ if (mGlobal && mTabChildGlobal) {
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
+ }
+ break;
case GeckoContentController::TapType::eLongTap:
if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
aInputBlockId);
}
break;
case GeckoContentController::TapType::eLongTapUp:
if (mGlobal && mTabChildGlobal) {
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -38,20 +38,26 @@ public:
/**
* Different types of tap-related events that can be sent in
* the HandleTap function. The names should be relatively self-explanatory.
* Note that the eLongTapUp will always be preceded by an eLongTap, but not
* all eLongTap notifications will be followed by an eLongTapUp (for instance,
* if the user moves their finger after triggering the long-tap but before
* lifting it).
+ * The difference between eDoubleTap and eSecondTap is subtle - the eDoubleTap
+ * is for an actual double-tap "gesture" while eSecondTap is for the same user
+ * input but where a double-tap gesture is not allowed. This is used to fire
+ * a click event with detail=2 to web content (similar to what a mouse double-
+ * click would do).
*/
enum class TapType {
eSingleTap,
eDoubleTap,
+ eSecondTap,
eLongTap,
eLongTapUp,
eSentinel,
};
/**
* Requests handling of a tap event. |aPoint| is in LD pixels, relative to the
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1054,16 +1054,17 @@ nsEventStatus AsyncPanZoomController::Ha
case TAPGESTURE_INPUT: {
const TapGestureInput& tapGestureInput = aEvent.AsTapGestureInput();
switch (tapGestureInput.mType) {
case TapGestureInput::TAPGESTURE_LONG: rv = OnLongPress(tapGestureInput); break;
case TapGestureInput::TAPGESTURE_LONG_UP: rv = OnLongPressUp(tapGestureInput); break;
case TapGestureInput::TAPGESTURE_UP: rv = OnSingleTapUp(tapGestureInput); break;
case TapGestureInput::TAPGESTURE_CONFIRMED: rv = OnSingleTapConfirmed(tapGestureInput); break;
case TapGestureInput::TAPGESTURE_DOUBLE: rv = OnDoubleTap(tapGestureInput); break;
+ case TapGestureInput::TAPGESTURE_SECOND: rv = OnSecondTap(tapGestureInput); break;
case TapGestureInput::TAPGESTURE_CANCEL: rv = OnCancelTap(tapGestureInput); break;
default: NS_WARNING("Unhandled tap gesture"); break;
}
break;
}
default: NS_WARNING("Unhandled input event"); break;
}
@@ -2114,16 +2115,22 @@ nsEventStatus AsyncPanZoomController::On
aEvent.modifiers, GetGuid(), GetCurrentTouchBlock()->GetBlockId());
}
}
return nsEventStatus_eConsumeNoDefault;
}
return nsEventStatus_eIgnore;
}
+nsEventStatus AsyncPanZoomController::OnSecondTap(const TapGestureInput& aEvent)
+{
+ APZC_LOG("%p got a second-tap in state %d\n", this, mState);
+ return GenerateSingleTap(TapType::eSecondTap, aEvent.mPoint, aEvent.modifiers);
+}
+
nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
APZC_LOG("%p got a cancel-tap in state %d\n", this, mState);
// XXX: Implement this.
return nsEventStatus_eIgnore;
}
ScreenToParentLayerMatrix4x4 AsyncPanZoomController::GetTransformToThis() const {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -474,16 +474,21 @@ protected:
nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
/**
* Helper method for double taps.
*/
nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);
/**
+ * Helper method for double taps where the double-tap gesture is disabled.
+ */
+ nsEventStatus OnSecondTap(const TapGestureInput& aEvent);
+
+ /**
* Helper method to cancel any gesture currently going to Gecko. Used
* primarily when a user taps the screen over some clickable content but then
* pans down instead of letting go (i.e. to cancel a previous touch so that a
* new one can properly take effect.
*/
nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
/**
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -191,28 +191,25 @@ nsEventStatus GestureEventListener::Hand
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:
+ case GESTURE_SECOND_SINGLE_TOUCH_DOWN:
// Cancel wait for double tap
CancelMaxTapTimeoutTask();
+ MOZ_ASSERT(mSingleTapSent);
+ if (!mSingleTapSent.value()) {
+ TriggerSingleTapConfirmedEvent();
+ }
+ mSingleTapSent = Nothing();
SetState(GESTURE_MULTI_TOUCH_DOWN);
- TriggerSingleTapConfirmedEvent();
- // 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);
- TriggerSingleTapConfirmedEvent();
// 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:
@@ -255,16 +252,17 @@ nsEventStatus GestureEventListener::Hand
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.
if (MoveDistanceIsLarge()) {
CancelLongTapTimeoutTask();
CancelMaxTapTimeoutTask();
+ mSingleTapSent = Nothing();
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");
@@ -341,31 +339,31 @@ nsEventStatus GestureEventListener::Hand
// GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore.
break;
case GESTURE_FIRST_SINGLE_TOUCH_DOWN: {
CancelLongTapTimeoutTask();
CancelMaxTapTimeoutTask();
nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(
CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP));
- 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);
- }
+ mSingleTapSent = Some(tapupStatus != nsEventStatus_eIgnore);
+ SetState(GESTURE_FIRST_SINGLE_TOUCH_UP);
+ CreateMaxTapTimeoutTask();
break;
}
case GESTURE_SECOND_SINGLE_TOUCH_DOWN: {
CancelMaxTapTimeoutTask();
- SetState(GESTURE_NONE);
+ MOZ_ASSERT(mSingleTapSent);
mAsyncPanZoomController->HandleGestureEvent(
- CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_DOUBLE));
+ CreateTapEvent(mLastTouchInput,
+ mSingleTapSent.value() ? TapGestureInput::TAPGESTURE_SECOND
+ : TapGestureInput::TAPGESTURE_DOUBLE));
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
break;
}
case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN:
CancelLongTapTimeoutTask();
SetState(GESTURE_NONE);
TriggerSingleTapConfirmedEvent();
break;
@@ -412,16 +410,17 @@ nsEventStatus GestureEventListener::Hand
break;
}
return rv;
}
nsEventStatus GestureEventListener::HandleInputTouchCancel()
{
+ mSingleTapSent = Nothing();
SetState(GESTURE_NONE);
CancelMaxTapTimeoutTask();
CancelLongTapTimeoutTask();
return nsEventStatus_eIgnore;
}
void GestureEventListener::HandleInputTimeoutLongTap()
{
@@ -453,20 +452,22 @@ void GestureEventListener::HandleInputTi
GEL_LOG("Running max-tap timeout task in state %d\n", mState);
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);
- if (!aDuringFastFling) {
+ MOZ_ASSERT(mSingleTapSent);
+ if (!aDuringFastFling && !mSingleTapSent.value()) {
TriggerSingleTapConfirmedEvent();
}
+ mSingleTapSent = Nothing();
+ SetState(GESTURE_NONE);
} else {
NS_WARNING("Unhandled state upon MAX_TAP timeout");
SetState(GESTURE_NONE);
}
}
void GestureEventListener::TriggerSingleTapConfirmedEvent()
{
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -226,14 +226,23 @@ private:
* GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states.
*
* CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set
* it to null.
*/
RefPtr<CancelableRunnable> mMaxTapTimeoutTask;
void CancelMaxTapTimeoutTask();
void CreateMaxTapTimeoutTask();
+
+ /**
+ * Tracks whether the single-tap event was already sent to content. This is
+ * needed because it affects how the double-tap gesture, if detected, is
+ * handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP and
+ * GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is
+ * stored in a Maybe which is set to Nothing() at all other times.
+ */
+ Maybe<bool> mSingleTapSent;
};
} // namespace layers
} // namespace mozilla
#endif
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -494,17 +494,18 @@ TEST_F(APZCGestureDetectorTester, Double
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
MakeApzcWaitForMainThread();
MakeApzcUnzoomable();
- EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(2);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+ EXPECT_CALL(*mcc, HandleTap(TapType::eSecondTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
uint64_t blockIds[2];
DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedInputBlock(blockIds[0], false);
apzc->ContentReceivedInputBlock(blockIds[1], false);
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -473,30 +473,31 @@ APZCCallbackHelper::DispatchWidgetEvent(
return status;
}
nsEventStatus
APZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessage aMsg,
uint64_t aTime,
const LayoutDevicePoint& aRefPoint,
Modifiers aModifiers,
+ int32_t aClickCount,
nsIWidget* aWidget)
{
MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown ||
aMsg == eMouseUp || aMsg == eMouseLongTap);
WidgetMouseEvent event(true, aMsg, aWidget,
WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
event.mTime = aTime;
event.button = WidgetMouseEvent::eLeftButton;
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
event.mIgnoreRootScrollFrame = true;
if (aMsg != eMouseMove) {
- event.mClickCount = 1;
+ event.mClickCount = aClickCount;
}
event.mModifiers = aModifiers;
// Real touch events will generate corresponding pointer events. We set
// convertToPointer to false to prevent the synthesized mouse events generate
// pointer events again.
event.convertToPointer = false;
return DispatchWidgetEvent(event);
}
@@ -520,27 +521,28 @@ APZCCallbackHelper::DispatchMouseEvent(c
&defaultPrevented, false, /* aIsWidgetEventSynthesized = */ false);
return defaultPrevented;
}
void
APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
Modifiers aModifiers,
+ int32_t aClickCount,
nsIWidget* aWidget)
{
if (aWidget->Destroyed()) {
return;
}
APZCCH_LOG("Dispatching single-tap component events to %s\n",
Stringify(aPoint).c_str());
int time = 0;
- DispatchSynthesizedMouseEvent(eMouseMove, time, aPoint, aModifiers, aWidget);
- DispatchSynthesizedMouseEvent(eMouseDown, time, aPoint, aModifiers, aWidget);
- DispatchSynthesizedMouseEvent(eMouseUp, time, aPoint, aModifiers, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseMove, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseDown, time, aPoint, aModifiers, aClickCount, aWidget);
+ DispatchSynthesizedMouseEvent(eMouseUp, time, aPoint, aModifiers, aClickCount, aWidget);
}
static dom::Element*
GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
{
if (!aScrollableFrame) {
return nullptr;
}
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -103,16 +103,17 @@ public:
static nsEventStatus DispatchWidgetEvent(WidgetGUIEvent& aEvent);
/* Synthesize a mouse event with the given parameters, and dispatch it
* via the given widget. */
static nsEventStatus DispatchSynthesizedMouseEvent(EventMessage aMsg,
uint64_t aTime,
const LayoutDevicePoint& aRefPoint,
Modifiers aModifiers,
+ int32_t aClickCount,
nsIWidget* aWidget);
/* Dispatch a mouse event with the given parameters.
* Return whether or not any listeners have called preventDefault on the event. */
static bool DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
const nsString& aType,
const CSSPoint& aPoint,
int32_t aButton,
@@ -120,16 +121,17 @@ public:
int32_t aModifiers,
bool aIgnoreRootScrollFrame,
unsigned short aInputSourceArg);
/* Fire a single-tap event at the given point. The event is dispatched
* via the given widget. */
static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
Modifiers aModifiers,
+ int32_t aClickCount,
nsIWidget* aWidget);
/* Perform hit-testing on the touch points of |aEvent| to determine
* which scrollable frames they target. If any of these frames don't have
* a displayport, set one.
*
* If any displayports need to be set, the actual notification to APZ is
* sent to the compositor, which will then post a message back to APZ's
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -124,29 +124,31 @@ APZEventState::~APZEventState()
class DelayedFireSingleTapEvent final : public nsITimerCallback
{
public:
NS_DECL_ISUPPORTS
DelayedFireSingleTapEvent(nsWeakPtr aWidget,
LayoutDevicePoint& aPoint,
Modifiers aModifiers,
+ int32_t aClickCount,
nsITimer* aTimer)
: mWidget(aWidget)
, mPoint(aPoint)
, mModifiers(aModifiers)
+ , mClickCount(aClickCount)
// Hold the reference count until we are called back.
, mTimer(aTimer)
{
}
NS_IMETHOD Notify(nsITimer*) override
{
if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
- APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, widget);
+ APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, mClickCount, widget);
}
mTimer = nullptr;
return NS_OK;
}
void ClearTimer() {
mTimer = nullptr;
}
@@ -154,26 +156,28 @@ public:
private:
~DelayedFireSingleTapEvent()
{
}
nsWeakPtr mWidget;
LayoutDevicePoint mPoint;
Modifiers mModifiers;
+ int32_t mClickCount;
nsCOMPtr<nsITimer> mTimer;
};
NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
void
APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers,
- const ScrollableLayerGuid& aGuid)
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount)
{
APZES_LOG("Handling single tap at %s on %s with %d\n",
Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return;
}
@@ -182,24 +186,24 @@ APZEventState::ProcessSingleTap(const CS
return;
}
LayoutDevicePoint ldPoint = aPoint * aScale;
if (!mActiveElementManager->ActiveElementUsesStyle()) {
// If the active element isn't visually affected by the :active style, we
// have no need to wait the extra sActiveDurationMs to make the activation
// visually obvious to the user.
- APZCCallbackHelper::FireSingleTapEvent(ldPoint, aModifiers, widget);
+ APZCCallbackHelper::FireSingleTapEvent(ldPoint, aModifiers, aClickCount, widget);
return;
}
APZES_LOG("Active element uses style, scheduling timer for click event\n");
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
RefPtr<DelayedFireSingleTapEvent> callback =
- new DelayedFireSingleTapEvent(mWidget, ldPoint, aModifiers, timer);
+ new DelayedFireSingleTapEvent(mWidget, ldPoint, aModifiers, aClickCount, timer);
nsresult rv = timer->InitWithCallback(callback,
sActiveDurationMs,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
// Make |callback| not hold the timer, so they will both be destructed when
// we leave the scope of this function.
callback->ClearTimer();
}
@@ -227,18 +231,19 @@ APZEventState::FireContextmenuEvents(con
// and so we should remove any activation
mActiveElementManager->ClearActivation();
} else {
// If no one handle context menu, fire MOZLONGTAP event
LayoutDevicePoint ldPoint = aPoint * aScale;
int time = 0;
nsEventStatus status =
APZCCallbackHelper::DispatchSynthesizedMouseEvent(eMouseLongTap, time,
- ldPoint,
- aModifiers, aWidget);
+ ldPoint, aModifiers,
+ /*clickCount*/ 1,
+ aWidget);
eventHandled = (status == nsEventStatus_eConsumeNoDefault);
APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
}
return eventHandled;
}
void
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -44,17 +44,18 @@ public:
APZEventState(nsIWidget* aWidget,
ContentReceivedInputBlockCallback&& aCallback);
NS_INLINE_DECL_REFCOUNTING(APZEventState);
void ProcessSingleTap(const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers,
- const ScrollableLayerGuid& aGuid);
+ const ScrollableLayerGuid& aGuid,
+ int32_t aClickCount);
void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId);
void ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
const CSSPoint& aPoint,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -182,21 +182,24 @@ ChromeProcessController::HandleTap(TapTy
if (!presShell->GetPresContext()) {
return;
}
CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
switch (aType) {
case TapType::eSingleTap:
- mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid);
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
break;
case TapType::eDoubleTap:
HandleDoubleTap(point, aModifiers, aGuid);
break;
+ case TapType::eSecondTap:
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
+ break;
case TapType::eLongTap:
mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
aInputBlockId);
break;
case TapType::eLongTapUp:
mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
break;
case TapType::eSentinel:
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -482,16 +482,17 @@ public:
{
// Warning, this enum is serialized and sent over IPC. If you reorder, add,
// or remove a value, you need to update its ParamTraits<> in nsGUIEventIPC.h
TAPGESTURE_LONG,
TAPGESTURE_LONG_UP,
TAPGESTURE_UP,
TAPGESTURE_CONFIRMED,
TAPGESTURE_DOUBLE,
+ TAPGESTURE_SECOND,
TAPGESTURE_CANCEL,
// Used as an upper bound for ContiguousEnumSerializer
TAPGESTURE_SENTINEL,
};
// Construct a tap gesture from a Screen point.
// mLocalPoint remains (0,0) unless it's set later.
--- a/widget/android/AndroidContentController.cpp
+++ b/widget/android/AndroidContentController.cpp
@@ -88,17 +88,18 @@ AndroidContentController::HandleTap(TapT
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId)
{
// This function will get invoked first on the Java UI thread, and then
// again on the main thread (because of the code in ChromeProcessController::
// HandleTap). We want to post the SingleTap message once; it can be
// done from either thread but we need access to the callback transform
// so we do it from the main thread.
- if (NS_IsMainThread() && aType == TapType::eSingleTap) {
+ if (NS_IsMainThread() &&
+ (aType == TapType::eSingleTap || aType == TapType::eSecondTap)) {
DispatchSingleTapToObservers(aPoint, aGuid);
}
ChromeProcessController::HandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId);
}
void
AndroidContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)