1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "InputBlockState.h"
10 #include "AsyncPanZoomController.h" // for AsyncPanZoomController
12 #include "mozilla/MouseEvents.h"
13 #include "mozilla/StaticPrefs_apz.h"
14 #include "mozilla/StaticPrefs_layout.h"
15 #include "mozilla/StaticPrefs_mousewheel.h"
16 #include "mozilla/StaticPrefs_test.h"
17 #include "mozilla/Telemetry.h" // for Telemetry
18 #include "mozilla/ToString.h"
19 #include "mozilla/layers/IAPZCTreeManager.h" // for AllowedTouchBehavior
20 #include "OverscrollHandoffState.h"
21 #include "QueuedInput.h"
23 static mozilla::LazyLogModule
sApzIbsLog("apz.inputstate");
24 #define TBS_LOG(...) MOZ_LOG(sApzIbsLog, LogLevel::Debug, (__VA_ARGS__))
29 static uint64_t sBlockCounter
= InputBlockState::NO_BLOCK_ID
+ 1;
31 InputBlockState::InputBlockState(
32 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
33 TargetConfirmationFlags aFlags
)
34 : mTargetApzc(aTargetApzc
),
35 mRequiresTargetConfirmation(aFlags
.mRequiresTargetConfirmation
),
36 mBlockId(sBlockCounter
++),
37 mTransformToApzc(aTargetApzc
->GetTransformToThis()) {
38 // We should never be constructed with a nullptr target.
39 MOZ_ASSERT(mTargetApzc
);
40 mOverscrollHandoffChain
= mTargetApzc
->BuildOverscrollHandoffChain();
41 // If a new block starts on a scrollthumb and we have APZ scrollbar
42 // dragging enabled, defer confirmation until we get the drag metrics
44 bool startingDrag
= StaticPrefs::apz_drag_enabled() && aFlags
.mHitScrollThumb
;
45 mTargetConfirmed
= aFlags
.mTargetConfirmed
&& !startingDrag
46 ? TargetConfirmationState::eConfirmed
47 : TargetConfirmationState::eUnconfirmed
;
50 bool InputBlockState::SetConfirmedTargetApzc(
51 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
52 TargetConfirmationState aState
, InputData
* aFirstInput
,
53 bool aForScrollbarDrag
) {
54 MOZ_ASSERT(aState
== TargetConfirmationState::eConfirmed
||
55 aState
== TargetConfirmationState::eTimedOut
);
57 // Sometimes, bugs in compositor hit testing can lead to APZ confirming
58 // a different target than the main thread. If this happens for a drag
59 // block created for a scrollbar drag, the consequences can be fairly
60 // user-unfriendly, such as the scrollbar not being draggable at all,
61 // or it scrolling the contents of the wrong scrollframe. In debug
62 // builds, we assert in this situation, so that the
63 // underlying compositor hit testing bug can be fixed. In release builds,
64 // however, we just silently accept the main thread's confirmed target,
65 // which will produce the expected behaviour (apart from drag events
66 // received so far being dropped).
67 if (AsDragBlock() && aForScrollbarDrag
&&
68 mTargetConfirmed
== TargetConfirmationState::eConfirmed
&&
69 aState
== TargetConfirmationState::eConfirmed
&& mTargetApzc
&&
70 aTargetApzc
&& mTargetApzc
->GetGuid() != aTargetApzc
->GetGuid()) {
72 "APZ and main thread confirmed scrollbar drag block with "
74 UpdateTargetApzc(aTargetApzc
);
78 if (mTargetConfirmed
!= TargetConfirmationState::eUnconfirmed
) {
81 mTargetConfirmed
= aState
;
83 TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc
.get());
84 if (mTargetApzc
== aTargetApzc
) {
85 // The confirmed target is the same as the tentative one, so we're done.
89 TBS_LOG("%p replacing unconfirmed target %p with real target %p\n", this,
90 mTargetApzc
.get(), aTargetApzc
.get());
92 UpdateTargetApzc(aTargetApzc
);
96 void InputBlockState::UpdateTargetApzc(
97 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
) {
98 if (mTargetApzc
== aTargetApzc
) {
99 MOZ_ASSERT_UNREACHABLE(
100 "The new target APZC should be different from the old one");
105 // Restore overscroll state on the previous target APZC and ancestor APZCs
106 // in the scroll handoff chain other than the new one.
107 mTargetApzc
->SnapBackIfOverscrolled();
109 uint32_t i
= mOverscrollHandoffChain
->IndexOf(mTargetApzc
) + 1;
110 for (; i
< mOverscrollHandoffChain
->Length(); i
++) {
111 AsyncPanZoomController
* apzc
= mOverscrollHandoffChain
->GetApzcAtIndex(i
);
112 if (apzc
!= aTargetApzc
) {
113 MOZ_ASSERT(!apzc
->IsOverscrolled() ||
114 apzc
->IsOverscrollAnimationRunning());
115 apzc
->SnapBackIfOverscrolled();
120 // note that aTargetApzc MAY be null here.
121 mTargetApzc
= aTargetApzc
;
122 mTransformToApzc
= aTargetApzc
? aTargetApzc
->GetTransformToThis()
123 : ScreenToParentLayerMatrix4x4();
124 mOverscrollHandoffChain
=
125 (mTargetApzc
? mTargetApzc
->BuildOverscrollHandoffChain() : nullptr);
128 const RefPtr
<AsyncPanZoomController
>& InputBlockState::GetTargetApzc() const {
132 const RefPtr
<const OverscrollHandoffChain
>&
133 InputBlockState::GetOverscrollHandoffChain() const {
134 return mOverscrollHandoffChain
;
137 uint64_t InputBlockState::GetBlockId() const { return mBlockId
; }
139 bool InputBlockState::IsTargetConfirmed() const {
140 return mTargetConfirmed
!= TargetConfirmationState::eUnconfirmed
;
143 bool InputBlockState::ShouldDropEvents() const {
144 return mRequiresTargetConfirmation
&&
145 (mTargetConfirmed
!= TargetConfirmationState::eConfirmed
);
148 bool InputBlockState::IsDownchainOf(AsyncPanZoomController
* aA
,
149 AsyncPanZoomController
* aB
) const {
155 for (size_t i
= 0; i
< mOverscrollHandoffChain
->Length(); ++i
) {
156 AsyncPanZoomController
* apzc
= mOverscrollHandoffChain
->GetApzcAtIndex(i
);
167 void InputBlockState::SetScrolledApzc(AsyncPanZoomController
* aApzc
) {
168 // An input block should only have one scrolled APZC.
169 MOZ_ASSERT(!mScrolledApzc
|| (StaticPrefs::apz_allow_immediate_handoff()
170 ? IsDownchainOf(mScrolledApzc
, aApzc
)
171 : mScrolledApzc
== aApzc
));
173 mScrolledApzc
= aApzc
;
176 AsyncPanZoomController
* InputBlockState::GetScrolledApzc() const {
177 return mScrolledApzc
;
180 bool InputBlockState::IsDownchainOfScrolledApzc(
181 AsyncPanZoomController
* aApzc
) const {
182 MOZ_ASSERT(aApzc
&& mScrolledApzc
);
184 return IsDownchainOf(mScrolledApzc
, aApzc
);
187 void InputBlockState::DispatchEvent(const InputData
& aEvent
) const {
188 GetTargetApzc()->HandleInputEvent(aEvent
, mTransformToApzc
);
191 CancelableBlockState::CancelableBlockState(
192 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
193 TargetConfirmationFlags aFlags
)
194 : InputBlockState(aTargetApzc
, aFlags
),
195 mPreventDefault(false),
196 mContentResponded(false),
197 mContentResponseTimerExpired(false) {}
199 bool CancelableBlockState::SetContentResponse(bool aPreventDefault
) {
200 if (mContentResponded
) {
203 TBS_LOG("%p got content response %d with timer expired %d\n", this,
204 aPreventDefault
, mContentResponseTimerExpired
);
205 mPreventDefault
= aPreventDefault
;
206 mContentResponded
= true;
210 bool CancelableBlockState::TimeoutContentResponse() {
211 if (mContentResponseTimerExpired
) {
214 TBS_LOG("%p got content timer expired with response received %d\n", this,
216 if (!mContentResponded
) {
217 mPreventDefault
= false;
219 mContentResponseTimerExpired
= true;
223 bool CancelableBlockState::IsContentResponseTimerExpired() const {
224 return mContentResponseTimerExpired
;
227 bool CancelableBlockState::IsDefaultPrevented() const {
228 MOZ_ASSERT(mContentResponded
|| mContentResponseTimerExpired
);
229 return mPreventDefault
;
232 bool CancelableBlockState::IsReadyForHandling() const {
233 if (!IsTargetConfirmed()) {
236 return mContentResponded
|| mContentResponseTimerExpired
;
239 bool CancelableBlockState::ShouldDropEvents() const {
240 return InputBlockState::ShouldDropEvents() || IsDefaultPrevented();
243 DragBlockState::DragBlockState(
244 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
245 TargetConfirmationFlags aFlags
, const MouseInput
& aInitialEvent
)
246 : CancelableBlockState(aTargetApzc
, aFlags
), mReceivedMouseUp(false) {}
248 bool DragBlockState::HasReceivedMouseUp() { return mReceivedMouseUp
; }
250 void DragBlockState::MarkMouseUpReceived() { mReceivedMouseUp
= true; }
252 void DragBlockState::SetInitialThumbPos(OuterCSSCoord aThumbPos
) {
253 mInitialThumbPos
= aThumbPos
;
256 void DragBlockState::SetDragMetrics(const AsyncDragMetrics
& aDragMetrics
,
257 const CSSRect
& aScrollableRect
) {
258 mDragMetrics
= aDragMetrics
;
259 mInitialScrollableRect
= aScrollableRect
;
262 void DragBlockState::DispatchEvent(const InputData
& aEvent
) const {
263 MouseInput mouseInput
= aEvent
.AsMouseInput();
264 if (!mouseInput
.TransformToLocal(mTransformToApzc
)) {
268 GetTargetApzc()->HandleDragEvent(mouseInput
, mDragMetrics
, mInitialThumbPos
,
269 mInitialScrollableRect
);
272 bool DragBlockState::MustStayActive() { return !mReceivedMouseUp
; }
274 const char* DragBlockState::Type() { return "drag"; }
275 // This is used to track the current wheel transaction.
276 static uint64_t sLastWheelBlockId
= InputBlockState::NO_BLOCK_ID
;
278 WheelBlockState::WheelBlockState(
279 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
280 TargetConfirmationFlags aFlags
, const ScrollWheelInput
& aInitialEvent
)
281 : CancelableBlockState(aTargetApzc
, aFlags
),
282 mScrollSeriesCounter(0),
283 mTransactionEnded(false) {
284 sLastWheelBlockId
= GetBlockId();
286 if (aFlags
.mTargetConfirmed
) {
287 // Find the nearest APZC in the overscroll handoff chain that is scrollable.
288 // If we get a content confirmation later that the apzc is different, then
289 // content should have found a scrollable apzc, so we don't need to handle
291 RefPtr
<AsyncPanZoomController
> apzc
=
292 mOverscrollHandoffChain
->FindFirstScrollable(aInitialEvent
,
293 &mAllowedScrollDirections
);
296 if (apzc
!= GetTargetApzc()) {
297 UpdateTargetApzc(apzc
);
299 } else if (!mOverscrollHandoffChain
->CanBePanned(
300 mOverscrollHandoffChain
->GetApzcAtIndex(0))) {
301 // If there's absolutely nothing scrollable start a transaction and mark
302 // this as such to we know to store our EventTime.
303 mIsScrollable
= false;
305 // Scrollable, but not in this direction.
311 bool WheelBlockState::SetContentResponse(bool aPreventDefault
) {
312 if (aPreventDefault
) {
315 return CancelableBlockState::SetContentResponse(aPreventDefault
);
318 bool WheelBlockState::SetConfirmedTargetApzc(
319 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
320 TargetConfirmationState aState
, InputData
* aFirstInput
,
321 bool aForScrollbarDrag
) {
322 // The APZC that we find via APZCCallbackHelpers may not be the same APZC
323 // ESM or OverscrollHandoff would have computed. Make sure we get the right
324 // one by looking for the first apzc the next pending event can scroll.
325 RefPtr
<AsyncPanZoomController
> apzc
= aTargetApzc
;
326 if (apzc
&& aFirstInput
) {
327 apzc
= apzc
->BuildOverscrollHandoffChain()->FindFirstScrollable(
328 *aFirstInput
, &mAllowedScrollDirections
);
331 InputBlockState::SetConfirmedTargetApzc(apzc
, aState
, aFirstInput
,
336 void WheelBlockState::Update(ScrollWheelInput
& aEvent
) {
337 // We might not be in a transaction if the block never started in a
338 // transaction - for example, if nothing was scrollable.
339 if (!InTransaction()) {
343 // The current "scroll series" is a like a sub-transaction. It has a separate
344 // timeout of 80ms. Since we need to compute wheel deltas at different phases
345 // of a transaction (for example, when it is updated, and later when the
346 // event action is taken), we affix the scroll series counter to the event.
347 // This makes GetScrollWheelDelta() consistent.
348 if (!mLastEventTime
.IsNull() &&
349 (aEvent
.mTimeStamp
- mLastEventTime
).ToMilliseconds() >
350 StaticPrefs::mousewheel_scroll_series_timeout()) {
351 mScrollSeriesCounter
= 0;
353 aEvent
.mScrollSeriesNumber
= ++mScrollSeriesCounter
;
355 // If we can't scroll in the direction of the wheel event, we don't update
356 // the last move time. This allows us to timeout a transaction even if the
357 // mouse isn't moving.
359 // We skip this check if the target is not yet confirmed, so that when it is
360 // confirmed, we don't timeout the transaction.
361 RefPtr
<AsyncPanZoomController
> apzc
= GetTargetApzc();
362 if (mIsScrollable
&& IsTargetConfirmed() && !apzc
->CanScroll(aEvent
)) {
366 // Update the time of the last known good event, and reset the mouse move
367 // time to null. This will reset the delays on both the general transaction
368 // timeout and the mouse-move-in-frame timeout.
369 mLastEventTime
= aEvent
.mTimeStamp
;
370 mLastMouseMove
= TimeStamp();
373 bool WheelBlockState::MustStayActive() { return !mTransactionEnded
; }
375 const char* WheelBlockState::Type() { return "scroll wheel"; }
377 bool WheelBlockState::ShouldAcceptNewEvent() const {
378 if (!InTransaction()) {
379 // If we're not in a transaction, start a new one.
383 RefPtr
<AsyncPanZoomController
> apzc
= GetTargetApzc();
384 if (apzc
->IsDestroyed()) {
391 bool WheelBlockState::MaybeTimeout(const ScrollWheelInput
& aEvent
) {
392 MOZ_ASSERT(InTransaction());
394 if (MaybeTimeout(aEvent
.mTimeStamp
)) {
398 if (!mLastMouseMove
.IsNull()) {
399 // If there's a recent mouse movement, we can time out the transaction
401 TimeDuration duration
= TimeStamp::Now() - mLastMouseMove
;
402 if (duration
.ToMilliseconds() >=
403 StaticPrefs::mousewheel_transaction_ignoremovedelay()) {
404 TBS_LOG("%p wheel transaction timed out after mouse move\n", this);
413 bool WheelBlockState::MaybeTimeout(const TimeStamp
& aTimeStamp
) {
414 MOZ_ASSERT(InTransaction());
416 // End the transaction if the event occurred > 1.5s after the most recently
418 TimeDuration duration
= aTimeStamp
- mLastEventTime
;
419 if (duration
.ToMilliseconds() <
420 StaticPrefs::mousewheel_transaction_timeout()) {
424 TBS_LOG("%p wheel transaction timed out\n", this);
426 if (StaticPrefs::test_mousescroll()) {
427 RefPtr
<AsyncPanZoomController
> apzc
= GetTargetApzc();
428 apzc
->NotifyMozMouseScrollEvent(u
"MozMouseScrollTransactionTimeout"_ns
);
435 void WheelBlockState::OnMouseMove(
436 const ScreenIntPoint
& aPoint
,
437 const Maybe
<ScrollableLayerGuid
>& aTargetGuid
) {
438 MOZ_ASSERT(InTransaction());
440 if (!GetTargetApzc()->Contains(aPoint
) ||
441 // If the mouse moved over to a different APZC, `mIsScrollable`
442 // may no longer be false and needs to be recomputed.
443 (!mIsScrollable
&& aTargetGuid
.isSome() &&
444 aTargetGuid
.value() != GetTargetApzc()->GetGuid())) {
449 if (mLastMouseMove
.IsNull()) {
450 // If the cursor is moving inside the frame, and it is more than the
451 // ignoremovedelay time since the last scroll operation, we record
452 // this as the most recent mouse movement.
453 TimeStamp now
= TimeStamp::Now();
454 TimeDuration duration
= now
- mLastEventTime
;
455 if (duration
.ToMilliseconds() >=
456 StaticPrefs::mousewheel_transaction_ignoremovedelay()) {
457 mLastMouseMove
= now
;
462 void WheelBlockState::UpdateTargetApzc(
463 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
) {
464 InputBlockState::UpdateTargetApzc(aTargetApzc
);
466 // If we found there was no target apzc, then we end the transaction.
467 if (!GetTargetApzc()) {
472 bool WheelBlockState::InTransaction() const {
473 // We consider a wheel block to be in a transaction if it has a confirmed
474 // target and is the most recent wheel input block to be created.
475 if (GetBlockId() != sLastWheelBlockId
) {
479 if (mTransactionEnded
) {
483 MOZ_ASSERT(GetTargetApzc());
487 bool WheelBlockState::AllowScrollHandoff() const {
488 // If we're in a wheel transaction, we do not allow overscroll handoff until
489 // a new event ends the wheel transaction.
490 return !IsTargetConfirmed() || !InTransaction();
493 void WheelBlockState::EndTransaction() {
494 TBS_LOG("%p ending wheel transaction\n", this);
495 mTransactionEnded
= true;
498 PanGestureBlockState::PanGestureBlockState(
499 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
500 TargetConfirmationFlags aFlags
, const PanGestureInput
& aInitialEvent
)
501 : CancelableBlockState(aTargetApzc
, aFlags
),
503 mWaitingForContentResponse(false),
504 mWaitingForBrowserGestureResponse(false),
505 mStartedBrowserGesture(false) {
506 if (aFlags
.mTargetConfirmed
) {
507 // Find the nearest APZC in the overscroll handoff chain that is scrollable.
508 // If we get a content confirmation later that the apzc is different, then
509 // content should have found a scrollable apzc, so we don't need to handle
511 RefPtr
<AsyncPanZoomController
> apzc
=
512 mOverscrollHandoffChain
->FindFirstScrollable(aInitialEvent
,
513 &mAllowedScrollDirections
);
515 if (apzc
&& apzc
!= GetTargetApzc()) {
516 UpdateTargetApzc(apzc
);
521 bool PanGestureBlockState::SetConfirmedTargetApzc(
522 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
523 TargetConfirmationState aState
, InputData
* aFirstInput
,
524 bool aForScrollbarDrag
) {
525 // The APZC that we find via APZCCallbackHelpers may not be the same APZC
526 // ESM or OverscrollHandoff would have computed. Make sure we get the right
527 // one by looking for the first apzc the next pending event can scroll.
528 RefPtr
<AsyncPanZoomController
> apzc
= aTargetApzc
;
529 if (apzc
&& aFirstInput
) {
530 RefPtr
<AsyncPanZoomController
> scrollableApzc
=
531 apzc
->BuildOverscrollHandoffChain()->FindFirstScrollable(
532 *aFirstInput
, &mAllowedScrollDirections
);
533 if (scrollableApzc
) {
534 apzc
= scrollableApzc
;
538 InputBlockState::SetConfirmedTargetApzc(apzc
, aState
, aFirstInput
,
543 bool PanGestureBlockState::MustStayActive() { return !mInterrupted
; }
545 const char* PanGestureBlockState::Type() { return "pan gesture"; }
547 bool PanGestureBlockState::SetContentResponse(bool aPreventDefault
) {
548 if (aPreventDefault
) {
549 TBS_LOG("%p setting interrupted flag\n", this);
552 bool stateChanged
= CancelableBlockState::SetContentResponse(aPreventDefault
);
553 if (mWaitingForContentResponse
) {
554 mWaitingForContentResponse
= false;
560 bool PanGestureBlockState::IsReadyForHandling() const {
561 if (!CancelableBlockState::IsReadyForHandling()) {
564 return !mWaitingForBrowserGestureResponse
&&
565 (!mWaitingForContentResponse
|| IsContentResponseTimerExpired());
568 bool PanGestureBlockState::ShouldDropEvents() const {
569 return CancelableBlockState::ShouldDropEvents() || mStartedBrowserGesture
;
572 bool PanGestureBlockState::TimeoutContentResponse() {
573 // Reset mWaitingForBrowserGestureResponse here so that we will not wait for
574 // the response forever.
575 mWaitingForBrowserGestureResponse
= false;
576 return CancelableBlockState::TimeoutContentResponse();
579 bool PanGestureBlockState::AllowScrollHandoff() const { return false; }
581 void PanGestureBlockState::SetNeedsToWaitForContentResponse(
582 bool aWaitForContentResponse
) {
583 mWaitingForContentResponse
= aWaitForContentResponse
;
586 void PanGestureBlockState::SetNeedsToWaitForBrowserGestureResponse(
587 bool aWaitForBrowserGestureResponse
) {
588 mWaitingForBrowserGestureResponse
= aWaitForBrowserGestureResponse
;
591 void PanGestureBlockState::SetBrowserGestureResponse(
592 BrowserGestureResponse aResponse
) {
593 mWaitingForBrowserGestureResponse
= false;
594 mStartedBrowserGesture
= bool(aResponse
);
597 PinchGestureBlockState::PinchGestureBlockState(
598 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
599 TargetConfirmationFlags aFlags
)
600 : CancelableBlockState(aTargetApzc
, aFlags
),
602 mWaitingForContentResponse(false) {}
604 bool PinchGestureBlockState::MustStayActive() { return true; }
606 const char* PinchGestureBlockState::Type() { return "pinch gesture"; }
608 bool PinchGestureBlockState::SetContentResponse(bool aPreventDefault
) {
609 if (aPreventDefault
) {
610 TBS_LOG("%p setting interrupted flag\n", this);
613 bool stateChanged
= CancelableBlockState::SetContentResponse(aPreventDefault
);
614 if (mWaitingForContentResponse
) {
615 mWaitingForContentResponse
= false;
621 bool PinchGestureBlockState::IsReadyForHandling() const {
622 if (!CancelableBlockState::IsReadyForHandling()) {
625 return !mWaitingForContentResponse
|| IsContentResponseTimerExpired();
628 void PinchGestureBlockState::SetNeedsToWaitForContentResponse(
629 bool aWaitForContentResponse
) {
630 mWaitingForContentResponse
= aWaitForContentResponse
;
633 TouchBlockState::TouchBlockState(
634 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
,
635 TargetConfirmationFlags aFlags
, TouchCounter
& aCounter
)
636 : CancelableBlockState(aTargetApzc
, aFlags
),
637 mAllowedTouchBehaviorSet(false),
638 mDuringFastFling(false),
639 mSingleTapOccurred(false),
642 mLongTapWasProcessed(false),
643 mTouchCounter(aCounter
),
644 mStartTime(GetTargetApzc()->GetFrameTime().Time()) {
645 mOriginalTargetConfirmedState
= mTargetConfirmed
;
646 TBS_LOG("Creating %p\n", this);
649 bool TouchBlockState::SetAllowedTouchBehaviors(
650 const nsTArray
<TouchBehaviorFlags
>& aBehaviors
) {
651 if (mAllowedTouchBehaviorSet
) {
654 TBS_LOG("%p got allowed touch behaviours for %zu points\n", this,
655 aBehaviors
.Length());
656 mAllowedTouchBehaviors
.AppendElements(aBehaviors
);
657 mAllowedTouchBehaviorSet
= true;
661 bool TouchBlockState::GetAllowedTouchBehaviors(
662 nsTArray
<TouchBehaviorFlags
>& aOutBehaviors
) const {
663 if (!mAllowedTouchBehaviorSet
) {
666 aOutBehaviors
.AppendElements(mAllowedTouchBehaviors
);
670 bool TouchBlockState::HasAllowedTouchBehaviors() const {
671 return mAllowedTouchBehaviorSet
;
674 void TouchBlockState::CopyPropertiesFrom(const TouchBlockState
& aOther
) {
675 TBS_LOG("%p copying properties from %p\n", this, &aOther
);
676 MOZ_ASSERT(aOther
.mAllowedTouchBehaviorSet
||
677 aOther
.IsContentResponseTimerExpired());
678 SetAllowedTouchBehaviors(aOther
.mAllowedTouchBehaviors
);
679 mTransformToApzc
= aOther
.mTransformToApzc
;
682 bool TouchBlockState::IsReadyForHandling() const {
683 if (!CancelableBlockState::IsReadyForHandling()) {
687 return mAllowedTouchBehaviorSet
|| IsContentResponseTimerExpired();
690 void TouchBlockState::SetDuringFastFling() {
691 TBS_LOG("%p setting fast-motion flag\n", this);
692 mDuringFastFling
= true;
695 bool TouchBlockState::IsDuringFastFling() const { return mDuringFastFling
; }
697 void TouchBlockState::SetSingleTapOccurred() {
698 TBS_LOG("%p setting single-tap-occurred flag\n", this);
699 mSingleTapOccurred
= true;
702 bool TouchBlockState::SingleTapOccurred() const { return mSingleTapOccurred
; }
704 bool TouchBlockState::MustStayActive() {
705 // If this touch block is for long-tap, it doesn't need to be active after the
706 // block was processed, it will be taken over by the original touch block
707 // which will stay active.
708 return !mForLongTap
|| !IsReadyForHandling();
711 const char* TouchBlockState::Type() { return "touch"; }
713 TimeDuration
TouchBlockState::GetTimeSinceBlockStart() const {
714 return GetTargetApzc()->GetFrameTime().Time() - mStartTime
;
717 void TouchBlockState::DispatchEvent(const InputData
& aEvent
) const {
718 MOZ_ASSERT(aEvent
.mInputType
== MULTITOUCH_INPUT
);
719 mTouchCounter
.Update(aEvent
.AsMultiTouchInput());
720 CancelableBlockState::DispatchEvent(aEvent
);
723 bool TouchBlockState::TouchActionAllowsPinchZoom() const {
724 // Pointer events specification requires that all touch points allow zoom.
725 for (auto& behavior
: mAllowedTouchBehaviors
) {
726 if (!(behavior
& AllowedTouchBehavior::PINCH_ZOOM
)) {
733 bool TouchBlockState::TouchActionAllowsDoubleTapZoom() const {
734 for (auto& behavior
: mAllowedTouchBehaviors
) {
735 if (!(behavior
& AllowedTouchBehavior::ANIMATING_ZOOM
)) {
742 bool TouchBlockState::TouchActionAllowsPanningX() const {
743 if (mAllowedTouchBehaviors
.IsEmpty()) {
744 // Default to allowed
747 TouchBehaviorFlags flags
= mAllowedTouchBehaviors
[0];
748 return (flags
& AllowedTouchBehavior::HORIZONTAL_PAN
);
751 bool TouchBlockState::TouchActionAllowsPanningY() const {
752 if (mAllowedTouchBehaviors
.IsEmpty()) {
753 // Default to allowed
756 TouchBehaviorFlags flags
= mAllowedTouchBehaviors
[0];
757 return (flags
& AllowedTouchBehavior::VERTICAL_PAN
);
760 bool TouchBlockState::TouchActionAllowsPanningXY() const {
761 if (mAllowedTouchBehaviors
.IsEmpty()) {
762 // Default to allowed
765 TouchBehaviorFlags flags
= mAllowedTouchBehaviors
[0];
766 return (flags
& AllowedTouchBehavior::HORIZONTAL_PAN
) &&
767 (flags
& AllowedTouchBehavior::VERTICAL_PAN
);
770 bool TouchBlockState::UpdateSlopState(const MultiTouchInput
& aInput
,
771 bool aApzcCanConsumeEvents
) {
772 if (aInput
.mType
== MultiTouchInput::MULTITOUCH_START
) {
773 // this is by definition the first event in this block. If it's the first
774 // touch, then we enter a slop state.
775 mInSlop
= (aInput
.mTouches
.Length() == 1);
777 mSlopOrigin
= aInput
.mTouches
[0].mScreenPoint
;
778 TBS_LOG("%p entering slop with origin %s\n", this,
779 ToString(mSlopOrigin
).c_str());
784 ScreenCoord threshold
= 0;
785 // If the target was confirmed to null then the threshold doesn't
786 // matter anyway since the events will never be processed.
787 if (const RefPtr
<AsyncPanZoomController
>& apzc
= GetTargetApzc()) {
788 threshold
= aApzcCanConsumeEvents
? apzc
->GetTouchStartTolerance()
789 : apzc
->GetTouchMoveTolerance();
792 (aInput
.mType
== MultiTouchInput::MULTITOUCH_MOVE
) &&
793 (aInput
.mTouches
.Length() == 1) &&
794 ((aInput
.mTouches
[0].mScreenPoint
- mSlopOrigin
).Length() < threshold
);
796 // we're out of the slop zone, and will stay out for the remainder of
798 TBS_LOG("%p exiting slop\n", this);
805 bool TouchBlockState::IsInSlop() const { return mInSlop
; }
807 Maybe
<ScrollDirection
> TouchBlockState::GetBestGuessPanDirection(
808 const MultiTouchInput
& aInput
) {
809 if (aInput
.mType
!= MultiTouchInput::MULTITOUCH_MOVE
||
810 aInput
.mTouches
.Length() != 1) {
813 ScreenPoint vector
= aInput
.mTouches
[0].mScreenPoint
- mSlopOrigin
;
814 double angle
= atan2(vector
.y
, vector
.x
); // range [-pi, pi]
815 angle
= fabs(angle
); // range [0, pi]
817 double angleThreshold
= TouchActionAllowsPanningXY()
818 ? StaticPrefs::apz_axis_lock_lock_angle()
819 : StaticPrefs::apz_axis_lock_direct_pan_angle();
820 if (apz::IsCloseToHorizontal(angle
, angleThreshold
)) {
821 return Some(ScrollDirection::eHorizontal
);
823 if (apz::IsCloseToVertical(angle
, angleThreshold
)) {
824 return Some(ScrollDirection::eVertical
);
829 uint32_t TouchBlockState::GetActiveTouchCount() const {
830 return mTouchCounter
.GetActiveTouchCount();
833 bool TouchBlockState::IsTargetOriginallyConfirmed() const {
834 return mOriginalTargetConfirmedState
!= TargetConfirmationState::eUnconfirmed
;
837 KeyboardBlockState::KeyboardBlockState(
838 const RefPtr
<AsyncPanZoomController
>& aTargetApzc
)
839 : InputBlockState(aTargetApzc
, TargetConfirmationFlags
{true}) {}
841 } // namespace layers
842 } // namespace mozilla