mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1009733 - Rewrite much of the APZC input block handling code. r=botond
This commit is contained in:
parent
9f8eabeb1c
commit
e7190ff526
@ -401,49 +401,17 @@ child:
|
||||
|
||||
UpdateFrame(FrameMetrics frame);
|
||||
|
||||
/**
|
||||
* Acknowledge the receipt of a scroll offset update from the content
|
||||
* process. This is used to manage concurrent scroll updates from many
|
||||
* sources.
|
||||
*/
|
||||
// The following methods correspond to functions on the GeckoContentController
|
||||
// interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
|
||||
// in that file for these functions.
|
||||
AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration);
|
||||
|
||||
/**
|
||||
* Requests handling of a double tap. |point| is in CSS pixels, relative to
|
||||
* the scroll offset. This message is expected to round-trip back to
|
||||
* ZoomToRect() with a rect indicating where we should zoom to.
|
||||
*/
|
||||
HandleDoubleTap(CSSPoint point, ScrollableLayerGuid aGuid);
|
||||
|
||||
/**
|
||||
* Requests handling of a single tap. |point| is in CSS pixels, relative to
|
||||
* the scroll offset. This message is expected to send a "mousedown" and
|
||||
* "mouseup" series of events at this point.
|
||||
*/
|
||||
HandleSingleTap(CSSPoint point, ScrollableLayerGuid aGuid);
|
||||
|
||||
/**
|
||||
* Requests handling of a long tap. |point| is in CSS pixels, relative to
|
||||
* the scroll offset. This message is expected to send a "contextmenu"
|
||||
* events at this point.
|
||||
*/
|
||||
HandleLongTap(CSSPoint point, ScrollableLayerGuid aGuid);
|
||||
|
||||
/**
|
||||
* 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(CSSPoint point, ScrollableLayerGuid aGuid);
|
||||
|
||||
/**
|
||||
* Notifies the child about various APZ state changes.
|
||||
* See GeckoContentController::NotifyAPZStateChange() for details.
|
||||
*/
|
||||
NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
|
||||
|
||||
|
||||
/**
|
||||
* Sending an activate message moves focus to the child.
|
||||
*/
|
||||
|
@ -702,9 +702,7 @@ TabChild::TabChild(nsIContentChild* aManager, const TabContext& aContext, uint32
|
||||
, mTriedBrowserInit(false)
|
||||
, mOrientation(eScreenOrientation_PortraitPrimary)
|
||||
, mUpdateHitRegion(false)
|
||||
, mContextMenuHandled(false)
|
||||
, mLongTapEventHandled(false)
|
||||
, mWaitingTouchListeners(false)
|
||||
, mPendingTouchPreventedResponse(false)
|
||||
, mIgnoreKeyPressEvent(false)
|
||||
, mActiveElementManager(new ActiveElementManager())
|
||||
, mHasValidInnerSize(false)
|
||||
@ -1805,23 +1803,23 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& a
|
||||
return true;
|
||||
}
|
||||
|
||||
mContextMenuHandled =
|
||||
bool eventHandled =
|
||||
DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
|
||||
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid),
|
||||
2, 1, 0, false,
|
||||
nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
|
||||
|
||||
// If no one handle context menu, fire MOZLONGTAP event
|
||||
if (!mContextMenuHandled) {
|
||||
if (!eventHandled) {
|
||||
LayoutDevicePoint currentPoint =
|
||||
APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();
|
||||
int time = 0;
|
||||
nsEventStatus status =
|
||||
DispatchSynthesizedMouseEvent(NS_MOUSE_MOZLONGTAP, time, currentPoint, mWidget);
|
||||
mLongTapEventHandled = (status == nsEventStatus_eConsumeNoDefault);
|
||||
eventHandled = (status == nsEventStatus_eConsumeNoDefault);
|
||||
}
|
||||
|
||||
SendContentReceivedTouch(aGuid, mContextMenuHandled || mLongTapEventHandled);
|
||||
SendContentReceivedTouch(aGuid, eventHandled);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1829,16 +1827,6 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const ScrollableLayerGuid& a
|
||||
bool
|
||||
TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (mContextMenuHandled) {
|
||||
mContextMenuHandled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mLongTapEventHandled) {
|
||||
mLongTapEventHandled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
RecvHandleSingleTap(aPoint, aGuid);
|
||||
return true;
|
||||
}
|
||||
@ -2095,23 +2083,21 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
|
||||
nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow ? outerWindow->GetCurrentInnerWindow() : nullptr;
|
||||
|
||||
if (!innerWindow || (!innerWindow->HasTouchEventListeners() &&
|
||||
!innerWindow->MayHaveTouchCaret())) {
|
||||
SendContentReceivedTouch(aGuid, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
|
||||
localEvent.mFlags.mMultipleActionsPrevented;
|
||||
switch (aEvent.message) {
|
||||
case NS_TOUCH_START: {
|
||||
if (mPendingTouchPreventedResponse) {
|
||||
// We can enter here if we get two TOUCH_STARTs in a row and didn't
|
||||
// respond to the first one. Respond to it now.
|
||||
SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
|
||||
mPendingTouchPreventedResponse = false;
|
||||
}
|
||||
if (isTouchPrevented) {
|
||||
SendContentReceivedTouch(aGuid, isTouchPrevented);
|
||||
} else {
|
||||
mWaitingTouchListeners = true;
|
||||
mPendingTouchPreventedResponse = true;
|
||||
mPendingTouchPreventedGuid = aGuid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2119,9 +2105,10 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
|
||||
case NS_TOUCH_MOVE:
|
||||
case NS_TOUCH_END:
|
||||
case NS_TOUCH_CANCEL: {
|
||||
if (mWaitingTouchListeners) {
|
||||
SendContentReceivedTouch(aGuid, isTouchPrevented);
|
||||
mWaitingTouchListeners = false;
|
||||
if (mPendingTouchPreventedResponse) {
|
||||
MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
|
||||
SendContentReceivedTouch(mPendingTouchPreventedGuid, isTouchPrevented);
|
||||
mPendingTouchPreventedResponse = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -586,9 +586,8 @@ private:
|
||||
bool mTriedBrowserInit;
|
||||
ScreenOrientation mOrientation;
|
||||
bool mUpdateHitRegion;
|
||||
bool mContextMenuHandled;
|
||||
bool mLongTapEventHandled;
|
||||
bool mWaitingTouchListeners;
|
||||
bool mPendingTouchPreventedResponse;
|
||||
ScrollableLayerGuid mPendingTouchPreventedGuid;
|
||||
void FireSingleTapEvent(LayoutDevicePoint aPoint);
|
||||
|
||||
bool mIgnoreKeyPressEvent;
|
||||
|
@ -68,7 +68,8 @@ public:
|
||||
* relative to the current scroll offset. HandleLongTapUp will always be
|
||||
* preceeded by HandleLongTap. However not all calls to HandleLongTap will
|
||||
* be followed by a HandleLongTapUp (for example, if the user drags
|
||||
* around between the long-tap and lifting their finger).
|
||||
* around between the long-tap and lifting their finger, or if content
|
||||
* notifies the APZ that the long-tap event was prevent-defaulted).
|
||||
*/
|
||||
virtual void HandleLongTapUp(const CSSPoint& aPoint,
|
||||
int32_t aModifiers,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "FrameMetrics.h" // for FrameMetrics, etc
|
||||
#include "GestureEventListener.h" // for GestureEventListener
|
||||
#include "InputData.h" // for MultiTouchInput, etc
|
||||
#include "TouchBlockState.h" // for TouchBlockState
|
||||
#include "Units.h" // for CSSRect, CSSPoint, etc
|
||||
#include "UnitTransforms.h" // for TransformTo
|
||||
#include "base/message_loop.h" // for MessageLoop
|
||||
@ -316,14 +317,6 @@ typedef GeckoContentController::APZStateChange APZStateChange;
|
||||
* Units: ms
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default touch behavior (is used when not touch behavior is set).
|
||||
*/
|
||||
static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
|
||||
AllowedTouchBehavior::HORIZONTAL_PAN |
|
||||
AllowedTouchBehavior::PINCH_ZOOM |
|
||||
AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
|
||||
|
||||
/**
|
||||
* Angle from axis within which we stay axis-locked
|
||||
*/
|
||||
@ -697,7 +690,6 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
||||
mSharingFrameMetricsAcrossProcesses(false),
|
||||
mMonitor("AsyncPanZoomController"),
|
||||
mState(NOTHING),
|
||||
mContentResponseTimeoutTask(nullptr),
|
||||
mX(MOZ_THIS_IN_INITIALIZER_LIST()),
|
||||
mY(MOZ_THIS_IN_INITIALIZER_LIST()),
|
||||
mPanDirRestricted(false),
|
||||
@ -707,7 +699,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
||||
mLastAsyncScrollOffset(0, 0),
|
||||
mCurrentAsyncScrollOffset(0, 0),
|
||||
mAsyncScrollTimeoutTask(nullptr),
|
||||
mHandlingTouchQueue(false),
|
||||
mTouchBlockBalance(0),
|
||||
mTreeManager(aTreeManager),
|
||||
mScrollParentId(FrameMetrics::NULL_SCROLL_ID),
|
||||
mAPZCId(sAsyncPanZoomControllerCount++),
|
||||
@ -759,6 +751,8 @@ AsyncPanZoomController::Destroy()
|
||||
{
|
||||
CancelAnimation();
|
||||
|
||||
mTouchBlockQueue.Clear();
|
||||
|
||||
{ // scope the lock
|
||||
MonitorAutoLock lock(mRefPtrMonitor);
|
||||
mGeckoContentController = nullptr;
|
||||
@ -802,39 +796,50 @@ AsyncPanZoomController::GetTouchStartTolerance()
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
|
||||
if (aEvent.mInputType == MULTITOUCH_INPUT &&
|
||||
aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
// Starting a new touch block, clear old touch block state.
|
||||
mTouchBlockState = TouchBlockState();
|
||||
if (aEvent.mInputType != MULTITOUCH_INPUT) {
|
||||
return HandleInputEvent(aEvent);
|
||||
}
|
||||
|
||||
// If we may have touch listeners and touch action property is enabled, we
|
||||
// enable the machinery that allows touch listeners to preventDefault any touch inputs
|
||||
// and also waits for the allowed touch behavior values to be received from the outside.
|
||||
// This should not happen unless there are actually touch listeners and touch-action property
|
||||
// enable as it introduces potentially unbounded lag because it causes a round-trip through
|
||||
// content. Usually, if content is responding in a timely fashion, this only introduces a
|
||||
// nearly constant few hundred ms of lag.
|
||||
if ((mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) &&
|
||||
aEvent.mInputType == MULTITOUCH_INPUT &&
|
||||
(mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
SetState(WAITING_CONTENT_RESPONSE);
|
||||
TouchBlockState* block = nullptr;
|
||||
if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
block = StartNewTouchBlock(false);
|
||||
APZC_LOG("%p started new touch block %p\n", this, block);
|
||||
if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.mMayHaveTouchCaret) {
|
||||
// Content may intercept the touch events and prevent-default them. So we schedule
|
||||
// a timeout to give content time to do that.
|
||||
ScheduleContentResponseTimeout();
|
||||
} else {
|
||||
// Content won't prevent-default this, so we can just pretend like we scheduled
|
||||
// a timeout and it expired. Note that we will still receive a ContentReceivedTouch
|
||||
// callback for this block, and so we need to make sure we adjust the touch balance.
|
||||
APZC_LOG("%p not waiting for content response on block %p\n", this, block);
|
||||
mTouchBlockBalance++;
|
||||
block->TimeoutContentResponse();
|
||||
}
|
||||
} else if (mTouchBlockQueue.IsEmpty()) {
|
||||
NS_WARNING("Received a non-start touch event while no touch blocks active!");
|
||||
} else {
|
||||
// this touch is part of the most-recently created block
|
||||
block = mTouchBlockQueue.LastElement().get();
|
||||
APZC_LOG("%p received new event in block %p\n", this, block);
|
||||
}
|
||||
|
||||
if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
|
||||
if (aEvent.mInputType == MULTITOUCH_INPUT) {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
mTouchQueue.AppendElement(multiTouchInput);
|
||||
|
||||
SetContentResponseTimer();
|
||||
}
|
||||
if (!block) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
return HandleInputEvent(aEvent);
|
||||
if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
|
||||
APZC_LOG("%p's current touch block is ready with preventdefault %d\n",
|
||||
this, block->IsDefaultPrevented());
|
||||
if (block->IsDefaultPrevented()) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
return HandleInputEvent(aEvent);
|
||||
}
|
||||
|
||||
// Otherwise, add it to the queue for the touch block
|
||||
block->AddEvent(aEvent.AsMultiTouchInput());
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
|
||||
@ -957,7 +962,6 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
|
||||
case CROSS_SLIDING_X:
|
||||
case CROSS_SLIDING_Y:
|
||||
case PINCHING:
|
||||
case WAITING_CONTENT_RESPONSE:
|
||||
NS_WARNING("Received impossible touch in OnTouchStart");
|
||||
break;
|
||||
default:
|
||||
@ -992,9 +996,7 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled() &&
|
||||
(GetTouchBehavior(0) & AllowedTouchBehavior::VERTICAL_PAN) &&
|
||||
(GetTouchBehavior(0) & AllowedTouchBehavior::HORIZONTAL_PAN)) {
|
||||
if (gfxPrefs::TouchActionEnabled() && CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
|
||||
// User tries to trigger a touch behavior. If allowed touch behavior is vertical pan
|
||||
// + horizontal pan (touch-action value is equal to AUTO) we can return ConsumeNoDefault
|
||||
// status immediately to trigger cancel event further. It should happen independent of
|
||||
@ -1017,7 +1019,6 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
|
||||
NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
case WAITING_CONTENT_RESPONSE:
|
||||
case SNAP_BACK: // Should not receive a touch-move in the SNAP_BACK state
|
||||
// as touch blocks that begin in an overscrolled state
|
||||
// are ignored.
|
||||
@ -1092,7 +1093,6 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
||||
NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
|
||||
return nsEventStatus_eIgnore;
|
||||
|
||||
case WAITING_CONTENT_RESPONSE:
|
||||
case SNAP_BACK: // Should not receive a touch-move in the SNAP_BACK state
|
||||
// as touch blocks that begin in an overscrolled state
|
||||
// are ignored.
|
||||
@ -1113,7 +1113,9 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven
|
||||
nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
|
||||
|
||||
if (!TouchActionAllowPinchZoom()) {
|
||||
// Note that there may not be a touch block at this point, if we received the
|
||||
// PinchGestureEvent directly from widget code without any touch events.
|
||||
if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
@ -1130,7 +1132,7 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
|
||||
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a scale in state %d\n", this, mState);
|
||||
|
||||
if (!TouchActionAllowPinchZoom()) {
|
||||
if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
@ -1213,7 +1215,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
|
||||
nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a scale-end in state %d\n", this, mState);
|
||||
|
||||
if (!TouchActionAllowPinchZoom()) {
|
||||
if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
@ -1353,8 +1355,8 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
SetState(WAITING_CONTENT_RESPONSE);
|
||||
SetContentResponseTimer();
|
||||
StartNewTouchBlock(true);
|
||||
ScheduleContentResponseTimeout();
|
||||
controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -1391,7 +1393,7 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aP
|
||||
NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
|
||||
geckoScreenPoint, modifiers, GetGuid()),
|
||||
0);
|
||||
mTouchBlockState.mSingleTapOccurred = true;
|
||||
CurrentTouchBlock()->SetSingleTapOccurred();
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
@ -1401,7 +1403,7 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aP
|
||||
void AsyncPanZoomController::OnTouchEndOrCancel() {
|
||||
if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
|
||||
controller->NotifyAPZStateChange(
|
||||
GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
|
||||
GetGuid(), APZStateChange::EndTouch, CurrentTouchBlock()->SingleTapOccurred());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1409,7 +1411,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
|
||||
APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
|
||||
// If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
|
||||
// sending event to content
|
||||
if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
|
||||
if (!(mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom())) {
|
||||
return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
|
||||
}
|
||||
return nsEventStatus_eIgnore;
|
||||
@ -1424,7 +1426,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
|
||||
APZC_LOG("%p got a double-tap in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
|
||||
if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
@ -1451,10 +1453,10 @@ const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
|
||||
return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBehaviorFlags aBehavior) {
|
||||
void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle) {
|
||||
// Handling of cross sliding will need to be added in this method after touch-action released
|
||||
// enabled by default.
|
||||
if ((aBehavior & AllowedTouchBehavior::VERTICAL_PAN) && (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN)) {
|
||||
if (CurrentTouchBlock()->TouchActionAllowsPanningXY()) {
|
||||
if (mX.CanScrollNow() && mY.CanScrollNow()) {
|
||||
if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
|
||||
mY.SetAxisLocked(true);
|
||||
@ -1470,7 +1472,7 @@ void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBe
|
||||
} else {
|
||||
SetState(NOTHING);
|
||||
}
|
||||
} else if (aBehavior & AllowedTouchBehavior::HORIZONTAL_PAN) {
|
||||
} else if (CurrentTouchBlock()->TouchActionAllowsPanningX()) {
|
||||
// Using bigger angle for panning to keep behavior consistent
|
||||
// with IE.
|
||||
if (IsCloseToHorizontal(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
|
||||
@ -1482,7 +1484,7 @@ void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBe
|
||||
// requires it.
|
||||
SetState(NOTHING);
|
||||
}
|
||||
} else if (aBehavior & AllowedTouchBehavior::VERTICAL_PAN) {
|
||||
} else if (CurrentTouchBlock()->TouchActionAllowsPanningY()) {
|
||||
if (IsCloseToVertical(aAngle, ALLOWED_DIRECT_PAN_ANGLE)) {
|
||||
mX.SetAxisLocked(true);
|
||||
SetState(PANNING_LOCKED_Y);
|
||||
@ -1561,7 +1563,7 @@ nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent
|
||||
angle = fabs(angle); // range [0, pi]
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
HandlePanningWithTouchAction(angle, GetTouchBehavior(0));
|
||||
HandlePanningWithTouchAction(angle);
|
||||
} else {
|
||||
if (GetAxisLockMode() == FREE) {
|
||||
SetState(PANNING);
|
||||
@ -2458,90 +2460,144 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
|
||||
mTouchBlockState.mPreventDefaultSet = true;
|
||||
mTouchBlockState.mPreventDefault = aPreventDefault;
|
||||
CheckContentResponse();
|
||||
void
|
||||
AsyncPanZoomController::ScheduleContentResponseTimeout() {
|
||||
APZC_LOG("%p scheduling content response timeout\n", this);
|
||||
PostDelayedTask(
|
||||
NewRunnableMethod(this, &AsyncPanZoomController::ContentResponseTimeout),
|
||||
gfxPrefs::APZContentResponseTimeout());
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::CheckContentResponse() {
|
||||
bool canProceedToTouchState = true;
|
||||
|
||||
if (mFrameMetrics.mMayHaveTouchListeners ||
|
||||
mFrameMetrics.mMayHaveTouchCaret) {
|
||||
canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
|
||||
}
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
|
||||
}
|
||||
|
||||
if (!canProceedToTouchState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mContentResponseTimeoutTask) {
|
||||
mContentResponseTimeoutTask->Cancel();
|
||||
mContentResponseTimeoutTask = nullptr;
|
||||
}
|
||||
|
||||
if (mState == WAITING_CONTENT_RESPONSE) {
|
||||
if (!mTouchBlockState.mPreventDefault) {
|
||||
SetState(NOTHING);
|
||||
}
|
||||
|
||||
mHandlingTouchQueue = true;
|
||||
|
||||
while (!mTouchQueue.IsEmpty()) {
|
||||
if (!mTouchBlockState.mPreventDefault) {
|
||||
HandleInputEvent(mTouchQueue[0]);
|
||||
}
|
||||
|
||||
if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_END ||
|
||||
mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_CANCEL) {
|
||||
mTouchQueue.RemoveElementAt(0);
|
||||
void
|
||||
AsyncPanZoomController::ContentResponseTimeout() {
|
||||
mTouchBlockBalance++;
|
||||
APZC_LOG("%p got a content response timeout; balance %d\n", this, mTouchBlockBalance);
|
||||
if (mTouchBlockBalance > 0) {
|
||||
// Find the first touch block in the queue that hasn't already received
|
||||
// the content response timeout callback, and notify it.
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->TimeoutContentResponse()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mTouchQueue.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
mHandlingTouchQueue = false;
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("APZC received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
// Pointer events specification implies all touch points to allow zoom
|
||||
// to perform it.
|
||||
for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
|
||||
return false;
|
||||
void
|
||||
AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
|
||||
mTouchBlockBalance--;
|
||||
APZC_LOG("%p got a content response; balance %d\n", this, mTouchBlockBalance);
|
||||
if (mTouchBlockBalance < 0) {
|
||||
// Find the first touch block in the queue that hasn't already received
|
||||
// its response from content, and notify it.
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->SetContentResponse(aPreventDefault)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("APZC received more ContentReceivedTouch calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
|
||||
return false;
|
||||
void
|
||||
AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("APZC received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
|
||||
AsyncPanZoomController::TouchBehaviorFlags
|
||||
AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
|
||||
if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
|
||||
return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
|
||||
void
|
||||
AsyncPanZoomController::ProcessPendingInputBlocks() {
|
||||
while (true) {
|
||||
TouchBlockState* curBlock = CurrentTouchBlock();
|
||||
if (!curBlock->IsReadyForHandling()) {
|
||||
break;
|
||||
}
|
||||
|
||||
APZC_LOG("%p processing input block %p; preventDefault %d\n",
|
||||
this, curBlock, curBlock->IsDefaultPrevented());
|
||||
if (curBlock->IsDefaultPrevented()) {
|
||||
SetState(NOTHING);
|
||||
curBlock->DropEvents();
|
||||
} else {
|
||||
while (curBlock->HasEvents()) {
|
||||
HandleInputEvent(curBlock->RemoveFirstEvent());
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(!curBlock->HasEvents());
|
||||
|
||||
if (mTouchBlockQueue.Length() == 1) {
|
||||
// If |curBlock| is the only touch block in the queue, then it is still
|
||||
// active and we cannot remove it yet. We only know that a touch block is
|
||||
// over when we start the next one. This block will be removed by the code
|
||||
// in StartNewTouchBlock, where new touch blocks are added.
|
||||
break;
|
||||
}
|
||||
|
||||
// If we get here, we know there are more touch blocks in the queue after
|
||||
// |curBlock|, so we can remove |curBlock| and try to process the next one.
|
||||
APZC_LOG("%p discarding depleted touch block %p\n", this, curBlock);
|
||||
mTouchBlockQueue.RemoveElementAt(0);
|
||||
}
|
||||
return DefaultTouchBehavior;
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
AsyncPanZoomController::StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent)
|
||||
{
|
||||
TouchBlockState* newBlock = new TouchBlockState();
|
||||
if (gfxPrefs::TouchActionEnabled() && aCopyAllowedTouchBehaviorFromCurrent) {
|
||||
newBlock->CopyAllowedTouchBehaviorsFrom(*CurrentTouchBlock());
|
||||
}
|
||||
|
||||
// We're going to start a new block, so clear out any depleted blocks at the head of the queue.
|
||||
// See corresponding comment in ProcessPendingInputBlocks.
|
||||
while (!mTouchBlockQueue.IsEmpty()) {
|
||||
if (mTouchBlockQueue[0]->IsReadyForHandling() && !mTouchBlockQueue[0]->HasEvents()) {
|
||||
APZC_LOG("%p discarding depleted touch block %p\n", this, mTouchBlockQueue[0]);
|
||||
mTouchBlockQueue.RemoveElementAt(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new block to the queue.
|
||||
mTouchBlockQueue.AppendElement(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
AsyncPanZoomController::CurrentTouchBlock()
|
||||
{
|
||||
MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
|
||||
return mTouchBlockQueue[0].get();
|
||||
}
|
||||
|
||||
bool
|
||||
AsyncPanZoomController::HasReadyTouchBlock()
|
||||
{
|
||||
return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
|
||||
}
|
||||
|
||||
AsyncPanZoomController::TouchBehaviorFlags
|
||||
@ -2552,13 +2608,6 @@ AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
|
||||
return AllowedTouchBehavior::UNKNOWN;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
mTouchBlockState.mAllowedTouchBehaviors.Clear();
|
||||
mTouchBlockState.mAllowedTouchBehaviors.AppendElements(aBehaviors);
|
||||
mTouchBlockState.mAllowedTouchBehaviorSet = true;
|
||||
CheckContentResponse();
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetState(PanZoomState aNewState) {
|
||||
|
||||
PanZoomState oldState;
|
||||
@ -2582,27 +2631,13 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
|
||||
return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
|
||||
return !(aState == NOTHING || aState == TOUCHING);
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
|
||||
return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SetContentResponseTimer() {
|
||||
if (!mContentResponseTimeoutTask) {
|
||||
mContentResponseTimeoutTask =
|
||||
NewRunnableMethod(this, &AsyncPanZoomController::TimeoutContentResponse);
|
||||
|
||||
PostDelayedTask(mContentResponseTimeoutTask, gfxPrefs::APZContentResponseTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TimeoutContentResponse() {
|
||||
mContentResponseTimeoutTask = nullptr;
|
||||
ContentReceivedTouch(false);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
|
||||
APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
|
||||
aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "InputData.h"
|
||||
#include "Axis.h"
|
||||
@ -41,6 +42,7 @@ class PCompositorParent;
|
||||
struct ViewTransform;
|
||||
class AsyncPanZoomAnimation;
|
||||
class FlingAnimation;
|
||||
class TouchBlockState;
|
||||
|
||||
/**
|
||||
* Controller for all panning and zooming logic. Any time a user input is
|
||||
@ -111,6 +113,9 @@ public:
|
||||
* based on what type of input it is. For example, a PinchGestureEvent will
|
||||
* cause scaling. This should only be called externally to this class.
|
||||
* HandleInputEvent() should be used internally.
|
||||
* This function returns nsEventStatus_eIgnore for events that are ignored,
|
||||
* and nsEventStatus_eConsumeDoDefault for events that are queued for
|
||||
* processing pending a content response.
|
||||
*/
|
||||
nsEventStatus ReceiveInputEvent(const InputData& aEvent);
|
||||
|
||||
@ -121,14 +126,6 @@ public:
|
||||
*/
|
||||
void ZoomToRect(CSSRect aRect);
|
||||
|
||||
/**
|
||||
* If we have touch listeners, this should always be called when we know
|
||||
* definitively whether or not content has preventDefaulted any touch events
|
||||
* that have come in. If |aPreventDefault| is true, any touch events in the
|
||||
* queue will be discarded.
|
||||
*/
|
||||
void ContentReceivedTouch(bool aPreventDefault);
|
||||
|
||||
/**
|
||||
* Updates any zoom constraints contained in the <meta name="viewport"> tag.
|
||||
*/
|
||||
@ -303,15 +300,6 @@ public:
|
||||
*/
|
||||
TouchBehaviorFlags GetAllowedTouchBehavior(ScreenIntPoint& aPoint);
|
||||
|
||||
/**
|
||||
* Sets allowed touch behavior for current touch session.
|
||||
* This method is invoked by the APZCTreeManager which in its turn invoked by
|
||||
* the widget after performing touch-action values retrieving.
|
||||
* Must be called after receiving the TOUCH_START even that started the
|
||||
* touch session.
|
||||
*/
|
||||
void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
|
||||
|
||||
/**
|
||||
* Returns whether this APZC is for an element marked with the 'scrollgrab'
|
||||
* attribute.
|
||||
@ -347,10 +335,6 @@ protected:
|
||||
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
ANIMATING_ZOOM, /* animated zoom to a new rect */
|
||||
WAITING_CONTENT_RESPONSE, /* a state halfway between NOTHING and TOUCHING - the user has
|
||||
put a finger down, but we don't yet know if a touch listener has
|
||||
prevented the default actions yet and the allowed touch behavior
|
||||
was not set yet. we still need to abort animations. */
|
||||
SNAP_BACK, /* snap-back animation to relieve overscroll */
|
||||
};
|
||||
|
||||
@ -487,7 +471,7 @@ protected:
|
||||
/**
|
||||
* Sets the panning state basing on the pan direction angle and current touch-action value.
|
||||
*/
|
||||
void HandlePanningWithTouchAction(double angle, TouchBehaviorFlags value);
|
||||
void HandlePanningWithTouchAction(double angle);
|
||||
|
||||
/**
|
||||
* Sets the panning state ignoring the touch action value.
|
||||
@ -543,24 +527,6 @@ protected:
|
||||
*/
|
||||
const FrameMetrics& GetFrameMetrics() const;
|
||||
|
||||
/**
|
||||
* Sets the timer for content response to a series of touch events, if it
|
||||
* hasn't been already. This is to prevent us from batching up touch events
|
||||
* indefinitely in the case that content doesn't respond with whether or not
|
||||
* it wants to preventDefault. When the timer is fired, the touch event queue
|
||||
* will be flushed.
|
||||
*/
|
||||
void SetContentResponseTimer();
|
||||
|
||||
/**
|
||||
* Timeout function for content response. This should be called on a timer
|
||||
* after we get our first touch event in a batch, under the condition that we
|
||||
* waiting for response from content. If a notification comes indicating whether or not
|
||||
* content preventDefaulted a series of touch events and touch behavior values are
|
||||
* set before the timeout, the timeout should be cancelled.
|
||||
*/
|
||||
void TimeoutContentResponse();
|
||||
|
||||
/**
|
||||
* Timeout function for mozbrowserasyncscroll event. Because we throttle
|
||||
* mozbrowserasyncscroll events in some conditions, this function ensures
|
||||
@ -570,62 +536,6 @@ protected:
|
||||
void FireAsyncScrollOnTimeout();
|
||||
|
||||
private:
|
||||
// State related to a single touch block. Does not persist across touch blocks.
|
||||
struct TouchBlockState {
|
||||
|
||||
TouchBlockState()
|
||||
: mAllowedTouchBehaviorSet(false),
|
||||
mPreventDefault(false),
|
||||
mPreventDefaultSet(false),
|
||||
mSingleTapOccurred(false)
|
||||
{}
|
||||
|
||||
// Values of allowed touch behavior for touch points of this touch block.
|
||||
// Since there are maybe a few current active touch points per time (multitouch case)
|
||||
// and each touch point should have its own value of allowed touch behavior- we're
|
||||
// keeping an array of allowed touch behavior values, not the single value.
|
||||
nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
|
||||
|
||||
// Specifies whether mAllowedTouchBehaviors is set for this touch events block.
|
||||
bool mAllowedTouchBehaviorSet;
|
||||
|
||||
// Flag used to specify that content prevented the default behavior of this
|
||||
// touch events block.
|
||||
bool mPreventDefault;
|
||||
|
||||
// Specifies whether mPreventDefault property is set for this touch events block.
|
||||
bool mPreventDefaultSet;
|
||||
|
||||
// Specifies whether a single tap event was generated during this touch block.
|
||||
bool mSingleTapOccurred;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns whether current touch behavior values allow pinch-zooming.
|
||||
*/
|
||||
bool TouchActionAllowPinchZoom();
|
||||
|
||||
/*
|
||||
* Returns whether current touch behavior values allow double-tap-zooming.
|
||||
*/
|
||||
bool TouchActionAllowDoubleTapZoom();
|
||||
|
||||
/*
|
||||
* Returns allowed touch behavior from the mAllowedTouchBehavior array.
|
||||
* In case apzc didn't receive touch behavior values within the timeout
|
||||
* it returns default value.
|
||||
*/
|
||||
TouchBehaviorFlags GetTouchBehavior(uint32_t touchIndex);
|
||||
|
||||
/**
|
||||
* To move from the WAITING_CONTENT_RESPONSE state to TOUCHING one we need two
|
||||
* conditions set: get content listeners response (whether they called preventDefault)
|
||||
* and get allowed touch behaviors.
|
||||
* This method checks both conditions and changes (or not changes) state
|
||||
* appropriately.
|
||||
*/
|
||||
void CheckContentResponse();
|
||||
|
||||
/**
|
||||
* Helper to set the current state. Holds the monitor before actually setting
|
||||
* it and fires content controller events based on state changes. Always set
|
||||
@ -734,10 +644,6 @@ private:
|
||||
// in a FIFO manner.
|
||||
FrameMetrics mLastDispatchedPaintMetrics;
|
||||
|
||||
nsTArray<MultiTouchInput> mTouchQueue;
|
||||
|
||||
CancelableTask* mContentResponseTimeoutTask;
|
||||
|
||||
AxisX mX;
|
||||
AxisY mY;
|
||||
|
||||
@ -771,19 +677,98 @@ private:
|
||||
// ensures the last mozbrowserasyncscroll event is always been fired.
|
||||
CancelableTask* mAsyncScrollTimeoutTask;
|
||||
|
||||
// Flag used to determine whether or not we should try to enter the
|
||||
// WAITING_LISTENERS state. This is used in the case that we are processing a
|
||||
// queued up event block. If set, this means that we are handling this queue
|
||||
// and we don't want to queue the events back up again.
|
||||
bool mHandlingTouchQueue;
|
||||
|
||||
// Stores information about the current touch block.
|
||||
TouchBlockState mTouchBlockState;
|
||||
|
||||
nsRefPtr<AsyncPanZoomAnimation> mAnimation;
|
||||
|
||||
friend class Axis;
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to manage
|
||||
* blocks of touch events and the state needed to deal with content
|
||||
* listeners.
|
||||
*/
|
||||
public:
|
||||
/**
|
||||
* This function is invoked by the APZCTreeManager which in turn is invoked
|
||||
* by the widget when web content decides whether or not it wants to
|
||||
* cancel a block of events. This automatically gets applied to the next
|
||||
* block of events that has not yet been responded to. This function MUST
|
||||
* be invoked exactly once for each touch block.
|
||||
*/
|
||||
void ContentReceivedTouch(bool aPreventDefault);
|
||||
|
||||
/**
|
||||
* Sets allowed touch behavior for current touch session.
|
||||
* This method is invoked by the APZCTreeManager which in its turn invoked by
|
||||
* the widget after performing touch-action values retrieving.
|
||||
* Must be called after receiving the TOUCH_START even that started the
|
||||
* touch session.
|
||||
*/
|
||||
void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
|
||||
|
||||
private:
|
||||
void ScheduleContentResponseTimeout();
|
||||
void ContentResponseTimeout();
|
||||
/**
|
||||
* Processes any pending input blocks that are ready for processing. There
|
||||
* must be at least one input block in the queue when this function is called.
|
||||
*/
|
||||
void ProcessPendingInputBlocks();
|
||||
TouchBlockState* StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent);
|
||||
TouchBlockState* CurrentTouchBlock();
|
||||
bool HasReadyTouchBlock();
|
||||
|
||||
private:
|
||||
// The queue of touch blocks that have not yet been processed by this APZC.
|
||||
nsTArray<UniquePtr<TouchBlockState>> mTouchBlockQueue;
|
||||
|
||||
// This variable requires some explanation. Strap yourself in.
|
||||
//
|
||||
// For each block of events, we do two things: (1) send the events to gecko and expect
|
||||
// exactly one call to ContentReceivedTouch in return, and (2) kick off a timeout
|
||||
// that triggers in case we don't hear from web content in a timely fashion.
|
||||
// Since events are constantly coming in, we need to be able to handle more than one
|
||||
// block of input events sitting in the queue.
|
||||
//
|
||||
// There are ordering restrictions on events that we can take advantage of, and that
|
||||
// we need to abide by. Blocks of events in the queue will always be in the order that
|
||||
// the user generated them. Responses we get from content will be in the same order as
|
||||
// as the blocks of events in the queue. The timeout callbacks that have been posted
|
||||
// will also fire in the same order as the blocks of events in the queue.
|
||||
// HOWEVER, we may get multiple responses from content interleaved with multiple
|
||||
// timeout expirations, and that interleaving is not predictable.
|
||||
//
|
||||
// Therefore, we need to make sure that for each block of events, we process the queued
|
||||
// events exactly once, either when we get the response from content, or when the
|
||||
// timeout expires (whichever happens first). There is no way to associate the timeout
|
||||
// or response from content with a particular block of events other than via ordering.
|
||||
//
|
||||
// So, what we do to accomplish this is to track a "touch block balance", which is the
|
||||
// number of timeout expirations that have fired, minus the number of content responses
|
||||
// that have been received. (Think "balance" as in teeter-totter balance). This
|
||||
// value is:
|
||||
// - zero when we are in a state where the next content response we expect to receive
|
||||
// and the next timeout expiration we expect to fire both correspond to the next
|
||||
// unprocessed block of events in the queue.
|
||||
// - negative when we are in a state where we have received more content responses than
|
||||
// timeout expirations. This means that the next content repsonse we receive will
|
||||
// correspond to the first unprocessed block, but the next n timeout expirations need
|
||||
// to be ignored as they are for blocks we have already processed. (n is the absolute
|
||||
// value of the balance.)
|
||||
// - positive when we are in a state where we have received more timeout expirations
|
||||
// than content responses. This means that the next timeout expiration that we will
|
||||
// receive will correspond to the first unprocessed block, but the next n content
|
||||
// responses need to be ignored as they are for blocks we have already processed.
|
||||
// (n is the absolute value of the balance.)
|
||||
//
|
||||
// Note that each touch block internally carries flags that indicate whether or not it
|
||||
// has received a content response and/or timeout expiration. However, we cannot rely
|
||||
// on that alone to deliver these notifications to the right input block, because
|
||||
// once an input block has been processed, it can potentially be removed from the queue.
|
||||
// Therefore the information in that block is lost. An alternative approach would
|
||||
// be to keep around those blocks until they have received both the content response
|
||||
// and timeout expiration, but that involves a higher level of memory usage.
|
||||
int32_t mTouchBlockBalance;
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to manage
|
||||
|
219
gfx/layers/apz/src/TouchBlockState.cpp
Normal file
219
gfx/layers/apz/src/TouchBlockState.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TouchBlockState.h"
|
||||
#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
|
||||
#include "gfxPrefs.h" // for gfxPrefs
|
||||
|
||||
#define TBS_LOG(...)
|
||||
// #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* Default touch behavior (is used when no touch behavior is set).
|
||||
*/
|
||||
static const TouchBlockState::TouchBehaviorFlags kDefaultTouchBehavior =
|
||||
AllowedTouchBehavior::VERTICAL_PAN |
|
||||
AllowedTouchBehavior::HORIZONTAL_PAN |
|
||||
AllowedTouchBehavior::PINCH_ZOOM |
|
||||
AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
|
||||
|
||||
TouchBlockState::TouchBlockState()
|
||||
: mAllowedTouchBehaviorSet(false)
|
||||
, mPreventDefault(false)
|
||||
, mContentResponded(false)
|
||||
, mContentResponseTimerExpired(false)
|
||||
, mSingleTapOccurred(false)
|
||||
{
|
||||
TBS_LOG("Creating %p\n", this);
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::SetContentResponse(bool aPreventDefault)
|
||||
{
|
||||
if (mContentResponded) {
|
||||
return false;
|
||||
}
|
||||
TBS_LOG("%p got content response %d with timer expired %d\n",
|
||||
this, aPreventDefault, mContentResponseTimerExpired);
|
||||
if (!mContentResponseTimerExpired) {
|
||||
mPreventDefault = aPreventDefault;
|
||||
}
|
||||
mContentResponded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TimeoutContentResponse()
|
||||
{
|
||||
if (mContentResponseTimerExpired) {
|
||||
return false;
|
||||
}
|
||||
TBS_LOG("%p got content timer expired with response received %d\n",
|
||||
this, mContentResponded);
|
||||
if (!mContentResponded) {
|
||||
mPreventDefault = false;
|
||||
}
|
||||
mContentResponseTimerExpired = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
|
||||
{
|
||||
if (mAllowedTouchBehaviorSet) {
|
||||
return false;
|
||||
}
|
||||
TBS_LOG("%p got allowed touch behaviours for %d points\n", this, aBehaviors.Length());
|
||||
mAllowedTouchBehaviors.AppendElements(aBehaviors);
|
||||
mAllowedTouchBehaviorSet = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther)
|
||||
{
|
||||
TBS_LOG("%p copying allowed touch behaviours from %p\n", this, &aOther);
|
||||
MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet);
|
||||
return SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::IsReadyForHandling() const
|
||||
{
|
||||
// TODO: for long-tap blocks we probably don't need the touch behaviour?
|
||||
if (gfxPrefs::TouchActionEnabled() && !mAllowedTouchBehaviorSet) {
|
||||
return false;
|
||||
}
|
||||
if (!mContentResponded && !mContentResponseTimerExpired) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::IsDefaultPrevented() const
|
||||
{
|
||||
MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
|
||||
return mPreventDefault;
|
||||
}
|
||||
|
||||
void
|
||||
TouchBlockState::SetSingleTapOccurred()
|
||||
{
|
||||
TBS_LOG("%p setting single-tap occurred\n", this);
|
||||
mSingleTapOccurred = true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::SingleTapOccurred() const
|
||||
{
|
||||
return mSingleTapOccurred;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::HasEvents() const
|
||||
{
|
||||
return !mEvents.IsEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
TouchBlockState::AddEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TBS_LOG("%p adding event of type %d\n", this, aEvent.mType);
|
||||
mEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
TouchBlockState::DropEvents()
|
||||
{
|
||||
TBS_LOG("%p dropping %d events\n", this, mEvents.Length());
|
||||
mEvents.Clear();
|
||||
}
|
||||
|
||||
MultiTouchInput
|
||||
TouchBlockState::RemoveFirstEvent()
|
||||
{
|
||||
MOZ_ASSERT(!mEvents.IsEmpty());
|
||||
TBS_LOG("%p returning first of %d events\n", this, mEvents.Length());
|
||||
MultiTouchInput event = mEvents[0];
|
||||
mEvents.RemoveElementAt(0);
|
||||
return event;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TouchActionAllowsPinchZoom() const
|
||||
{
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
// Pointer events specification requires that all touch points allow zoom.
|
||||
for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TouchActionAllowsDoubleTapZoom() const
|
||||
{
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TouchActionAllowsPanningX() const
|
||||
{
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (mAllowedTouchBehaviors.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
|
||||
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TouchActionAllowsPanningY() const
|
||||
{
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (mAllowedTouchBehaviors.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
|
||||
return (flags & AllowedTouchBehavior::VERTICAL_PAN);
|
||||
}
|
||||
|
||||
bool
|
||||
TouchBlockState::TouchActionAllowsPanningXY() const
|
||||
{
|
||||
if (!gfxPrefs::TouchActionEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (mAllowedTouchBehaviors.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
|
||||
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
|
||||
&& (flags & AllowedTouchBehavior::VERTICAL_PAN);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
145
gfx/layers/apz/src/TouchBlockState.h
Normal file
145
gfx/layers/apz/src/TouchBlockState.h
Normal file
@ -0,0 +1,145 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_TouchBlockState_h
|
||||
#define mozilla_layers_TouchBlockState_h
|
||||
|
||||
#include "nsTArray.h" // for nsTArray
|
||||
#include "InputData.h" // for MultiTouchInput
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* This class represents a single touch block. A touch block is
|
||||
* a set of touch events that can be cancelled by web content via
|
||||
* touch event listeners.
|
||||
*
|
||||
* Every touch-start event creates a new touch block. In this case, the
|
||||
* touch block consists of the touch-start, followed by all touch events
|
||||
* up to but not including the next touch-start (except in the case where
|
||||
* a long-tap happens, see below). Note that in particular we cannot know
|
||||
* when a touch block ends until the next one is started. Most touch
|
||||
* blocks are created by receipt of a touch-start event.
|
||||
*
|
||||
* Every long-tap event also creates a new touch block, since it can also
|
||||
* be consumed by web content. In this case, when the long-tap event is
|
||||
* dispatched to web content, a new touch block is started to hold the remaining
|
||||
* touch events, up to but not including the next touch start (or long-tap).
|
||||
*
|
||||
* Conceptually, each touch block can be cancelled by web content, and
|
||||
* this information is stored in the mPreventDefault flag. Because web
|
||||
* content runs on the Gecko main thread, we cannot always wait for web content's
|
||||
* response. Instead, there is a timeout that sets this flag in the case
|
||||
* where web content doesn't respond in time. The mContentResponded
|
||||
* and mContentResponseTimerExpired flags indicate which of these scenarios
|
||||
* occurred.
|
||||
*
|
||||
* Additionally, if touch-action is enabled, each touch block should
|
||||
* have a set of allowed touch behavior flags; one for each touch point.
|
||||
* This also requires running code on the Gecko main thread, and so may
|
||||
* be populated with some latency. The mAllowedTouchBehaviorSet and
|
||||
* mAllowedTouchBehaviors variables track this information.
|
||||
*/
|
||||
class TouchBlockState
|
||||
{
|
||||
public:
|
||||
typedef uint32_t TouchBehaviorFlags;
|
||||
|
||||
TouchBlockState();
|
||||
|
||||
/**
|
||||
* Record whether or not content cancelled this block of events.
|
||||
* @param aPreventDefault true iff the block is cancelled.
|
||||
* @return false if this block has already received a response from
|
||||
* web content, true if not.
|
||||
*/
|
||||
bool SetContentResponse(bool aPreventDefault);
|
||||
/**
|
||||
* Record that content didn't respond in time.
|
||||
* @return false if this block already timed out, true if not.
|
||||
*/
|
||||
bool TimeoutContentResponse();
|
||||
/**
|
||||
* Set the allowed touch behavior flags for this block.
|
||||
* @return false if this block already has these flags set, true if not.
|
||||
*/
|
||||
bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
|
||||
/**
|
||||
* Copy the allowed touch behavior flags from another block.
|
||||
* @return false if this block already has these flags set, true if not.
|
||||
*/
|
||||
bool CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther);
|
||||
|
||||
/**
|
||||
* @return true iff this block has received all the information needed
|
||||
* to properly dispatch the events in the block.
|
||||
*/
|
||||
bool IsReadyForHandling() const;
|
||||
/**
|
||||
* @return true iff web content cancelled this block of events.
|
||||
*/
|
||||
bool IsDefaultPrevented() const;
|
||||
|
||||
/**
|
||||
* Set a flag that indicates that this touch block triggered a single tap event.
|
||||
*/
|
||||
void SetSingleTapOccurred();
|
||||
/**
|
||||
* @return true iff SetSingleTapOccurred was previously called on this block.
|
||||
*/
|
||||
bool SingleTapOccurred() const;
|
||||
|
||||
/**
|
||||
* @return true iff there are pending events in this touch block.
|
||||
*/
|
||||
bool HasEvents() const;
|
||||
/**
|
||||
* Add a new touch event to the queue of events in this input block.
|
||||
*/
|
||||
void AddEvent(const MultiTouchInput& aEvent);
|
||||
/**
|
||||
* Throw away all the events in this input block.
|
||||
*/
|
||||
void DropEvents();
|
||||
/**
|
||||
* @return the first event in the queue. The event is removed from the queue
|
||||
* before it is returned.
|
||||
*/
|
||||
MultiTouchInput RemoveFirstEvent();
|
||||
|
||||
/**
|
||||
* @return false iff touch-action is enabled and the allowed touch behaviors for
|
||||
* this touch block do not allow pinch-zooming.
|
||||
*/
|
||||
bool TouchActionAllowsPinchZoom() const;
|
||||
/**
|
||||
* @return false iff touch-action is enabled and the allowed touch behaviors for
|
||||
* this touch block do not allow double-tap zooming.
|
||||
*/
|
||||
bool TouchActionAllowsDoubleTapZoom() const;
|
||||
/**
|
||||
* @return false iff touch-action is enabled and the allowed touch behaviors for
|
||||
* the first touch point do not allow panning in the specified direction(s).
|
||||
*/
|
||||
bool TouchActionAllowsPanningX() const;
|
||||
bool TouchActionAllowsPanningY() const;
|
||||
bool TouchActionAllowsPanningXY() const;
|
||||
|
||||
private:
|
||||
nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
|
||||
bool mAllowedTouchBehaviorSet;
|
||||
bool mPreventDefault;
|
||||
bool mContentResponded;
|
||||
bool mContentResponseTimerExpired;
|
||||
bool mSingleTapOccurred;
|
||||
nsTArray<MultiTouchInput> mEvents;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_TouchBlockState_h
|
@ -235,6 +235,7 @@ UNIFIED_SOURCES += [
|
||||
'apz/src/Axis.cpp',
|
||||
'apz/src/GestureEventListener.cpp',
|
||||
'apz/src/TaskThrottler.cpp',
|
||||
'apz/src/TouchBlockState.cpp',
|
||||
'apz/testutil/APZTestData.cpp',
|
||||
'apz/util/ActiveElementManager.cpp',
|
||||
'apz/util/APZCCallbackHelper.cpp',
|
||||
|
@ -349,10 +349,10 @@ ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
|
||||
ApzcPan(aApzc, aTreeManager, aTime, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses);
|
||||
|
||||
nsEventStatus touchStartStatus;
|
||||
if (hasTouchListeners) {
|
||||
if (hasTouchListeners || gfxPrefs::TouchActionEnabled()) {
|
||||
// APZC shouldn't consume the start event now, instead queueing it up
|
||||
// waiting for content's response.
|
||||
touchStartStatus = nsEventStatus_eIgnore;
|
||||
// waiting for content's response and/or allowed behavior.
|
||||
touchStartStatus = nsEventStatus_eConsumeDoDefault;
|
||||
} else {
|
||||
// APZC should go into the touching state and therefore consume the event.
|
||||
touchStartStatus = nsEventStatus_eConsumeNoDefault;
|
||||
@ -360,7 +360,10 @@ ApzcPanAndCheckStatus(AsyncPanZoomController* aApzc,
|
||||
EXPECT_EQ(touchStartStatus, statuses[0]);
|
||||
|
||||
nsEventStatus touchMoveStatus;
|
||||
if (expectIgnoredPan) {
|
||||
if (hasTouchListeners) {
|
||||
// APZC will queue up this event while waiting for content's response.
|
||||
touchMoveStatus = nsEventStatus_eConsumeDoDefault;
|
||||
} else if (expectIgnoredPan) {
|
||||
// APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
|
||||
// The same applies to all consequent touch move events.
|
||||
touchMoveStatus = nsEventStatus_eIgnore;
|
||||
@ -1036,12 +1039,23 @@ protected:
|
||||
int time = 0;
|
||||
|
||||
nsEventStatus status = ApzcDown(apzc, 10, 10, time);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
// If touch-action is enabled, then the event is queued until the
|
||||
// allowed touch behavior is set.
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
} else {
|
||||
// Otherwise, it is processed immediately.
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
}
|
||||
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
|
||||
}
|
||||
// Have content "respond" to the touchstart
|
||||
apzc->ContentReceivedTouch(false);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
|
||||
@ -1057,6 +1071,7 @@ protected:
|
||||
EXPECT_CALL(check, Call("postHandleLongTapUp"));
|
||||
}
|
||||
|
||||
// There is a longpress event scheduled on a timeout
|
||||
mcc->CheckHasDelayedTask();
|
||||
|
||||
// Manually invoke the longpress while the touch is currently down.
|
||||
@ -1066,24 +1081,23 @@ protected:
|
||||
|
||||
// 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.
|
||||
|
||||
// Dispatching the longpress event starts a new touch block, which
|
||||
// needs a new content response and also has a pending timeout task
|
||||
// in the queue. Deal with those here. We do the content response first
|
||||
// with preventDefault=false, and then we run the timeout task which
|
||||
// "loses the race" and does nothing.
|
||||
apzc->ContentReceivedTouch(false);
|
||||
mcc->CheckHasDelayedTask();
|
||||
mcc->ClearDelayedTask();
|
||||
apzc->ContentReceivedTouch(true);
|
||||
mcc->RunDelayedTask();
|
||||
|
||||
time += 1000;
|
||||
|
||||
// Finally, simulate lifting the finger. Since the long-press wasn't
|
||||
// prevent-defaulted, we should get a long-tap-up event.
|
||||
check.Call("preHandleLongTapUp");
|
||||
status = ApzcUp(apzc, 10, 10, time);
|
||||
EXPECT_EQ(nsEventStatus_eIgnore, status);
|
||||
|
||||
// To get a LongTapUp event, we must kick APZC to flush its event queue. This
|
||||
// would normally happen if we had a (Tab|RenderFrame)(Parent|Child)
|
||||
// mechanism.
|
||||
check.Call("preHandleLongTapUp");
|
||||
apzc->ContentReceivedTouch(false);
|
||||
check.Call("postHandleLongTapUp");
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
@ -1101,12 +1115,23 @@ protected:
|
||||
|
||||
int time = 0;
|
||||
nsEventStatus status = ApzcDown(apzc, touchX, touchStartY, time);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
// If touch-action is enabled, then the event is queued until the
|
||||
// allowed touch behavior is set.
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
} else {
|
||||
// Otherwise, it is processed immediately.
|
||||
EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
|
||||
}
|
||||
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
|
||||
}
|
||||
// Have content "respond" to the touchstart
|
||||
apzc->ContentReceivedTouch(false);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
|
||||
@ -1127,11 +1152,15 @@ protected:
|
||||
|
||||
// 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.
|
||||
mcc->ClearDelayedTask();
|
||||
|
||||
// There should be a TimeoutContentResponse task in the queue still,
|
||||
// waiting for the response from the longtap event dispatched above.
|
||||
// Send the signal that content has handled the long-tap, and then run
|
||||
// the timeout task (it will be a no-op because the content "wins" the
|
||||
// race. This takes the place of the "contextmenu" event.
|
||||
apzc->ContentReceivedTouch(true);
|
||||
mcc->CheckHasDelayedTask();
|
||||
mcc->RunDelayedTask();
|
||||
|
||||
time += 1000;
|
||||
|
||||
@ -1144,11 +1173,6 @@ protected:
|
||||
status = ApzcUp(apzc, touchX, touchEndY, time);
|
||||
EXPECT_EQ(nsEventStatus_eIgnore, status);
|
||||
|
||||
// Flush the event queue. Once the "contextmenu" event is handled, any touch
|
||||
// events that come from the same series of start->n*move->end events should
|
||||
// be discarded, even if only the "contextmenu" event is preventDefaulted.
|
||||
apzc->ContentReceivedTouch(false);
|
||||
|
||||
ScreenPoint pointOut;
|
||||
ViewTransform viewTransformOut;
|
||||
apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
|
||||
|
Loading…
Reference in New Issue
Block a user