Files
palemoon27/gfx/layers/apz/src/InputQueue.cpp
T
roytam1 7d4a5ffc8c import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1190469 - Refactor some code to have a general-purpose snap function available in APZC. r=botond (a4e8706df5)
- Bug 1225761 - Clear axis lock in CancelAnimation and EndTouch. r=botond (1436279d0d)
- Bug 1226316. Fixup to only expand displayport when skating with apz. r=kats (51c49a8fca)
- Bug 1181703 - Allow re-entering the panning state if a second finger is tapped while panning with one finger. r=botond (e87cd85058)
- Bug 1181703 - Add a gtest for making sure a pan can be continued after a second finger does a tap. r=botond (16354bec40)
- Bug 1190469 - Request scroll snapping in a few places that animations are cancelled so that we don't leave things unsnapped. r=botond (4b0ba5e513)
- Bug 960317 - Remove cross-slide code from APZC. r=botond (b2a045dbc9)
- Bug 1231972 - Document how the apz.fling_friction pref is used. r=kats. DONTBUILD for comment change (c181588265)
- Bug 1221593 - Don't reset other axis' scroll position during APZ drag. r=kats (9de4e134c6)
- Bug 1219929 - Fix bad MouseInputData case. r=me (3630a3fcfa)
- Bug 1211506 - Respect the APZ lock ordering in AsyncPanZoomController::OnScrollWheel(). r=mstange (3f152ffabf)
- Bug 1229125 - Reset velocity in OnPanEnd if there's nowhere to scroll. r=botond (66ec61692a)
- fix misspatch (f7aea52b9d)
- Bug 1221186 - Don't clobber a fling-snap with a snap-to-where-we-are-now in the overscroll handoff chain. r=botond (7d4862bbe6)
- Bug 1204932 - When C++APZ is enabled, elements that are zoomed with double tap should be centered. r=botond (b9e02a24b4)
- Bug 1141884 - Rename ResetInputState and make it only apply to touch events. r=botond (aab239f51a)
- Bug 1229125 - Correct velocity computation for pan gesture events. r=botond (b1f11b2e04)
- Bug 1228559 - get MSG by audio channel type. r=roc (0e5e32f9bb)
- Bug 1191207 - cancel chrome checking (74bde1263e)
- Bug 1155469 - Mark nsTextEditorState::mTextCtrlElement as MOZ_NON_OWNING_REF; r=baku (7e6f4fa9d0)
- Bug 1218072 - crash in nsTextEditorState::FinishedRestoringSelection, r=smaug (c7811e18ea)
- bits of 1190258 (7f8fbbddfa)
- Bug 1130237: [MSE] P1. Only ever return a frame if we have data. r=gerald (1ed595a5f8)
- Bug 1130237: P2. Reset decoder state even if no decoder has been created yet. r=gerald (d352c0314b)
- Bug 1130237: [MSE] P3. Add mochitest testing behavior. r=gerald (ce2b710a7e)
- Bug 1229987: [MSE] P1. Ensure next random access point properly calculated after seek. r=gerald (d9a435f0b5)
2023-06-28 15:52:17 +08:00

708 lines
24 KiB
C++

