mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 18:08:58 +00:00
Bug 1083395 - Extract an InputQueue class from the APZC input code. r=botond
This commit is contained in:
parent
2489aa5b2e
commit
541e8f016d
@ -16,6 +16,7 @@
|
||||
#include "GestureEventListener.h" // for GestureEventListener
|
||||
#include "InputData.h" // for MultiTouchInput, etc
|
||||
#include "InputBlockState.h" // for InputBlockState, TouchBlockState
|
||||
#include "InputQueue.h" // for InputQueue
|
||||
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
|
||||
#include "TaskThrottler.h" // for TaskThrottler
|
||||
#include "Units.h" // for CSSRect, CSSPoint, etc
|
||||
@ -885,7 +886,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
||||
mAsyncScrollTimeoutTask(nullptr),
|
||||
mState(NOTHING),
|
||||
mNotificationBlockers(0),
|
||||
mTouchBlockBalance(0),
|
||||
mInputQueue(new InputQueue()),
|
||||
mTreeManager(aTreeManager),
|
||||
mAPZCId(sAsyncPanZoomControllerCount++),
|
||||
mSharedLock(nullptr),
|
||||
@ -926,6 +927,13 @@ AsyncPanZoomController::GetGestureEventListener() const {
|
||||
return listener.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<InputQueue>
|
||||
AsyncPanZoomController::GetInputQueue() const {
|
||||
MonitorAutoLock lock(mRefPtrMonitor);
|
||||
MOZ_ASSERT(mInputQueue);
|
||||
return mInputQueue;
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::Destroy()
|
||||
{
|
||||
@ -933,12 +941,11 @@ AsyncPanZoomController::Destroy()
|
||||
|
||||
CancelAnimation();
|
||||
|
||||
mTouchBlockQueue.Clear();
|
||||
|
||||
{ // scope the lock
|
||||
MonitorAutoLock lock(mRefPtrMonitor);
|
||||
mGeckoContentController = nullptr;
|
||||
mGestureEventListener = nullptr;
|
||||
mInputQueue = nullptr; // XXX this is temporary and will be removed in a future patch
|
||||
}
|
||||
mPrevSibling = nullptr;
|
||||
mLastChild = nullptr;
|
||||
@ -1007,76 +1014,7 @@ AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
|
||||
AssertOnControllerThread();
|
||||
|
||||
if (aEvent.mInputType != MULTITOUCH_INPUT) {
|
||||
HandleInputEvent(aEvent);
|
||||
// The return value for non-touch input isn't really used, so just return
|
||||
// ConsumeDoDefault for now. This can be changed later if needed.
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
TouchBlockState* block = nullptr;
|
||||
if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
block = StartNewTouchBlock(false);
|
||||
APZC_LOG("%p started new touch block %p\n", this, block);
|
||||
|
||||
// We want to cancel animations here as soon as possible (i.e. without waiting for
|
||||
// content responses) because a finger has gone down and we don't want to keep moving
|
||||
// the content under the finger. However, to prevent "future" touchstart events from
|
||||
// interfering with "past" animations (i.e. from a previous touch block that is still
|
||||
// being processed) we only do this animation-cancellation if there are no older
|
||||
// touch blocks still in the queue.
|
||||
if (block == CurrentTouchBlock()) {
|
||||
if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
|
||||
// If we're already in a fast fling, then we want the touch event to stop the fling
|
||||
// and to disallow the touch event from being used as part of a fling.
|
||||
block->DisallowSingleTap();
|
||||
}
|
||||
block->GetOverscrollHandoffChain()->CancelAnimations();
|
||||
}
|
||||
|
||||
if (NeedToWaitForContent()) {
|
||||
// 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 (!block) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus result = ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
|
||||
? nsEventStatus_eConsumeDoDefault
|
||||
: nsEventStatus_eIgnore;
|
||||
|
||||
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 result;
|
||||
}
|
||||
HandleInputEvent(aEvent);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Otherwise, add it to the queue for the touch block
|
||||
block->AddEvent(aEvent.AsMultiTouchInput());
|
||||
return result;
|
||||
return GetInputQueue()->ReceiveInputEvent(this, aEvent);
|
||||
}
|
||||
|
||||
nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
|
||||
@ -1534,7 +1472,7 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
|
||||
CancelAnimation();
|
||||
}
|
||||
|
||||
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
|
||||
mPanGestureState = MakeUnique<InputBlockState>(this);
|
||||
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
|
||||
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
|
||||
@ -1613,7 +1551,7 @@ nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput&
|
||||
CancelAnimation();
|
||||
}
|
||||
|
||||
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
|
||||
mPanGestureState = MakeUnique<InputBlockState>(this);
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -1641,8 +1579,7 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
StartNewTouchBlock(true);
|
||||
ScheduleContentResponseTimeout();
|
||||
GetInputQueue()->InjectNewTouchBlock(this);
|
||||
controller->HandleLongTap(geckoScreenPoint, modifiers, GetGuid());
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -2917,115 +2854,14 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::ScheduleContentResponseTimeout() {
|
||||
APZC_LOG("%p scheduling content response timeout\n", this);
|
||||
PostDelayedTask(
|
||||
NewRunnableMethod(this, &AsyncPanZoomController::ContentResponseTimeout),
|
||||
gfxPrefs::APZContentResponseTimeout());
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::ContentResponseTimeout() {
|
||||
AssertOnControllerThread();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("APZC received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
|
||||
AssertOnControllerThread();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
GetInputQueue()->ContentReceivedTouch(aPreventDefault);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
AssertOnControllerThread();
|
||||
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("APZC received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::ProcessPendingInputBlocks() {
|
||||
AssertOnControllerThread();
|
||||
|
||||
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()) {
|
||||
curBlock->DropEvents();
|
||||
ResetInputState();
|
||||
} 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);
|
||||
}
|
||||
GetInputQueue()->SetAllowedTouchBehavior(aBehaviors);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -3034,6 +2870,12 @@ AsyncPanZoomController::NeedToWaitForContent() const
|
||||
return (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret());
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
AsyncPanZoomController::CurrentTouchBlock()
|
||||
{
|
||||
return GetInputQueue()->CurrentTouchBlock();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::ResetInputState()
|
||||
{
|
||||
@ -3046,43 +2888,10 @@ AsyncPanZoomController::ResetInputState()
|
||||
}
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
AsyncPanZoomController::StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent)
|
||||
{
|
||||
TouchBlockState* newBlock = new TouchBlockState(BuildOverscrollHandoffChain());
|
||||
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].get());
|
||||
mTouchBlockQueue.RemoveElementAt(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new block to the queue.
|
||||
mTouchBlockQueue.AppendElement(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
AsyncPanZoomController::CurrentTouchBlock()
|
||||
{
|
||||
AssertOnControllerThread();
|
||||
|
||||
MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
|
||||
return mTouchBlockQueue[0].get();
|
||||
}
|
||||
|
||||
bool
|
||||
AsyncPanZoomController::HasReadyTouchBlock()
|
||||
{
|
||||
return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
|
||||
return GetInputQueue()->HasReadyTouchBlock();
|
||||
}
|
||||
|
||||
AsyncPanZoomController::TouchBehaviorFlags
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "InputData.h"
|
||||
#include "Axis.h"
|
||||
#include "InputQueue.h"
|
||||
#include "TaskThrottler.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "nsRegion.h"
|
||||
@ -583,15 +584,6 @@ protected:
|
||||
*/
|
||||
void FireAsyncScrollOnTimeout();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Given the number of touch points in an input event and touch block they
|
||||
* belong to, check if the event can result in a panning/zooming behavior.
|
||||
* This is primarily used to figure out when to dispatch the pointercancel
|
||||
* event for the pointer events spec.
|
||||
*/
|
||||
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
|
||||
|
||||
/**
|
||||
* Convert ScreenPoint relative to this APZC to CSSPoint relative
|
||||
* to the parent document. This excludes the transient compositor transform.
|
||||
@ -638,6 +630,7 @@ private:
|
||||
/* Utility functions that return a addrefed pointer to the corresponding fields. */
|
||||
already_AddRefed<GeckoContentController> GetGeckoContentController() const;
|
||||
already_AddRefed<GestureEventListener> GetGestureEventListener() const;
|
||||
nsRefPtr<InputQueue> GetInputQueue() const;
|
||||
|
||||
// If we are sharing our frame metrics with content across processes
|
||||
bool mSharingFrameMetricsAcrossProcesses;
|
||||
@ -781,20 +774,12 @@ private:
|
||||
*/
|
||||
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.
|
||||
* See InputQueue::ContentReceivedTouch
|
||||
*/
|
||||
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.
|
||||
* See InputQueue::SetAllowedTouchBehavior
|
||||
*/
|
||||
void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
|
||||
|
||||
@ -804,74 +789,29 @@ public:
|
||||
*/
|
||||
void FlushRepaintForNewInputBlock();
|
||||
|
||||
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.
|
||||
* Given the number of touch points in an input event and touch block they
|
||||
* belong to, check if the event can result in a panning/zooming behavior.
|
||||
* This is primarily used to figure out when to dispatch the pointercancel
|
||||
* event for the pointer events spec.
|
||||
*/
|
||||
bool ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints);
|
||||
|
||||
/**
|
||||
* Return true if there are are touch listeners registered on content
|
||||
* scrolled by this APZC.
|
||||
*/
|
||||
void ProcessPendingInputBlocks();
|
||||
TouchBlockState* StartNewTouchBlock(bool aCopyAllowedTouchBehaviorFromCurrent);
|
||||
TouchBlockState* CurrentTouchBlock();
|
||||
bool HasReadyTouchBlock();
|
||||
bool NeedToWaitForContent() const;
|
||||
|
||||
/**
|
||||
* Clear internal state relating to input handling.
|
||||
*/
|
||||
void ResetInputState();
|
||||
|
||||
private:
|
||||
// The queue of touch blocks that have not yet been processed by this APZC.
|
||||
// This member must only be accessed on the controller/UI thread.
|
||||
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.
|
||||
//
|
||||
// This member must only be accessed on the controller/UI thread.
|
||||
int32_t mTouchBlockBalance;
|
||||
nsRefPtr<InputQueue> mInputQueue;
|
||||
TouchBlockState* CurrentTouchBlock();
|
||||
bool HasReadyTouchBlock();
|
||||
|
||||
|
||||
/* ===================================================================
|
||||
@ -1030,6 +970,27 @@ public:
|
||||
*/
|
||||
bool SnapBackIfOverscrolled();
|
||||
|
||||
/**
|
||||
* Build the chain of APZCs along which scroll will be handed off when
|
||||
* this APZC receives input events.
|
||||
*
|
||||
* Notes on lifetime and const-correctness:
|
||||
* - The returned handoff chain is |const|, to indicate that it cannot be
|
||||
* changed after being built.
|
||||
* - When passing the chain to a function that uses it without storing it,
|
||||
* pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
|
||||
* - When storing the chain, store it by RefPtr-to-const (as in
|
||||
* |nsRefPtr<const OverscrollHandoffChain>|). This ensures the chain is
|
||||
* kept alive. Note that queueing a task that uses the chain as an
|
||||
* argument constitutes storing, as the task may outlive its queuer.
|
||||
* - When passing the chain to a function that will store it, pass it as
|
||||
* |const nsRefPtr<const OverscrollHandoffChain>&|. This allows the
|
||||
* function to copy it into the |nsRefPtr<const OverscrollHandoffChain>|
|
||||
* that will store it, while avoiding an unnecessary copy (and thus
|
||||
* AddRef() and Release()) when passing it.
|
||||
*/
|
||||
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
|
||||
|
||||
private:
|
||||
/**
|
||||
* A helper function for calling APZCTreeManager::DispatchScroll().
|
||||
@ -1056,26 +1017,6 @@ private:
|
||||
*/
|
||||
bool OverscrollBy(const ScreenPoint& aOverscroll);
|
||||
|
||||
/**
|
||||
* Build the chain of APZCs along which scroll will be handed off when
|
||||
* this APZC receives input events.
|
||||
*
|
||||
* Notes on lifetime and const-correctness:
|
||||
* - The returned handoff chain is |const|, to indicate that it cannot be
|
||||
* changed after being built.
|
||||
* - When passing the chain to a function that uses it without storing it,
|
||||
* pass it by reference-to-const (as in |const OverscrollHandoffChain&|).
|
||||
* - When storing the chain, store it by RefPtr-to-const (as in
|
||||
* |nsRefPtr<const OverscrollHandoffChain>|). This ensures the chain is
|
||||
* kept alive. Note that queueing a task that uses the chain as an
|
||||
* argument constitutes storing, as the task may outlive its queuer.
|
||||
* - When passing the chain to a function that will store it, pass it as
|
||||
* |const nsRefPtr<const OverscrollHandoffChain>&|. This allows the
|
||||
* function to copy it into the |nsRefPtr<const OverscrollHandoffChain>|
|
||||
* that will store it, while avoiding an unnecessary copy (and thus
|
||||
* AddRef() and Release()) when passing it.
|
||||
*/
|
||||
nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain();
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to maintain the
|
||||
|
@ -15,11 +15,18 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
InputBlockState::InputBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain)
|
||||
: mOverscrollHandoffChain(aOverscrollHandoffChain)
|
||||
InputBlockState::InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
|
||||
: mTargetApzc(aTargetApzc)
|
||||
{
|
||||
// We should never be constructed with a nullptr handoff chain.
|
||||
MOZ_ASSERT(mOverscrollHandoffChain);
|
||||
// We should never be constructed with a nullptr target.
|
||||
MOZ_ASSERT(mTargetApzc);
|
||||
mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
|
||||
}
|
||||
|
||||
const nsRefPtr<AsyncPanZoomController>&
|
||||
InputBlockState::GetTargetApzc() const
|
||||
{
|
||||
return mTargetApzc;
|
||||
}
|
||||
|
||||
const nsRefPtr<const OverscrollHandoffChain>&
|
||||
@ -28,8 +35,8 @@ InputBlockState::GetOverscrollHandoffChain() const
|
||||
return mOverscrollHandoffChain;
|
||||
}
|
||||
|
||||
TouchBlockState::TouchBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain)
|
||||
: InputBlockState(aOverscrollHandoffChain)
|
||||
TouchBlockState::TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc)
|
||||
: InputBlockState(aTargetApzc)
|
||||
, mAllowedTouchBehaviorSet(false)
|
||||
, mPreventDefault(false)
|
||||
, mContentResponded(false)
|
||||
|
@ -23,10 +23,13 @@ class OverscrollHandoffChain;
|
||||
class InputBlockState
|
||||
{
|
||||
public:
|
||||
explicit InputBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain);
|
||||
explicit InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
|
||||
|
||||
const nsRefPtr<AsyncPanZoomController>& GetTargetApzc() const;
|
||||
const nsRefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncPanZoomController> mTargetApzc;
|
||||
nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
|
||||
};
|
||||
|
||||
@ -66,7 +69,7 @@ class TouchBlockState : public InputBlockState
|
||||
public:
|
||||
typedef uint32_t TouchBehaviorFlags;
|
||||
|
||||
explicit TouchBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain);
|
||||
explicit TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
|
||||
|
||||
/**
|
||||
* Record whether or not content cancelled this block of events.
|
||||
|
263
gfx/layers/apz/src/InputQueue.cpp
Normal file
263
gfx/layers/apz/src/InputQueue.cpp
Normal file
@ -0,0 +1,263 @@
|
||||
/* -*- 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 "InputQueue.h"
|
||||
|
||||
#include "AsyncPanZoomController.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "InputBlockState.h"
|
||||
#include "OverscrollHandoffState.h"
|
||||
|
||||
#define INPQ_LOG(...)
|
||||
// #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
InputQueue::InputQueue()
|
||||
: mTouchBlockBalance(0)
|
||||
{
|
||||
}
|
||||
|
||||
InputQueue::~InputQueue() {
|
||||
mTouchBlockQueue.Clear();
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
InputQueue::ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
if (aEvent.mInputType != MULTITOUCH_INPUT) {
|
||||
aTarget->HandleInputEvent(aEvent);
|
||||
// The return value for non-touch input isn't really used, so just return
|
||||
// ConsumeDoDefault for now. This can be changed later if needed.
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
TouchBlockState* block = nullptr;
|
||||
if (aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
block = StartNewTouchBlock(aTarget, false);
|
||||
INPQ_LOG("%p started new touch block %p for target %p\n", this, block, aTarget.get());
|
||||
|
||||
// We want to cancel animations here as soon as possible (i.e. without waiting for
|
||||
// content responses) because a finger has gone down and we don't want to keep moving
|
||||
// the content under the finger. However, to prevent "future" touchstart events from
|
||||
// interfering with "past" animations (i.e. from a previous touch block that is still
|
||||
// being processed) we only do this animation-cancellation if there are no older
|
||||
// touch blocks still in the queue.
|
||||
if (block == CurrentTouchBlock()) {
|
||||
if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
|
||||
// If we're already in a fast fling, then we want the touch event to stop the fling
|
||||
// and to disallow the touch event from being used as part of a fling.
|
||||
block->DisallowSingleTap();
|
||||
}
|
||||
block->GetOverscrollHandoffChain()->CancelAnimations();
|
||||
}
|
||||
|
||||
if (aTarget->NeedToWaitForContent()) {
|
||||
// Content may intercept the touch events and prevent-default them. So we schedule
|
||||
// a timeout to give content time to do that.
|
||||
ScheduleContentResponseTimeout(aTarget);
|
||||
} 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.
|
||||
INPQ_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();
|
||||
INPQ_LOG("%p received new event in block %p\n", this, block);
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
nsEventStatus result = aTarget->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())
|
||||
? nsEventStatus_eConsumeDoDefault
|
||||
: nsEventStatus_eIgnore;
|
||||
|
||||
if (block == CurrentTouchBlock() && block->IsReadyForHandling()) {
|
||||
INPQ_LOG("%p's current touch block is ready with preventdefault %d\n",
|
||||
this, block->IsDefaultPrevented());
|
||||
if (block->IsDefaultPrevented()) {
|
||||
return result;
|
||||
}
|
||||
aTarget->HandleInputEvent(aEvent);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Otherwise, add it to the queue for the touch block
|
||||
block->AddEvent(aEvent.AsMultiTouchInput());
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
|
||||
{
|
||||
StartNewTouchBlock(aTarget, true);
|
||||
ScheduleContentResponseTimeout(aTarget);
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
InputQueue::StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent)
|
||||
{
|
||||
TouchBlockState* newBlock = new TouchBlockState(aTarget);
|
||||
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()) {
|
||||
INPQ_LOG("%p discarding depleted touch block %p\n", this, mTouchBlockQueue[0].get());
|
||||
mTouchBlockQueue.RemoveElementAt(0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new block to the queue.
|
||||
mTouchBlockQueue.AppendElement(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
TouchBlockState*
|
||||
InputQueue::CurrentTouchBlock() const
|
||||
{
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
MOZ_ASSERT(!mTouchBlockQueue.IsEmpty());
|
||||
return mTouchBlockQueue[0].get();
|
||||
}
|
||||
|
||||
bool
|
||||
InputQueue::HasReadyTouchBlock() const
|
||||
{
|
||||
return !mTouchBlockQueue.IsEmpty() && mTouchBlockQueue[0]->IsReadyForHandling();
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget) {
|
||||
INPQ_LOG("%p scheduling content response timeout for target %p\n", this, aTarget.get());
|
||||
aTarget->PostDelayedTask(
|
||||
NewRunnableMethod(this, &InputQueue::ContentResponseTimeout),
|
||||
gfxPrefs::APZContentResponseTimeout());
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::ContentResponseTimeout() {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
mTouchBlockBalance++;
|
||||
INPQ_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;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("INPQ received more ContentResponseTimeout calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::ContentReceivedTouch(bool aPreventDefault) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
mTouchBlockBalance--;
|
||||
INPQ_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("INPQ received more ContentReceivedTouch calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
|
||||
if (mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
ProcessPendingInputBlocks();
|
||||
} else {
|
||||
NS_WARNING("INPQ received more SetAllowedTouchBehavior calls than it has unprocessed touch blocks\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputQueue::ProcessPendingInputBlocks() {
|
||||
AsyncPanZoomController::AssertOnControllerThread();
|
||||
|
||||
while (true) {
|
||||
TouchBlockState* curBlock = CurrentTouchBlock();
|
||||
if (!curBlock->IsReadyForHandling()) {
|
||||
break;
|
||||
}
|
||||
|
||||
INPQ_LOG("%p processing input block %p; preventDefault %d target %p\n",
|
||||
this, curBlock, curBlock->IsDefaultPrevented(),
|
||||
curBlock->GetTargetApzc().get());
|
||||
nsRefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
|
||||
if (curBlock->IsDefaultPrevented()) {
|
||||
curBlock->DropEvents();
|
||||
target->ResetInputState();
|
||||
} else {
|
||||
while (curBlock->HasEvents()) {
|
||||
target->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.
|
||||
INPQ_LOG("%p discarding depleted touch block %p\n", this, curBlock);
|
||||
mTouchBlockQueue.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
147
gfx/layers/apz/src/InputQueue.h
Normal file
147
gfx/layers/apz/src/InputQueue.h
Normal file
@ -0,0 +1,147 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_InputQueue_h
|
||||
#define mozilla_layers_InputQueue_h
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class InputData;
|
||||
|
||||
namespace layers {
|
||||
|
||||
class AsyncPanZoomController;
|
||||
class OverscrollHandoffChain;
|
||||
class TouchBlockState;
|
||||
|
||||
/**
|
||||
* This class stores incoming input events, separated into "input blocks", until
|
||||
* they are ready for handling. Currently input blocks are only created from
|
||||
* touch input.
|
||||
*/
|
||||
class InputQueue {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputQueue)
|
||||
|
||||
public:
|
||||
typedef uint32_t TouchBehaviorFlags;
|
||||
|
||||
public:
|
||||
InputQueue();
|
||||
|
||||
/**
|
||||
* Notifies the InputQueue of a new incoming input event. The APZC that the
|
||||
* input event was targeted to should be provided in the |aTarget| parameter.
|
||||
* See the documentation on APZCTreeManager::ReceiveInputEvent for info on
|
||||
* return values from this function.
|
||||
*/
|
||||
nsEventStatus ReceiveInputEvent(const nsRefPtr<AsyncPanZoomController>& aTarget, const InputData& aEvent);
|
||||
/**
|
||||
* This function should be invoked to notify the InputQueue 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, after the touch-start event that creates the block is sent to
|
||||
* ReceiveInputEvent.
|
||||
*/
|
||||
void ContentReceivedTouch(bool aPreventDefault);
|
||||
/**
|
||||
* This function should be invoked to notify the InputQueue of the touch-
|
||||
* action properties for the different touch points in an input block. This
|
||||
* automatically gets applied to the next block of events that has not yet
|
||||
* received a touch behaviour notification. This function MUST be invoked
|
||||
* exactly once for each touch block, after the touch-start event that creates
|
||||
* the block is sent to ReceiveInputEvent. If touch-action is not enabled on
|
||||
* the platform, this function does nothing and need not be called.
|
||||
*/
|
||||
void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
|
||||
/**
|
||||
* Adds a new touch block at the end of the input queue that has the same
|
||||
* allowed touch behaviour flags as the the touch block currently being
|
||||
* processed. This should only be called when processing of a touch block
|
||||
* triggers the creation of a new touch block.
|
||||
*/
|
||||
void InjectNewTouchBlock(AsyncPanZoomController* aTarget);
|
||||
/**
|
||||
* Returns the touch block at the head of the queue.
|
||||
*/
|
||||
TouchBlockState* CurrentTouchBlock() const;
|
||||
/**
|
||||
* Returns true iff the touch block at the head of the queue is ready for
|
||||
* handling.
|
||||
*/
|
||||
bool HasReadyTouchBlock() const;
|
||||
|
||||
private:
|
||||
~InputQueue();
|
||||
TouchBlockState* StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent);
|
||||
void ScheduleContentResponseTimeout(const nsRefPtr<AsyncPanZoomController>& aTarget);
|
||||
void ContentResponseTimeout();
|
||||
void ProcessPendingInputBlocks();
|
||||
|
||||
private:
|
||||
// The queue of touch blocks that have not yet been processed.
|
||||
// This member must only be accessed on the controller/UI thread.
|
||||
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.
|
||||
//
|
||||
// This member must only be accessed on the controller/UI thread.
|
||||
int32_t mTouchBlockBalance;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // mozilla_layers_InputQueue_h
|
@ -240,6 +240,7 @@ UNIFIED_SOURCES += [
|
||||
'apz/src/Axis.cpp',
|
||||
'apz/src/GestureEventListener.cpp',
|
||||
'apz/src/InputBlockState.cpp',
|
||||
'apz/src/InputQueue.cpp',
|
||||
'apz/src/OverscrollHandoffState.cpp',
|
||||
'apz/src/TaskThrottler.cpp',
|
||||
'apz/testutil/APZTestData.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user