Bug 942929 - Use longtapup event to handle firing clicks when longtap not handled. r=kats

This commit is contained in:
Dale Harvey 2013-12-12 00:39:06 +00:00
parent 0f282f129e
commit 6ecc49a360
18 changed files with 189 additions and 19 deletions

View File

@ -346,6 +346,15 @@ child:
*/
HandleLongTap(CSSIntPoint point);
/**
* Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
* relative to the current scroll offset. In the case the "contextmenu"
* event generated by the preceding HandleLongTap call was not handled,
* this message is expected to generate a "mousedown" and "mouseup"
* series of events
*/
HandleLongTapUp(CSSIntPoint point);
/**
* Notifies the child that the parent has begun or finished transforming
* the visible child content area. Useful for showing/hiding scrollbars.

View File

@ -285,6 +285,7 @@ TabChild::TabChild(ContentChild* aManager, const TabContext& aContext, uint32_t
, mTriedBrowserInit(false)
, mOrientation(eScreenOrientation_PortraitPrimary)
, mUpdateHitRegion(false)
, mContextMenuHandled(false)
{
}
@ -1660,6 +1661,18 @@ TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint)
return true;
}
bool
TabChild::RecvHandleLongTapUp(const CSSIntPoint& aPoint)
{
if (mContextMenuHandled) {
mContextMenuHandled = false;
return true;
}
RecvHandleSingleTap(aPoint);
return true;
}
bool
TabChild::RecvNotifyTransformBegin(const ViewID& aViewId)
{

View File

@ -217,6 +217,7 @@ public:
virtual bool RecvHandleDoubleTap(const CSSIntPoint& aPoint);
virtual bool RecvHandleSingleTap(const CSSIntPoint& aPoint);
virtual bool RecvHandleLongTap(const CSSIntPoint& aPoint);
virtual bool RecvHandleLongTapUp(const CSSIntPoint& aPoint);
virtual bool RecvNotifyTransformBegin(const ViewID& aViewId);
virtual bool RecvNotifyTransformEnd(const ViewID& aViewId);
virtual bool RecvActivate();
@ -496,6 +497,7 @@ private:
bool mTriedBrowserInit;
ScreenOrientation mOrientation;
bool mUpdateHitRegion;
bool mContextMenuHandled;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

View File

@ -521,6 +521,13 @@ void TabParent::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
}
}
void TabParent::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
{
if (!mIsDestroyed) {
unused << SendHandleLongTapUp(aPoint);
}
}
void TabParent::NotifyTransformBegin(ViewID aViewId)
{
if (!mIsDestroyed) {
@ -713,6 +720,15 @@ bool TabParent::SendHandleLongTap(const CSSIntPoint& aPoint)
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint));
}
bool TabParent::SendHandleLongTapUp(const CSSIntPoint& aPoint)
{
if (mIsDestroyed) {
return false;
}
return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint));
}
bool TabParent::SendHandleDoubleTap(const CSSIntPoint& aPoint)
{
if (mIsDestroyed) {

View File

@ -196,6 +196,7 @@ public:
void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers);
void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers);
void NotifyTransformBegin(ViewID aViewId);
void NotifyTransformEnd(ViewID aViewId);
void Activate();
@ -217,6 +218,7 @@ public:
bool SendRealTouchEvent(WidgetTouchEvent& event);
bool SendHandleSingleTap(const CSSIntPoint& aPoint);
bool SendHandleLongTap(const CSSIntPoint& aPoint);
bool SendHandleLongTapUp(const CSSIntPoint& aPoint);
bool SendHandleDoubleTap(const CSSIntPoint& aPoint);
virtual PDocumentRendererParent*

View File

@ -518,6 +518,7 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent)
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;
@ -821,6 +822,20 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
return nsEventStatus_eIgnore;
}
nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEvent) {
APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (controller) {
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
CSSIntPoint geckoScreenPoint;
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
controller->HandleLongTapUp(geckoScreenPoint, modifiers);
return nsEventStatus_eConsumeNoDefault;
}
}
return nsEventStatus_eIgnore;
}
nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();

View File

@ -350,30 +350,23 @@ protected:
nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
/**
* Helper method for long press gestures.
*
* XXX: Implement this.
* Helper methods for long press gestures.
*/
nsEventStatus OnLongPress(const TapGestureInput& aEvent);
nsEventStatus OnLongPressUp(const TapGestureInput& aEvent);
/**
* Helper method for single tap gestures.
*
* XXX: Implement this.
*/
nsEventStatus OnSingleTapUp(const TapGestureInput& aEvent);
/**
* Helper method for a single tap confirmed.
*
* XXX: Implement this.
*/
nsEventStatus OnSingleTapConfirmed(const TapGestureInput& aEvent);
/**
* Helper method for double taps.
*
* XXX: Implement this.
*/
nsEventStatus OnDoubleTap(const TapGestureInput& aEvent);