/* -*- 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 "LayersLogging.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "OverscrollHandoffState.h"
#define INPQ_LOG(...)
// #define INPQ_LOG(...) printf_stderr("INPQ: " __VA_ARGS__)
namespace mozilla {
namespace layers {
InputQueue::InputQueue()
{
}
InputQueue::~InputQueue() {
mInputBlockQueue.Clear();
}
nsEventStatus
InputQueue::ReceiveInputEvent(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
const InputData& aEvent,
uint64_t* aOutInputBlockId) {
APZThreadUtils::AssertOnControllerThread();
switch (aEvent.mInputType) {
case MULTITOUCH_INPUT: {
const MultiTouchInput& event = aEvent.AsMultiTouchInput();
return ReceiveTouchInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
}
case SCROLLWHEEL_INPUT: {
const ScrollWheelInput& event = aEvent.AsScrollWheelInput();
return ReceiveScrollWheelInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
}
case PANGESTURE_INPUT: {
const PanGestureInput& event = aEvent.AsPanGestureInput();
return ReceivePanGestureInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
}
case MOUSE_INPUT: {
const MouseInput& event = aEvent.AsMouseInput();
return ReceiveMouseInput(aTarget, aTargetConfirmed, event, aOutInputBlockId);
}
default:
// The return value for non-touch input is only used by tests, so just pass
// through the return value for now. This can be changed later if needed.
// TODO (bug 1098430): we will eventually need to have smarter handling for
// non-touch events as well.
return aTarget->HandleInputEvent(aEvent, aTarget->GetTransformToThis());
}
}
bool
InputQueue::MaybeHandleCurrentBlock(CancelableBlockState *block,
const InputData& aEvent) {
if (block == CurrentBlock() && block->IsReadyForHandling()) {
const RefPtr<AsyncPanZoomController>& target = block->GetTargetApzc();
INPQ_LOG("current block is ready with target %p preventdefault %d\n",
target.get(), block->IsDefaultPrevented());
if (!target || block->IsDefaultPrevented()) {
return true;
}
UpdateActiveApzc(block->GetTargetApzc());
block->DispatchImmediate(aEvent);
return true;
}
return false;
}
nsEventStatus
InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
const MultiTouchInput& aEvent,
uint64_t* aOutInputBlockId) {
TouchBlockState* block = nullptr;
if (aEvent.mType == MultiTouchInput::MULTITOUCH_START) {
nsTArray<TouchBehaviorFlags> currentBehaviors;
bool haveBehaviors = false;
if (!gfxPrefs::TouchActionEnabled()) {
haveBehaviors = true;
} else if (!mInputBlockQueue.IsEmpty() && CurrentBlock()->AsTouchBlock()) {
haveBehaviors = CurrentTouchBlock()->GetAllowedTouchBehaviors(currentBehaviors);
// If the behaviours aren't set, but the main-thread response timer on
// the block is expired we still treat it as though it has behaviors,
// because in that case we still want to interrupt the fast-fling and
// use the default behaviours.
haveBehaviors |= CurrentTouchBlock()->IsContentResponseTimerExpired();
}
block = StartNewTouchBlock(aTarget, aTargetConfirmed, false);
INPQ_LOG("started new touch block %p id %" PRIu64 " for target %p\n",
block, block->GetBlockId(), aTarget.get());
// XXX using the chain from |block| here may be wrong in cases where the
// target isn't confirmed and the real target turns out to be something
// else. For now assume this is rare enough that it's not an issue.
if (block == CurrentBlock() &&
aEvent.mTouches.Length() == 1 &&
block->GetOverscrollHandoffChain()->HasFastFlungApzc() &&
haveBehaviors) {
// If we're already in a fast fling, and a single finger goes down, then
// we want special handling for the touch event, because it shouldn't get
// delivered to content. Note that we don't set this flag when going
// from a fast fling to a pinch state (i.e. second finger goes down while
// the first finger is moving).
block->SetDuringFastFling();
block->SetConfirmedTargetApzc(aTarget);
if (gfxPrefs::TouchActionEnabled()) {
block->SetAllowedTouchBehaviors(currentBehaviors);
}
INPQ_LOG("block %p tagged as fast-motion\n", block);
}
CancelAnimationsForNewBlock(block);
MaybeRequestContentResponse(aTarget, block);
} else {
if (!mInputBlockQueue.IsEmpty()) {
block = mInputBlockQueue.LastElement().get()->AsTouchBlock();
}
if (!block) {
NS_WARNING("Received a non-start touch event while no touch blocks active!");
return nsEventStatus_eIgnore;
}
INPQ_LOG("received new event in block %p\n", block);
}
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
// Note that the |aTarget| the APZCTM sent us may contradict the confirmed
// target set on the block. In this case the confirmed target (which may be
// null) should take priority. This is equivalent to just always using the
// target (confirmed or not) from the block.
RefPtr<AsyncPanZoomController> target = block->GetTargetApzc();
nsEventStatus result = nsEventStatus_eIgnore;
// XXX calling ArePointerEventsConsumable on |target| may be wrong here if
// the target isn't confirmed and the real target turns out to be something
// else. For now assume this is rare enough that it's not an issue.
if (block->IsDuringFastFling()) {
INPQ_LOG("dropping event due to block %p being in fast motion\n", block);
result = nsEventStatus_eConsumeNoDefault;
} else if (target && target->ArePointerEventsConsumable(block, aEvent.AsMultiTouchInput().mTouches.Length())) {
if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), true)) {
INPQ_LOG("dropping event due to block %p being in slop\n", block);
result = nsEventStatus_eConsumeNoDefault;
} else {
result = nsEventStatus_eConsumeDoDefault;
}
} else if (block->UpdateSlopState(aEvent.AsMultiTouchInput(), false)) {
INPQ_LOG("dropping event due to block %p being in mini-slop\n", block);
result = nsEventStatus_eConsumeNoDefault;
}
if (!MaybeHandleCurrentBlock(block, aEvent)) {
block->AddEvent(aEvent.AsMultiTouchInput());
}
return result;
}
nsEventStatus
InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
const MouseInput& aEvent,
uint64_t* aOutInputBlockId) {
MOZ_ASSERT(!aTargetConfirmed); // We wont know the target until content tells us
// On a new mouse down we can have a new target so we must force a new block
// with a new target.
bool newBlock = aEvent.mType == MouseInput::MOUSE_DOWN && aEvent.IsLeftButton();
DragBlockState* block = nullptr;
if (!newBlock && !mInputBlockQueue.IsEmpty()) {
block = mInputBlockQueue.LastElement()->AsDragBlock();
}
if (block && block->HasReceivedMouseUp()) {
block = nullptr;
}
if (!newBlock && !block) {
return nsEventStatus_eConsumeDoDefault;
}
if (!block) {
MOZ_ASSERT(newBlock);
block = new DragBlockState(aTarget, aTargetConfirmed, aEvent);
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
INPQ_LOG("started new drag block %p for target %p\n", block, aTarget.get());
SweepDepletedBlocks();
mInputBlockQueue.AppendElement(block);
CancelAnimationsForNewBlock(block);
MaybeRequestContentResponse(aTarget, block);
block->AddEvent(aEvent.AsMouseInput());
// The first event will confirm the block or not.
return nsEventStatus_eConsumeDoDefault;
}
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
if (!MaybeHandleCurrentBlock(block, aEvent)) {
block->AddEvent(aEvent.AsMouseInput());
}
bool mouseUp = aEvent.mType == MouseInput::MOUSE_UP && aEvent.IsLeftButton();
if (mouseUp) {
block->MarkMouseUpReceived();
}
// If we're not the first event then we need to wait for the confirmation of
// the block.
return nsEventStatus_eConsumeDoDefault;
}
nsEventStatus
InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
const ScrollWheelInput& aEvent,
uint64_t* aOutInputBlockId) {
WheelBlockState* block = nullptr;
if (!mInputBlockQueue.IsEmpty()) {
block = mInputBlockQueue.LastElement()->AsWheelBlock();
// If the block is not accepting new events we'll create a new input block
// (and therefore a new wheel transaction).
if (block &&
(!block->ShouldAcceptNewEvent() ||
block->MaybeTimeout(aEvent)))
{
block = nullptr;
}
}
MOZ_ASSERT(!block || block->InTransaction());
if (!block) {
block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
INPQ_LOG("started new scroll wheel block %p for target %p\n", block, aTarget.get());
SweepDepletedBlocks();
mInputBlockQueue.AppendElement(block);
CancelAnimationsForNewBlock(block);
MaybeRequestContentResponse(aTarget, block);
} else {
INPQ_LOG("received new event in block %p\n", block);
}
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
// Copy the event, since WheelBlockState needs to affix a counter.
ScrollWheelInput event(aEvent);
block->Update(event);
// Note that the |aTarget| the APZCTM sent us may contradict the confirmed
// target set on the block. In this case the confirmed target (which may be
// null) should take priority. This is equivalent to just always using the
// target (confirmed or not) from the block, which is what
// MaybeHandleCurrentBlock() does.
if (!MaybeHandleCurrentBlock(block, event)) {
block->AddEvent(event);
}
return nsEventStatus_eConsumeDoDefault;
}
static bool
CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
PanGestureBlockState* aBlock)
{
PanGestureInput horizontalComponent = aInitialEvent;
horizontalComponent.mPanDisplacement.y = 0;
RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent);
return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc();
}
nsEventStatus
InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
const PanGestureInput& aEvent,
uint64_t* aOutInputBlockId) {
if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART ||
aEvent.mType == PanGestureInput::PANGESTURE_CANCELLED) {
// Ignore these events for now.
return nsEventStatus_eConsumeDoDefault;
}
PanGestureBlockState* block = nullptr;
if (!mInputBlockQueue.IsEmpty() &&
aEvent.mType != PanGestureInput::PANGESTURE_START) {
block = mInputBlockQueue.LastElement()->AsPanGestureBlock();
}
nsEventStatus result = nsEventStatus_eConsumeDoDefault;
if (!block || block->WasInterrupted()) {
if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
// Only PANGESTURE_START events are allowed to start a new pan gesture block.
return nsEventStatus_eConsumeDoDefault;
}
block = new PanGestureBlockState(aTarget, aTargetConfirmed, aEvent);
INPQ_LOG("started new pan gesture block %p for target %p\n", block, aTarget.get());
if (aTargetConfirmed &&
aEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
!CanScrollTargetHorizontally(aEvent, block)) {
// This event may trigger a swipe gesture, depending on what our caller
// wants to do it. We need to suspend handling of this block until we get
// a content response which will tell us whether to proceed or abort the
// block.
block->SetNeedsToWaitForContentResponse(true);
// Inform our caller that we haven't scrolled in response to the event
// and that a swipe can be started from this event if desired.
result = nsEventStatus_eIgnore;
}
SweepDepletedBlocks();
mInputBlockQueue.AppendElement(block);
CancelAnimationsForNewBlock(block);
MaybeRequestContentResponse(aTarget, block);
} else {
INPQ_LOG("received new event in block %p\n", block);
}
if (aOutInputBlockId) {
*aOutInputBlockId = block->GetBlockId();
}
// Note that the |aTarget| the APZCTM sent us may contradict the confirmed
// target set on the block. In this case the confirmed target (which may be
// null) should take priority. This is equivalent to just always using the
// target (confirmed or not) from the block, which is what
// MaybeHandleCurrentBlock() does.
if (!MaybeHandleCurrentBlock(block, aEvent)) {
block->AddEvent(aEvent.AsPanGestureInput());
}
return result;
}
void
InputQueue::CancelAnimationsForNewBlock(CancelableBlockState* aBlock)
{
// 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 (aBlock == CurrentBlock()) {
aBlock->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
}
}
void
InputQueue::MaybeRequestContentResponse(const RefPtr<AsyncPanZoomController>& aTarget,
CancelableBlockState* aBlock)
{
bool waitForMainThread = false;
if (aBlock->IsTargetConfirmed()) {
// Content won't prevent-default this, so we can just set the flag directly.
INPQ_LOG("not waiting for content response on block %p\n", aBlock);
aBlock->SetContentResponse(false);
} else {
waitForMainThread = true;
}
if (aBlock->AsTouchBlock() && gfxPrefs::TouchActionEnabled()) {
// TODO: once bug 1101628 is fixed, waitForMainThread should only be set
// to true if the APZCTM didn't know the touch-action behaviours for this
// block.
waitForMainThread = true;
}
if (waitForMainThread) {
// We either don't know for sure if aTarget is the right APZC, or we may
// need to wait to give content the opportunity to prevent-default the
// touch events. Either way we schedule a timeout so the main thread stuff
// can run.
ScheduleMainThreadTimeout(aTarget, aBlock->GetBlockId());
}
}
uint64_t
InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
{
TouchBlockState* block = StartNewTouchBlock(aTarget,
/* aTargetConfirmed = */ true,
/* aCopyPropertiesFromCurrent = */ true);
INPQ_LOG("injecting new touch block %p with id %" PRIu64 " and target %p\n",
block, block->GetBlockId(), aTarget);
ScheduleMainThreadTimeout(aTarget, block->GetBlockId());
return block->GetBlockId();
}
void
InputQueue::SweepDepletedBlocks()
{
// We're going to start a new block, so clear out any depleted blocks at the head of the queue.
// See corresponding comment in ProcessInputBlocks.
while (!mInputBlockQueue.IsEmpty()) {
CancelableBlockState* block = mInputBlockQueue[0].get();
if (!block->IsReadyForHandling() || block->HasEvents()) {
break;
}
INPQ_LOG("discarding depleted %s block %p\n", block->Type(), block);
mInputBlockQueue.RemoveElementAt(0);
}
}
TouchBlockState*
InputQueue::StartNewTouchBlock(const RefPtr<AsyncPanZoomController>& aTarget,
bool aTargetConfirmed,
bool aCopyPropertiesFromCurrent)
{
TouchBlockState* newBlock = new TouchBlockState(aTarget, aTargetConfirmed,
mTouchCounter);
if (aCopyPropertiesFromCurrent) {
newBlock->CopyPropertiesFrom(*CurrentTouchBlock());
}
SweepDepletedBlocks();
// Add the new block to the queue.
mInputBlockQueue.AppendElement(newBlock);
return newBlock;
}
CancelableBlockState*
InputQueue::CurrentBlock() const
{
APZThreadUtils::AssertOnControllerThread();
MOZ_ASSERT(!mInputBlockQueue.IsEmpty());
return mInputBlockQueue[0].get();
}
TouchBlockState*
InputQueue::CurrentTouchBlock() const
{
TouchBlockState* block = CurrentBlock()->AsTouchBlock();
MOZ_ASSERT(block);
return block;
}
WheelBlockState*
InputQueue::CurrentWheelBlock() const
{
WheelBlockState* block = CurrentBlock()->AsWheelBlock();
MOZ_ASSERT(block);
return block;
}
DragBlockState*
InputQueue::CurrentDragBlock() const
{
DragBlockState* block = CurrentBlock()->AsDragBlock();
MOZ_ASSERT(block);
return block;
}
PanGestureBlockState*
InputQueue::CurrentPanGestureBlock() const
{
PanGestureBlockState* block = CurrentBlock()->AsPanGestureBlock();
MOZ_ASSERT(block);
return block;
}
WheelBlockState*
InputQueue::GetCurrentWheelTransaction() const
{
if (mInputBlockQueue.IsEmpty()) {
return nullptr;
}
WheelBlockState* block = CurrentBlock()->AsWheelBlock();
if (!block || !block->InTransaction()) {
return nullptr;
}
return block;
}
bool
InputQueue::HasReadyTouchBlock() const
{
return !mInputBlockQueue.IsEmpty() &&
mInputBlockQueue[0]->AsTouchBlock() &&
mInputBlockQueue[0]->IsReadyForHandling();
}
bool
InputQueue::AllowScrollHandoff() const
{
MOZ_ASSERT(CurrentBlock());
if (CurrentBlock()->AsWheelBlock()) {
return CurrentBlock()->AsWheelBlock()->AllowScrollHandoff();
}
if (CurrentBlock()->AsPanGestureBlock()) {
return CurrentBlock()->AsPanGestureBlock()->AllowScrollHandoff();
}
return true;
}
void
InputQueue::ScheduleMainThreadTimeout(const RefPtr<AsyncPanZoomController>& aTarget, uint64_t aInputBlockId) {
INPQ_LOG("scheduling main thread timeout for target %p\n", aTarget.get());
aTarget->PostDelayedTask(
NewRunnableMethod(this, &InputQueue::MainThreadTimeout, aInputBlockId),
gfxPrefs::APZContentResponseTimeout());
}
void
InputQueue::MainThreadTimeout(const uint64_t& aInputBlockId) {
APZThreadUtils::AssertOnControllerThread();
INPQ_LOG("got a main thread timeout; block=%" PRIu64 "\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
// time out the touch-listener response and also confirm the existing
// target apzc in the case where the main thread doesn't get back to us
// fast enough.
success = mInputBlockQueue[i]->TimeoutContentResponse();
success |= mInputBlockQueue[i]->SetConfirmedTargetApzc(mInputBlockQueue[i]->GetTargetApzc());
break;
}
}
if (success) {
ProcessInputBlocks();
}
}
void
InputQueue::ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
APZThreadUtils::AssertOnControllerThread();
INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
CancelableBlockState* block = mInputBlockQueue[i].get();
success = block->SetContentResponse(aPreventDefault);
break;
}
}
if (success) {
ProcessInputBlocks();
}
}
void
InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc) {
APZThreadUtils::AssertOnControllerThread();
INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
bool success = false;
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
success = mInputBlockQueue[i]->SetConfirmedTargetApzc(aTargetApzc);
break;
}
}
if (success) {
ProcessInputBlocks();
}
}
void
InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomController>& aTargetApzc,
const AsyncDragMetrics& aDragMetrics)
{
APZThreadUtils::AssertOnControllerThread();
INPQ_LOG("got a target apzc; block=%" PRIu64 " guid=%s\n",
aInputBlockId, aTargetApzc ? Stringify(aTargetApzc->GetGuid()).c_str() : "");
bool success = false;
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
DragBlockState* block = mInputBlockQueue[i]->AsDragBlock();
if (block && block->GetBlockId() == aInputBlockId) {
block->SetDragMetrics(aDragMetrics);
success = block->SetConfirmedTargetApzc(aTargetApzc);
break;
}
}
if (success) {
ProcessInputBlocks();
}
}
void
InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
APZThreadUtils::AssertOnControllerThread();
INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
bool success = false;
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
if (mInputBlockQueue[i]->GetBlockId() == aInputBlockId) {
TouchBlockState *block = mInputBlockQueue[i]->AsTouchBlock();
if (block) {
success = block->SetAllowedTouchBehaviors(aBehaviors);
} else {
NS_WARNING("input block is not a touch block");
}
break;
}
}
if (success) {
ProcessInputBlocks();
}
}
void
InputQueue::ProcessInputBlocks() {
APZThreadUtils::AssertOnControllerThread();
do {
CancelableBlockState* curBlock = CurrentBlock();
if (!curBlock->IsReadyForHandling()) {
break;
}
INPQ_LOG("processing input block %p; preventDefault %d target %p\n",
curBlock, curBlock->IsDefaultPrevented(),
curBlock->GetTargetApzc().get());
RefPtr<AsyncPanZoomController> target = curBlock->GetTargetApzc();
// target may be null here if the initial target was unconfirmed and then
// we later got a confirmed null target. in that case drop the events.
if (!target) {
curBlock->DropEvents();
} else if (curBlock->IsDefaultPrevented()) {
curBlock->DropEvents();
if (curBlock->AsTouchBlock()) {
target->ResetTouchInputState();
}
} else {
UpdateActiveApzc(curBlock->GetTargetApzc());
curBlock->HandleEvents();
}
MOZ_ASSERT(!curBlock->HasEvents());
if (mInputBlockQueue.Length() == 1 && curBlock->MustStayActive()) {
// Some types of blocks (e.g. touch blocks) accumulate events until the
// next input block is started. Therefore we cannot remove the block from
// the queue until we have started another block. This block will be
// removed by SweepDeletedBlocks() whenever a new block is 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("discarding processed %s block %p\n", curBlock->Type(), curBlock);
mInputBlockQueue.RemoveElementAt(0);
} while (!mInputBlockQueue.IsEmpty());
}
void
InputQueue::UpdateActiveApzc(const RefPtr<AsyncPanZoomController>& aNewActive) {
if (mLastActiveApzc && mLastActiveApzc != aNewActive
&& mTouchCounter.GetActiveTouchCount() > 0) {
mLastActiveApzc->ResetTouchInputState();
}
mLastActiveApzc = aNewActive;
}
void
InputQueue::Clear()
{
APZThreadUtils::AssertOnControllerThread();
mInputBlockQueue.Clear();
mLastActiveApzc = nullptr;
}
} // namespace layers
} // namespace mozilla