Files
palemoon27/gfx/layers/apz/src/InputBlockState.cpp
T
roytam1 05670874b5 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1262361 - Use gfxCriticalError to collect useful info when device reset. r=milan (a565047753)
- Bug 1265282 - Annotate reports instead of crashing when setting an incompatible compositor (D3D11). r=dvander (ed04e9e8eb)
- Bug 1235407 - Part 3: Force a reset when OpenSharedHandle fails. r=milan (a2f5c656eb)
- Bug 1264142 - Add ImageLayerComposite::GetFullyRenderedRegion() r=mattwoodrow (3a5f479820)
- Bug 1224199 - Remove some unused code in TiledLayerBufferComposite - r=nical (b3c60126ce)
- Bug 1262328 - Add Add ClientLayer::HandleMemoryPressure() r=nical (31bb89613d)
- Bug 1224833 - Check explicitly if image has TextureClient r=nical (34044dc195)
- Bug 1240867 - Add missing include. r=nical (abd9a8876e)
- Bug 1240800: When we've reallocated our buffer client side and fail to track the proper invalid region always upload the bounds of the visible region. r=mattwoodrow (7a7cc1928c)
- Bug 1253386 - Double buffer in CanvasClient2D - r=nical (a0fe10f1a6)
- Bug 1266380 - fix offset of untransformed surface when 3d transforming in BasicLayerManager. r=jrmuizel (3d3931346f)
- Bug 1257288 - Fix a bug in APZCTM logging code. r=kats (3c73c99077)
- Bug 1265424 - Record if the target was confirmed via a timeout or not. r=botond (e25c0c016d)
- Bug 1265424 - Ensure that HasReceivedAllContentNotifications doesn't start returning true if the target APZC was confirmed by timeout. r=botond (6c4e57cb8f)
- Bug 1229039 - If a PanGesture input block gets interrupted, just start a new block instead of not sending the rest of the events through the APZ. r=mstange (534b6691f7)
- Bug 1056356 - Remove the hand-rolled mechanism used to get nsRefPtr<const OverscrollHandoffChaiin> to work. r=kats (9d692fa32f)
- Bug 1253617 - Fix non-unified build bustage in OverscrollHandoffStat.cpp r=BenWa (38a53f7521)
- Bug 1262432 - Remove assertion that may be legitimately false sometimes. r=tnikkel (36bbb36476)
- Bug 1169802 - Temporary workaround for a deeper bug, to prevent an assertion from firing. r=botond (b1edd69e49)
- bits of Bug 1223296 (182a1ee34e)
- move around (bug 1014691) and align some more tests (19db30010c)
- Bug 944164 - Update jorendb to current function names, and much else: (b96a636c60)
- Bug 1244222 - Tests. r=bz (9cfbe54859)
- Bug 888969 - Make XPCJSID instanceof comparisons work correctly when [[GetPrototypeOf]] on the [[Prototype]] chain of the instance being tested throws an exception. r=bz (ff450de2ef)
- Bug 1256306 - Bump the Windows stack limit. r=bholley (5f4f0c3b1c)
- Bug 1257234 - Detect main thread's stack size at runtime, on Windows. r=ted (108608ce73)
- bug 1264651 - remove dom.max_child_script_run_time pref r=billm (443bc36d84)
- Bug 1266630 - Make fallible the orphan node table used during memory reporting. r=mccr8. (b7c6da934c)
- Bug 1259699 - Adjust Windows stack limits to account for large PGO stack frames. r=bholley (87ff9dbef9)
- bug 1250486 - get rid of the static ctor for XPCShellImpl.cpp r=bz (748def73ea)
- Bug 1231309 - guard sz with assert. r=bobbyholley (34080a1022)
- Bug 1255817 part 5. Remove the now-unused xpc::SystemErrorReporter. r=bholley (c014f7fa7f)
- Bug 1257888 - Link chromium mutex-based atomics implementation to webrtc signaling tests. r=froydnj (fadb9764b2)
- Bug 1253123 - Remove ipc_sync_message (r=jld) a=kwierso (7cc8a8fe0e)
- Bug 1253123 - Remove ipc_channel_proxy (r=jld) (4000ea80f3)
- Bug 1253123 - Remove message_router (r=jld) a=kwierso (8b41859a17)
- Bug 1235633 - IPC OOM mitigation by eliminating buffer copying (r=jld) (ecd6c12bec)
- Bug 1263314: Remove NonThreadSafe. r=jld (06a6331dee)
- Bug 1261567 - Include compat dir in libevent include path. r=billm (b496d2ad1f)
- Bug 1258312 - Add crash annotation to EnumSerializer r=jld (7face45d63)
- Bug 1262463 - part 1 - turn NS_RUNTIMEABORTs in protocols into FatalErrors; r=jld (731b35dc68)
- Bug 1262463 - part 2 - don't pass the other process pid into FatalError; r=jld (6f54c58494)
- Bug 1262463 - part 3 - out-of-line NS_RUNTIMEABORT calls in IPDL-generated code; r=jld (0728f61a5f)
- Bug 1259428 - part 1 - don't call Log methods of generated method classes; r=jld (938c6ff705)
- Bug 1259428 - part 2 - remove dodgy static_cast downcasts from logging statements; r=jld (4fa0a5228d)
- Bug 1259428 - part 3 - remove Log() methods from generated message subclasses; r=jld (964a0c2487)
- Bug 1259428 - part 4 - remove prtime.h from generated protocol headers; r=jld (8548b37b91)
- Bug 1259428 - part 5 - convert Message subclasses to constructor functions; r=jld (63f195ffce)
- Bug 1259428 - part 6 - remove unneeded MessageDecl methods; r=jld (fc8cd72bd7)
- Bug 1262458 - rename {msg,reply}Class-related things to reflect new functional reality; r=jld (8122887df4)
- Bug 1258663 - Crash annotate system call failures in the IPC transport. r=gabor (5f483a17a5)
- Bug 1258317 - Part 1: Annotate the crash report with process information on failure to transfer an IPC transport to another process. r=krizsa (107947c7c3)
- Bug 1258317 - cross compilation fixup. (534c214182)
- Bug 1258317 - Part 2: Remove unused privilege in mozilla::ipc::TransferHandleTorProcess(). r=krizsa (82fc8ca4cc)
- Bug 1252246 - Try to use PTHREAD_PROCESS_SHARED for CrossProcessMutex on more Unices. r=glandium f=kats (195742a508)
- now we can add crashreporter (2cbb6f2d1a)
- Bug 1251226 - Avoid passing a std::wstring through the variadic method; r=bobowen (80b2dd8034)
- Bug 1260736 - Let the client to filter out its interested messages to lower the number of times entering the monitor in PeekMessages(). r=dvander (3aea1ed77a)
- Bug 1264662 - Record IPC message capacity instead of size. r=billm (fbfff4e5b8)
- Bug 1261099 - Avoid two Message copies in MaybeUndeferIncall. r=billm (ebb7fe2b47)
2024-07-23 11:36:09 +08:00