View File

@ -49,6 +49,13 @@ public:
*/
virtual void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) = 0;
/**
* Requests handling of releasing a long tap. |aPoint| is in CSS pixels,
* relative to the current scroll offset. HandleLongTapUp will always be
* preceeded by HandleLongTap
*/
virtual void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) = 0;
/**
* Requests sending a mozbrowserasyncscroll domevent to embedder.
* |aContentRect| is in CSS pixels, relative to the current cssPage.

View File

@ -159,7 +159,10 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
}
if (mState == GESTURE_WAITING_SINGLE_TAP &&
if (mState == GESTURE_LONG_TAP_UP) {
HandleLongTapUpEvent(event);
mState = GESTURE_NONE;
} else if (mState == GESTURE_WAITING_SINGLE_TAP &&
event.mTime - mTapStartTime > MAX_TAP_TIME) {
// Extended taps are immediately dispatched as single taps
CancelLongTapTimeoutTask();
@ -314,6 +317,13 @@ nsEventStatus GestureEventListener::HandleLongTapEvent(const MultiTouchInput& aE
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;
@ -326,6 +336,7 @@ nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEven
break;
case GESTURE_WAITING_DOUBLE_TAP:
case GESTURE_LONG_TAP_UP:
mState = GESTURE_NONE;
break;
default:
@ -366,7 +377,7 @@ void GestureEventListener::TimeoutLongTap()
mLongTapTimeoutTask = nullptr;
// If the tap has not been released, this is a long press.
if (mState == GESTURE_WAITING_SINGLE_TAP) {
mState = GESTURE_NONE;
mState = GESTURE_LONG_TAP_UP;
HandleLongTapEvent(mLastTouchInput);
}

View File

@ -85,7 +85,10 @@ protected:
// 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
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
};
/**
@ -120,6 +123,12 @@ protected:
*/
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

View File

