Bug 795567 - Part 4: Rework apzc to wait for touch-action value from content as it waits for preventDefault value from listeners. r=kats

This commit is contained in:
Nick Lebedev 2014-01-15 10:03:15 -05:00
Родитель 1651d2c5bb
Коммит a9da19067c
2 изменённых файлов: 96 добавлений и 51 удалений

Просмотреть файл

@ -217,12 +217,12 @@ static const CSSToScreenScale MAX_ZOOM(8.0f);
static const CSSToScreenScale MIN_ZOOM(0.125f);
/**
* Amount of time before we timeout touch event listeners. For example, if
* Amount of time before we timeout response from content. For example, if
* content is being unruly/slow and we don't get a response back within this
* time, we will just pretend that content did not preventDefault any touch
* events we dispatched to it.
*/
static int gTouchListenerTimeout = 300;
static int gContentResponseTimeout = 300;
/**
* Number of samples to store of how long it took to paint after the previous
@ -385,7 +385,7 @@ AsyncPanZoomController::InitializeGlobalState()
Preferences::AddIntVarCache(&gPanRepaintInterval, "apz.pan_repaint_interval", gPanRepaintInterval);
Preferences::AddIntVarCache(&gFlingRepaintInterval, "apz.fling_repaint_interval", gFlingRepaintInterval);
Preferences::AddFloatVarCache(&gMinSkateSpeed, "apz.min_skate_speed", gMinSkateSpeed);
Preferences::AddIntVarCache(&gTouchListenerTimeout, "apz.touch_listener_timeout", gTouchListenerTimeout);
Preferences::AddIntVarCache(&gContentResponseTimeout, "apz.content_response_timeout", gContentResponseTimeout);
Preferences::AddIntVarCache(&gNumPaintDurationSamples, "apz.num_paint_duration_samples", gNumPaintDurationSamples);
Preferences::AddFloatVarCache(&gTouchStartTolerance, "apz.touch_start_tolerance", gTouchStartTolerance);
Preferences::AddFloatVarCache(&gXSkateSizeMultiplier, "apz.x_skate_size_multiplier", gXSkateSizeMultiplier);
@ -415,7 +415,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
mRefPtrMonitor("RefPtrMonitor"),
mMonitor("AsyncPanZoomController"),
mTouchActionPropertyEnabled(gTouchActionPropertyEnabled),
mTouchListenerTimeoutTask(nullptr),
mContentResponseTimeoutTask(nullptr),
mX(MOZ_THIS_IN_INITIALIZER_LIST()),
mY(MOZ_THIS_IN_INITIALIZER_LIST()),
mPanDirRestricted(false),
@ -427,6 +427,9 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
mCurrentAsyncScrollOffset(0, 0),
mAsyncScrollTimeoutTask(nullptr),
mHandlingTouchQueue(false),
mAllowedTouchBehaviorSet(false),
mPreventDefault(false),
mPreventDefaultSet(false),
mTreeManager(aTreeManager),
mAPZCId(sAsyncPanZoomControllerCount++),
mSharedFrameMetricsBuffer(nullptr),
@ -501,30 +504,35 @@ AsyncPanZoomController::GetTouchStartTolerance()
}
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
// If we may have touch listeners, we enable the machinery that allows touch
// listeners to preventDefault any touch inputs. This should not happen unless
// there are actually touch listeners 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 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 && aEvent.mInputType == MULTITOUCH_INPUT &&
(mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
SetState(WAITING_LISTENERS);
mAllowedTouchBehaviors.Clear();
mAllowedTouchBehaviorSet = false;
mPreventDefault = false;
mPreventDefaultSet = false;
SetState(WAITING_CONTENT_RESPONSE);
}
}
if (mState == WAITING_LISTENERS || mHandlingTouchQueue) {
if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
if (aEvent.mInputType == MULTITOUCH_INPUT) {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
mTouchQueue.AppendElement(multiTouchInput);
if (!mTouchListenerTimeoutTask) {
mTouchListenerTimeoutTask =
NewRunnableMethod(this, &AsyncPanZoomController::TimeoutTouchListeners);
if (!mContentResponseTimeoutTask) {
mContentResponseTimeoutTask =
NewRunnableMethod(this, &AsyncPanZoomController::TimeoutContentResponse);
PostDelayedTask(mTouchListenerTimeoutTask, gTouchListenerTimeout);
PostDelayedTask(mContentResponseTimeoutTask, gContentResponseTimeout);
}
}
return nsEventStatus_eIgnore;
@ -616,7 +624,7 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
case PINCHING:
case WAITING_LISTENERS:
case WAITING_CONTENT_RESPONSE:
NS_WARNING("Received impossible touch in OnTouchStart");
break;
default:
@ -676,7 +684,7 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
return nsEventStatus_eIgnore;
case WAITING_LISTENERS:
case WAITING_CONTENT_RESPONSE:
NS_WARNING("Received impossible touch in OnTouchMove");
break;
}
@ -734,7 +742,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
return nsEventStatus_eIgnore;
case WAITING_LISTENERS:
case WAITING_CONTENT_RESPONSE:
NS_WARNING("Received impossible touch in OnTouchEnd");
break;
}
@ -1668,25 +1676,40 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
}
void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
if (!mFrameMetrics.mMayHaveTouchListeners) {
mTouchQueue.Clear();
mPreventDefaultSet = true;
mPreventDefault = aPreventDefault;
CheckContentResponse();
}
void AsyncPanZoomController::CheckContentResponse() {
bool canProceedToTouchState = true;
if (mFrameMetrics.mMayHaveTouchListeners) {
canProceedToTouchState &= mPreventDefaultSet;
}
if (mTouchActionPropertyEnabled) {
canProceedToTouchState &= mAllowedTouchBehaviorSet;
}
if (!canProceedToTouchState) {
return;
}
if (mTouchListenerTimeoutTask) {
mTouchListenerTimeoutTask->Cancel();
mTouchListenerTimeoutTask = nullptr;
if (mContentResponseTimeoutTask) {
mContentResponseTimeoutTask->Cancel();
mContentResponseTimeoutTask = nullptr;
}
if (mState == WAITING_LISTENERS) {
if (!aPreventDefault) {
if (mState == WAITING_CONTENT_RESPONSE) {
if (!mPreventDefault) {
SetState(NOTHING);
}
mHandlingTouchQueue = true;
while (!mTouchQueue.IsEmpty()) {
if (!aPreventDefault) {
if (!mPreventDefault) {
HandleInputEvent(mTouchQueue[0]);
}
@ -1722,6 +1745,8 @@ AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
mAllowedTouchBehaviors.Clear();
mAllowedTouchBehaviors.AppendElements(aBehaviors);
mAllowedTouchBehaviorSet = true;
CheckContentResponse();
}
void AsyncPanZoomController::SetState(PanZoomState aNewState) {
@ -1747,15 +1772,15 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
}
bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_LISTENERS);
return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
}
bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
}
void AsyncPanZoomController::TimeoutTouchListeners() {
mTouchListenerTimeoutTask = nullptr;
void AsyncPanZoomController::TimeoutContentResponse() {
mContentResponseTimeoutTask = nullptr;
ContentReceivedTouch(false);
}

Просмотреть файл

@ -499,13 +499,13 @@ protected:
const FrameMetrics& GetFrameMetrics();
/**
* Timeout function for touch listeners. This should be called on a timer
* 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
* have touch listeners. If a notification comes indicating whether or not
* content preventDefaulted a series of touch events before the timeout, the
* timeout should be cancelled.
* 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 TimeoutTouchListeners();
void TimeoutContentResponse();
/**
* Timeout function for mozbrowserasyncscroll event. Because we throttle
@ -517,24 +517,25 @@ protected:
private:
enum PanZoomState {
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
TOUCHING, /* one touch-start event received */
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
TOUCHING, /* one touch-start event received */
PANNING, /* panning the frame */
PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
PANNING_LOCKED_Y, /* as above for Y axis */
PANNING, /* panning the frame */
PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
PANNING_LOCKED_Y, /* as above for Y axis */
CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture
on a vertically-scrollable view. This used for the
Windows Metro "cross-slide" gesture. */
CROSS_SLIDING_Y, /* as above for Y axis */
CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture
on a vertically-scrollable view. This used for the
Windows Metro "cross-slide" gesture. */
CROSS_SLIDING_Y, /* as above for Y axis */
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATING_ZOOM, /* animated zoom to a new rect */
WAITING_LISTENERS, /* 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. we still need to abort animations. */
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. */
};
/*
@ -544,6 +545,15 @@ private:
*/
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
@ -621,7 +631,7 @@ private:
nsTArray<MultiTouchInput> mTouchQueue;
CancelableTask* mTouchListenerTimeoutTask;
CancelableTask* mContentResponseTimeoutTask;
AxisX mX;
AxisY mY;
@ -674,6 +684,16 @@ private:
// keeping an array of allowed touch behavior values, not the single value.
nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
// Specifies whether mAllowedTouchBehaviors is set for current touch events block.
bool mAllowedTouchBehaviorSet;
// Flag used to specify that content prevented the default behavior of the current
// touch events block.
bool mPreventDefault;
// Specifies whether mPreventDefault property is set for current touch events block.
bool mPreventDefaultSet;
RefPtr<AsyncPanZoomAnimation> mAnimation;
friend class Axis;