986 lines
25 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 "InputBlockState.h"
#include "AsyncPanZoomController.h" // for AsyncPanZoomController
#include "AsyncScrollBase.h" // for kScrollSeriesTimeoutMs
#include "gfxPrefs.h" // for gfxPrefs
#include "mozilla/MouseEvents.h"
#include "mozilla/SizePrintfMacros.h" // for PRIuSIZE
#include "mozilla/Telemetry.h" // for Telemetry
#include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
#include "OverscrollHandoffState.h"
#define TBS_LOG(...)
// #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
namespace mozilla {
namespace layers {
static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed)
: mTargetApzc(aTargetApzc)
, mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed
: TargetConfirmationState::eUnconfirmed)
, mBlockId(sBlockCounter++)
, mTransformToApzc(aTargetApzc->GetTransformToThis())
{
// We should never be constructed with a nullptr target.
MOZ_ASSERT(mTargetApzc);
mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
}
bool
InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
|| aState == TargetConfirmationState::eTimedOut);
if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
aState == TargetConfirmationState::eConfirmed) {
// The main thread finally responded. We had already timed out the
// confirmation, but we want to update the state internally so that we
// can record the time for telemetry purposes.
mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded;
}
if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) {
return false;
}
mTargetConfirmed = aState;
TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
if (mTargetApzc == aTargetApzc) {
// The confirmed target is the same as the tentative one, so we're done.
return true;
}
TBS_LOG("%p replacing unconfirmed target %p with real target %p\n",
this, mTargetApzc.get(), aTargetApzc.get());
UpdateTargetApzc(aTargetApzc);
return true;
}
void
InputBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
{
// note that aTargetApzc MAY be null here.
mTargetApzc = aTargetApzc;
mTransformToApzc = aTargetApzc ? aTargetApzc->GetTransformToThis() : ScreenToParentLayerMatrix4x4();
mOverscrollHandoffChain = (mTargetApzc ? mTargetApzc->BuildOverscrollHandoffChain() : nullptr);
}
const RefPtr<AsyncPanZoomController>&
InputBlockState::GetTargetApzc() const
{
return mTargetApzc;
}
const RefPtr<const OverscrollHandoffChain>&
InputBlockState::GetOverscrollHandoffChain() const
{
return mOverscrollHandoffChain;
}
uint64_t
InputBlockState::GetBlockId() const
{
return mBlockId;
}
bool
InputBlockState::IsTargetConfirmed() const
{
return mTargetConfirmed != TargetConfirmationState::eUnconfirmed;
}
bool
InputBlockState::HasReceivedRealConfirmedTarget() const
{
return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
}
bool
InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const
{
if (aA == aB) {
return true;
}
bool seenA = false;
for (size_t i = 0; i < mOverscrollHandoffChain->Length(); ++i) {
AsyncPanZoomController* apzc = mOverscrollHandoffChain->GetApzcAtIndex(i);
if (apzc == aB) {
return seenA;
}
if (apzc == aA) {
seenA = true;
}
}
return false;
}
void
InputBlockState::SetScrolledApzc(AsyncPanZoomController* aApzc)
{
// An input block should only have one scrolled APZC.
MOZ_ASSERT(!mScrolledApzc || (gfxPrefs::APZAllowImmediateHandoff() ? IsDownchainOf(mScrolledApzc, aApzc) : mScrolledApzc == aApzc));
mScrolledApzc = aApzc;
}
AsyncPanZoomController*
InputBlockState::GetScrolledApzc() const
{
return mScrolledApzc;
}
bool
InputBlockState::IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const
{
MOZ_ASSERT(aApzc && mScrolledApzc);
return IsDownchainOf(mScrolledApzc, aApzc);
}
CancelableBlockState::CancelableBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed)
: InputBlockState(aTargetApzc, aTargetConfirmed)
, mPreventDefault(false)
, mContentResponded(false)
, mContentResponseTimerExpired(false)
{
}
bool
CancelableBlockState::SetContentResponse(bool aPreventDefault)
{
if (mContentResponded) {
return false;
}
TBS_LOG("%p got content response %d with timer expired %d\n",
this, aPreventDefault, mContentResponseTimerExpired);
mPreventDefault = aPreventDefault;
mContentResponded = true;
return true;
}
void
CancelableBlockState::StartContentResponseTimer()
{
MOZ_ASSERT(mContentResponseTimer.IsNull());
mContentResponseTimer = TimeStamp::Now();
}
bool
CancelableBlockState::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
CancelableBlockState::IsContentResponseTimerExpired() const
{
return mContentResponseTimerExpired;
}
bool
CancelableBlockState::IsDefaultPrevented() const
{
MOZ_ASSERT(mContentResponded || mContentResponseTimerExpired);
return mPreventDefault;
}
bool
CancelableBlockState::HasReceivedAllContentNotifications() const
{
return HasReceivedRealConfirmedTarget() && mContentResponded;
}
bool
CancelableBlockState::IsReadyForHandling() const
{
if (!IsTargetConfirmed()) {
return false;
}
return mContentResponded || mContentResponseTimerExpired;
}
void
CancelableBlockState::DispatchImmediate(const InputData& aEvent) const
{
MOZ_ASSERT(!HasEvents());
MOZ_ASSERT(GetTargetApzc());
DispatchEvent(aEvent);
}
void
CancelableBlockState::DispatchEvent(const InputData& aEvent) const
{
GetTargetApzc()->HandleInputEvent(aEvent, mTransformToApzc);
}
void
CancelableBlockState::RecordContentResponseTime()
{
if (!mContentResponseTimer) {
// We might get responses from content even though we didn't wait for them.
// In that case, ignore the time on them, because they're not relevant for
// tuning our timeout value. Also this function might get called multiple
// times on the same input block, so we should only record the time from the
// first successful call.
return;
}
if (!HasReceivedAllContentNotifications()) {
// Not done yet, we'll get called again
return;
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::CONTENT_RESPONSE_DURATION,
(uint32_t)(TimeStamp::Now() - mContentResponseTimer).ToMilliseconds());
mContentResponseTimer = TimeStamp();
}
DragBlockState::DragBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed,
const MouseInput& aInitialEvent)
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
, mReceivedMouseUp(false)
{
}
bool
DragBlockState::HasReceivedMouseUp()
{
return mReceivedMouseUp;
}
void
DragBlockState::MarkMouseUpReceived()
{
mReceivedMouseUp = true;
}
void
DragBlockState::SetDragMetrics(const AsyncDragMetrics& aDragMetrics)
{
mDragMetrics = aDragMetrics;
}
void
DragBlockState::DispatchEvent(const InputData& aEvent) const
{
MouseInput mouseInput = aEvent.AsMouseInput();
if (!mouseInput.TransformToLocal(mTransformToApzc)) {
return;
}
GetTargetApzc()->HandleDragEvent(mouseInput, mDragMetrics);
}
void
DragBlockState::AddEvent(const MouseInput& aEvent)
{
mEvents.AppendElement(aEvent);
}
bool
DragBlockState::HasEvents() const
{
return !mEvents.IsEmpty();
}
void
DragBlockState::DropEvents()
{
TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
mEvents.Clear();
}
void
DragBlockState::HandleEvents()
{
while (HasEvents()) {
TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
MouseInput event = mEvents[0];
mEvents.RemoveElementAt(0);
DispatchEvent(event);
}
}
bool
DragBlockState::MustStayActive()
{
return !mReceivedMouseUp;
}
const char*
DragBlockState::Type()
{
return "drag";
}
// This is used to track the current wheel transaction.
static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed,
const ScrollWheelInput& aInitialEvent)
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
, mScrollSeriesCounter(0)
, mTransactionEnded(false)
{
sLastWheelBlockId = GetBlockId();
if (aTargetConfirmed) {
// Find the nearest APZC in the overscroll handoff chain that is scrollable.
// If we get a content confirmation later that the apzc is different, then
// content should have found a scrollable apzc, so we don't need to handle
// that case.
RefPtr<AsyncPanZoomController> apzc =
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
// If nothing is scrollable, we don't consider this block as starting a
// transaction.
if (!apzc) {
EndTransaction();
return;
}
if (apzc != GetTargetApzc()) {
UpdateTargetApzc(apzc);
}
}
}
bool
WheelBlockState::SetContentResponse(bool aPreventDefault)
{
if (aPreventDefault) {
EndTransaction();
}
return CancelableBlockState::SetContentResponse(aPreventDefault);
}
bool
WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
// The APZC that we find via APZCCallbackHelpers may not be the same APZC
// ESM or OverscrollHandoff would have computed. Make sure we get the right
// one by looking for the first apzc the next pending event can scroll.
RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
if (apzc && mEvents.Length() > 0) {
const ScrollWheelInput& event = mEvents.ElementAt(0);
apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
}
InputBlockState::SetConfirmedTargetApzc(apzc, aState);
return true;
}
void
WheelBlockState::Update(ScrollWheelInput& aEvent)
{
// We might not be in a transaction if the block never started in a
// transaction - for example, if nothing was scrollable.
if (!InTransaction()) {
return;
}
// The current "scroll series" is a like a sub-transaction. It has a separate
// timeout of 80ms. Since we need to compute wheel deltas at different phases
// of a transaction (for example, when it is updated, and later when the
// event action is taken), we affix the scroll series counter to the event.
// This makes GetScrollWheelDelta() consistent.
if (!mLastEventTime.IsNull() &&
(aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs)
{
mScrollSeriesCounter = 0;
}
aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter;
// If we can't scroll in the direction of the wheel event, we don't update
// the last move time. This allows us to timeout a transaction even if the
// mouse isn't moving.
//
// We skip this check if the target is not yet confirmed, so that when it is
// confirmed, we don't timeout the transaction.
RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
return;
}
// Update the time of the last known good event, and reset the mouse move
// time to null. This will reset the delays on both the general transaction
// timeout and the mouse-move-in-frame timeout.
mLastEventTime = aEvent.mTimeStamp;
mLastMouseMove = TimeStamp();
}
void
WheelBlockState::AddEvent(const ScrollWheelInput& aEvent)
{
mEvents.AppendElement(aEvent);
}
bool
WheelBlockState::HasEvents() const
{
return !mEvents.IsEmpty();
}
void
WheelBlockState::DropEvents()
{
TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
mEvents.Clear();
}
void
WheelBlockState::HandleEvents()
{
while (HasEvents()) {
TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
ScrollWheelInput event = mEvents[0];
mEvents.RemoveElementAt(0);
DispatchEvent(event);
}
}
bool
WheelBlockState::MustStayActive()
{
return !mTransactionEnded;
}
const char*
WheelBlockState::Type()
{
return "scroll wheel";
}
bool
WheelBlockState::ShouldAcceptNewEvent() const
{
if (!InTransaction()) {
// If we're not in a transaction, start a new one.
return false;
}
RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
if (apzc->IsDestroyed()) {
return false;
}
return true;
}
bool
WheelBlockState::MaybeTimeout(const ScrollWheelInput& aEvent)
{
MOZ_ASSERT(InTransaction());
if (MaybeTimeout(aEvent.mTimeStamp)) {
return true;
}
if (!mLastMouseMove.IsNull()) {
// If there's a recent mouse movement, we can time out the transaction early.
TimeDuration duration = TimeStamp::Now() - mLastMouseMove;
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
EndTransaction();
return true;
}
}
return false;
}
bool
WheelBlockState::MaybeTimeout(const TimeStamp& aTimeStamp)
{
MOZ_ASSERT(InTransaction());
// End the transaction if the event occurred > 1.5s after the most recently
// seen wheel event.
TimeDuration duration = aTimeStamp - mLastEventTime;
if (duration.ToMilliseconds() < gfxPrefs::MouseWheelTransactionTimeoutMs()) {
return false;
}
TBS_LOG("%p wheel transaction timed out\n", this);
if (gfxPrefs::MouseScrollTestingEnabled()) {
RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
apzc->NotifyMozMouseScrollEvent(NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"));
}
EndTransaction();
return true;
}
void
WheelBlockState::OnMouseMove(const ScreenIntPoint& aPoint)
{
MOZ_ASSERT(InTransaction());
if (!GetTargetApzc()->Contains(aPoint)) {
EndTransaction();
return;
}
if (mLastMouseMove.IsNull()) {
// If the cursor is moving inside the frame, and it is more than the
// ignoremovedelay time since the last scroll operation, we record
// this as the most recent mouse movement.
TimeStamp now = TimeStamp::Now();
TimeDuration duration = now - mLastEventTime;
if (duration.ToMilliseconds() >= gfxPrefs::MouseWheelIgnoreMoveDelayMs()) {
mLastMouseMove = now;
}
}
}
void
WheelBlockState::UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
{
InputBlockState::UpdateTargetApzc(aTargetApzc);
// If we found there was no target apzc, then we end the transaction.
if (!GetTargetApzc()) {
EndTransaction();
}
}
bool
WheelBlockState::InTransaction() const
{
// We consider a wheel block to be in a transaction if it has a confirmed
// target and is the most recent wheel input block to be created.
if (GetBlockId() != sLastWheelBlockId) {
return false;
}
if (mTransactionEnded) {
return false;
}
MOZ_ASSERT(GetTargetApzc());
return true;
}
bool
WheelBlockState::AllowScrollHandoff() const
{
// If we're in a wheel transaction, we do not allow overscroll handoff until
// a new event ends the wheel transaction.
return !IsTargetConfirmed() || !InTransaction();
}
void
WheelBlockState::EndTransaction()
{
TBS_LOG("%p ending wheel transaction\n", this);
mTransactionEnded = true;
}
PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed,
const PanGestureInput& aInitialEvent)
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
, mInterrupted(false)
, mWaitingForContentResponse(false)
{
if (aTargetConfirmed) {
// Find the nearest APZC in the overscroll handoff chain that is scrollable.
// If we get a content confirmation later that the apzc is different, then
// content should have found a scrollable apzc, so we don't need to handle
// that case.
RefPtr<AsyncPanZoomController> apzc =
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent);
if (apzc && apzc != GetTargetApzc()) {
UpdateTargetApzc(apzc);
}
}
}
bool
PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
// The APZC that we find via APZCCallbackHelpers may not be the same APZC
// ESM or OverscrollHandoff would have computed. Make sure we get the right
// one by looking for the first apzc the next pending event can scroll.
RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
if (apzc && mEvents.Length() > 0) {
const PanGestureInput& event = mEvents.ElementAt(0);
RefPtr<AsyncPanZoomController> scrollableApzc =
apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
if (scrollableApzc) {
apzc = scrollableApzc;
}
}
InputBlockState::SetConfirmedTargetApzc(apzc, aState);
return true;
}
void
PanGestureBlockState::AddEvent(const PanGestureInput& aEvent)
{
mEvents.AppendElement(aEvent);
}
bool
PanGestureBlockState::HasEvents() const
{
return !mEvents.IsEmpty();
}
void
PanGestureBlockState::DropEvents()
{
TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
mEvents.Clear();
}
void
PanGestureBlockState::HandleEvents()
{
while (HasEvents()) {
TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
PanGestureInput event = mEvents[0];
mEvents.RemoveElementAt(0);
DispatchEvent(event);
}
}
bool
PanGestureBlockState::MustStayActive()
{
return !mInterrupted;
}
const char*
PanGestureBlockState::Type()
{
return "pan gesture";
}
bool
PanGestureBlockState::SetContentResponse(bool aPreventDefault)
{
if (aPreventDefault) {
TBS_LOG("%p setting interrupted flag\n", this);
mInterrupted = true;
}
bool stateChanged = CancelableBlockState::SetContentResponse(aPreventDefault);
if (mWaitingForContentResponse) {
mWaitingForContentResponse = false;
stateChanged = true;
}
return stateChanged;
}
bool
PanGestureBlockState::HasReceivedAllContentNotifications() const
{
return CancelableBlockState::HasReceivedAllContentNotifications()
&& !mWaitingForContentResponse;
}
bool
PanGestureBlockState::IsReadyForHandling() const
{
if (!CancelableBlockState::IsReadyForHandling()) {
return false;
}
return !mWaitingForContentResponse ||
IsContentResponseTimerExpired();
}
bool
PanGestureBlockState::AllowScrollHandoff() const
{
return false;
}
void
PanGestureBlockState::SetNeedsToWaitForContentResponse(bool aWaitForContentResponse)
{
mWaitingForContentResponse = aWaitForContentResponse;
}
TouchBlockState::TouchBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed, TouchCounter& aCounter)
: CancelableBlockState(aTargetApzc, aTargetConfirmed)
, mAllowedTouchBehaviorSet(false)
, mDuringFastFling(false)
, mSingleTapOccurred(false)
, mInSlop(false)
, mTouchCounter(aCounter)
{
TBS_LOG("Creating %p\n", this);
}
bool
TouchBlockState::SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors)
{
if (mAllowedTouchBehaviorSet) {
return false;
}
TBS_LOG("%p got allowed touch behaviours for %" PRIuSIZE " points\n", this, aBehaviors.Length());
mAllowedTouchBehaviors.AppendElements(aBehaviors);
mAllowedTouchBehaviorSet = true;
return true;
}
bool
TouchBlockState::GetAllowedTouchBehaviors(nsTArray<TouchBehaviorFlags>& aOutBehaviors) const
{
if (!mAllowedTouchBehaviorSet) {
return false;
}
aOutBehaviors.AppendElements(mAllowedTouchBehaviors);
return true;
}
void
TouchBlockState::CopyPropertiesFrom(const TouchBlockState& aOther)
{
TBS_LOG("%p copying properties from %p\n", this, &aOther);
if (gfxPrefs::TouchActionEnabled()) {
MOZ_ASSERT(aOther.mAllowedTouchBehaviorSet || aOther.IsContentResponseTimerExpired());
SetAllowedTouchBehaviors(aOther.mAllowedTouchBehaviors);
}
mTransformToApzc = aOther.mTransformToApzc;
}
bool
TouchBlockState::HasReceivedAllContentNotifications() const
{
return CancelableBlockState::HasReceivedAllContentNotifications()
&& (!gfxPrefs::TouchActionEnabled() || mAllowedTouchBehaviorSet);
}
bool
TouchBlockState::IsReadyForHandling() const
{
if (!CancelableBlockState::IsReadyForHandling()) {
return false;
}
if (!gfxPrefs::TouchActionEnabled()) {
return true;
}
return mAllowedTouchBehaviorSet || IsContentResponseTimerExpired();
}
void
TouchBlockState::SetDuringFastFling()
{
TBS_LOG("%p setting fast-motion flag\n", this);
mDuringFastFling = true;
}
bool
TouchBlockState::IsDuringFastFling() const
{
return mDuringFastFling;
}
void
TouchBlockState::SetSingleTapOccurred()
{
TBS_LOG("%p setting single-tap-occurred flag\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);
}
bool
TouchBlockState::MustStayActive()
{
return true;
}
const char*
TouchBlockState::Type()
{
return "touch";
}
void
TouchBlockState::DropEvents()
{
TBS_LOG("%p dropping %" PRIuSIZE " events\n", this, mEvents.Length());
mEvents.Clear();
}
void
TouchBlockState::HandleEvents()
{
while (HasEvents()) {
TBS_LOG("%p returning first of %" PRIuSIZE " events\n", this, mEvents.Length());
MultiTouchInput event = mEvents[0];
mEvents.RemoveElementAt(0);
DispatchEvent(event);
}
}
void
TouchBlockState::DispatchEvent(const InputData& aEvent) const
{
MOZ_ASSERT(aEvent.mInputType == MULTITOUCH_INPUT);
mTouchCounter.Update(aEvent.AsMultiTouchInput());
CancelableBlockState::DispatchEvent(aEvent);
}
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()) {
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN);
}
bool
TouchBlockState::TouchActionAllowsPanningY() const
{
if (!gfxPrefs::TouchActionEnabled()) {
return true;
}
if (mAllowedTouchBehaviors.IsEmpty()) {
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::VERTICAL_PAN);
}
bool
TouchBlockState::TouchActionAllowsPanningXY() const
{
if (!gfxPrefs::TouchActionEnabled()) {
return true;
}
if (mAllowedTouchBehaviors.IsEmpty()) {
// Default to allowed
return true;
}
TouchBehaviorFlags flags = mAllowedTouchBehaviors[0];
return (flags & AllowedTouchBehavior::HORIZONTAL_PAN)
&& (flags & AllowedTouchBehavior::VERTICAL_PAN);
}
bool
TouchBlockState::UpdateSlopState(const MultiTouchInput& aInput,
bool aApzcCanConsumeEvents)
{
if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
// this is by definition the first event in this block. If it's the first
// touch, then we enter a slop state.
mInSlop = (aInput.mTouches.Length() == 1);
if (mInSlop) {
mSlopOrigin = aInput.mTouches[0].mScreenPoint;
TBS_LOG("%p entering slop with origin %s\n", this, Stringify(mSlopOrigin).c_str());
}
return false;
}
if (mInSlop) {
ScreenCoord threshold = aApzcCanConsumeEvents
? AsyncPanZoomController::GetTouchStartTolerance()
: ScreenCoord(gfxPrefs::APZTouchMoveTolerance() * APZCTreeManager::GetDPI());
bool stayInSlop = (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) &&
(aInput.mTouches.Length() == 1) &&
((aInput.mTouches[0].mScreenPoint - mSlopOrigin).Length() < threshold);
if (!stayInSlop) {
// we're out of the slop zone, and will stay out for the remainder of
// this block
TBS_LOG("%p exiting slop\n", this);
mInSlop = false;
}
}
return mInSlop;
}
uint32_t
TouchBlockState::GetActiveTouchCount() const
{
return mTouchCounter.GetActiveTouchCount();
}
} // namespace layers
} // namespace mozilla