@ -13,6 +13,7 @@
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/APZCTreeManager.h"
#include "base/task.h"
#include "Layers.h"
#include "TestLayers.h"
@ -23,16 +24,35 @@ using ::testing::_;
using ::testing::NiceMock;
using ::testing::AtLeast;
class Task;
class MockContentController : public GeckoContentController {
public:
MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
MOCK_METHOD2(HandleDoubleTap, void(const CSSIntPoint&, int32_t));
MOCK_METHOD2(HandleSingleTap, void(const CSSIntPoint&, int32_t));
MOCK_METHOD2(HandleLongTap, void(const CSSIntPoint&, int32_t));
MOCK_METHOD2(HandleLongTapUp, void(const CSSIntPoint&, int32_t));
MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
};
class MockContentControllerDelayed : public MockContentController {
public:
void PostDelayedTask(Task* aTask, int aDelayMs) {
mCurrentTask = aTask;
}
Task* GetDelayedTask() {
return mCurrentTask;
}
private:
Task *mCurrentTask;
};
class TestAPZCContainerLayer : public ContainerLayer {
public:
TestAPZCContainerLayer()
@ -152,20 +172,27 @@ ApzcPinch(AsyncPanZoomController* aApzc, int aFocusX, int aFocusY, float aScale)
}
static nsEventStatus
ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength) {
ApzcDown(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
nsEventStatus status = apzc->ReceiveInputEvent(mti);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
// APZC should be in TOUCHING state
return apzc->ReceiveInputEvent(mti);
}
aTime += aTapLength;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
static nsEventStatus
ApzcUp(AsyncPanZoomController* apzc, int aX, int aY, int& aTime) {
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, 0);
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0));
return apzc->ReceiveInputEvent(mti);
}
static nsEventStatus
ApzcTap(AsyncPanZoomController* apzc, int aX, int aY, int& aTime, int aTapLength) {
nsEventStatus status = ApzcDown(apzc, aX, aY, aTime);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
aTime += aTapLength;
return ApzcUp(apzc, aX, aY, aTime);
}
TEST(AsyncPanZoomController, Constructor) {
// RefCounted class can't live in the stack
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
@ -487,6 +514,39 @@ TEST(AsyncPanZoomController, MediumPress) {
apzc->Destroy();
}
TEST(AsyncPanZoomController, LongPress) {
nsRefPtr<MockContentControllerDelayed> mcc = new MockContentControllerDelayed();
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
apzc->UpdateZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0));
int time = 0;
nsEventStatus status = ApzcDown(apzc, 10, 10, time);
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
Task* t = mcc->GetDelayedTask();
EXPECT_TRUE(nullptr != t);
EXPECT_CALL(*mcc, HandleLongTap(CSSIntPoint(10, 10), 0)).Times(1);
EXPECT_CALL(*mcc, HandleLongTapUp(CSSIntPoint(10, 10), 0)).Times(1);
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
// Manually invoke the longpress while the touch is currently down.
t->Run();
time += 1000;
status = ApzcUp(apzc, 10, 10, time);
EXPECT_EQ(nsEventStatus_eIgnore, status);
apzc->Destroy();
}
// Layer tree for HitTesting1
static already_AddRefed<mozilla::layers::Layer>
CreateTestLayerTree1(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {

View File

@ -567,6 +567,24 @@ public:
}
}
virtual void HandleLongTapUp(const CSSIntPoint& aPoint,
int32_t aModifiers) MOZ_OVERRIDE
{
if (MessageLoop::current() != mUILoop) {
// We have to send this message from the "UI thread" (main
// thread).
mUILoop->PostTask(
FROM_HERE,
NewRunnableMethod(this, &RemoteContentController::HandleLongTapUp,
aPoint, aModifiers));
return;
}
if (mRenderFrame) {
TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
browser->HandleLongTapUp(aPoint, aModifiers);
}
}
void ClearRenderFrame() { mRenderFrame = nullptr; }
virtual void SendAsyncScrollDOMEvent(bool aIsRoot,

View File

@ -239,6 +239,7 @@ public:
enum TapGestureType
{
TAPGESTURE_LONG,
TAPGESTURE_LONG_UP,
TAPGESTURE_UP,
TAPGESTURE_CONFIRMED,
TAPGESTURE_DOUBLE,

View File

@ -2002,6 +2002,11 @@ AndroidBridge::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
NS_LITERAL_CSTRING("Gesture:LongPress"), data));
}
void
AndroidBridge::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
{
}
void
AndroidBridge::SendAsyncScrollDOMEvent(bool aIsRoot,
const CSSRect& aContentRect,

View File

@ -409,6 +409,7 @@ public:
void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE;
void SendAsyncScrollDOMEvent(bool aIsRoot,
const CSSRect& aContentRect,
const CSSSize& aScrollableSize) MOZ_OVERRIDE;

View File

@ -23,6 +23,8 @@ public:
virtual void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
virtual void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
virtual void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
virtual void HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers) MOZ_OVERRIDE {}
virtual void SendAsyncScrollDOMEvent(bool aIsRoot,
const CSSRect &aContentRect,
const CSSSize &aScrollableSize) MOZ_OVERRIDE {}

View File

@ -301,6 +301,11 @@ APZController::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
{
}
void
APZController::HandleLongTapUp(const CSSIntPoint& aPoint, int32_t aModifiers)
{
}
// requests that we send a mozbrowserasyncscroll domevent. not in use.
void
APZController::SendAsyncScrollDOMEvent(bool aIsRoot,

View File

@ -35,6 +35,7 @@ public:
virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
virtual void HandleLongTapUp(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
virtual void PostDelayedTask(Task* aTask, int aDelayMs);
virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid);