Bug 1425573 - Require target confirmation for events that hit unlayerized scroll frames with non-default overscroll-behavior. r=kats
MozReview-Commit-ID: K88tXahKg8H
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -75,22 +75,25 @@ CompleteAsyncTransform(const AsyncTransf
{
return ViewAs<AsyncTransformMatrix>(aMatrix,
PixelCastJustification::MultipleAsyncTransforms);
}
struct TargetConfirmationFlags {
explicit TargetConfirmationFlags(bool aTargetConfirmed)
: mTargetConfirmed(aTargetConfirmed)
+ , mRequiresTargetConfirmation(false)
{}
explicit TargetConfirmationFlags(gfx::CompositorHitTestInfo aHitTestInfo)
: mTargetConfirmed(aHitTestInfo != gfx::CompositorHitTestInfo::eInvisibleToHitTest &&
!(aHitTestInfo & gfx::CompositorHitTestInfo::eDispatchToContent))
+ , mRequiresTargetConfirmation(aHitTestInfo & gfx::CompositorHitTestInfo::eRequiresTargetConfirmation)
{}
bool mTargetConfirmed : 1;
+ bool mRequiresTargetConfirmation : 1;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_APZUtils_h
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -22,16 +22,17 @@ namespace layers {
static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationFlags aFlags)
: mTargetApzc(aTargetApzc)
, mTargetConfirmed(aFlags.mTargetConfirmed ? TargetConfirmationState::eConfirmed
: TargetConfirmationState::eUnconfirmed)
+ , mRequiresTargetConfirmation(aFlags.mRequiresTargetConfirmation)
, mBlockId(sBlockCounter++)
, mTransformToApzc(aTargetApzc->GetTransformToThis())
{
// We should never be constructed with a nullptr target.
MOZ_ASSERT(mTargetApzc);
mOverscrollHandoffChain = mTargetApzc->BuildOverscrollHandoffChain();
}
@@ -106,17 +107,17 @@ InputBlockState::HasReceivedRealConfirme
{
return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
}
bool
InputBlockState::ShouldDropEvents() const
{
- return false;
+ return mRequiresTargetConfirmation && (mTargetConfirmed != TargetConfirmationState::eConfirmed);
}
bool
InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const
{
if (aA == aB) {
return true;
}
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -107,16 +107,17 @@ protected:
private:
// Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in
// |mOverscrollHandoffChain|.
bool IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const;
private:
RefPtr<AsyncPanZoomController> mTargetApzc;
TargetConfirmationState mTargetConfirmed;
+ bool mRequiresTargetConfirmation;
const uint64_t mBlockId;
// The APZC that was actually scrolled by events in this input block.
// This is used in configurations where a single input block is only
// allowed to scroll a single APZC (configurations where gfxPrefs::
// APZAllowImmediateHandoff() is false).
// Set the first time an input event in this block scrolls an APZC.
RefPtr<AsyncPanZoomController> mScrolledApzc;
--- a/gfx/src/CompositorHitTestInfo.h
+++ b/gfx/src/CompositorHitTestInfo.h
@@ -41,19 +41,25 @@ enum class CompositorHitTestInfo : uint1
// The frame is a scrollthumb. If this is set then eScrollbar will also be
// set, unless gecko somehow generates a scroll thumb without a containing
// scrollbar.
eScrollbarThumb = 1 << 7,
// If eScrollbar is set, this flag indicates if the scrollbar is a vertical
// one (if set) or a horizontal one (if not set)
eScrollbarVertical = 1 << 8,
+ // Events targeting this frame should only be processed if a target
+ // confirmation is received from the main thread. If no such confirmation
+ // is received within a timeout period, the event may be dropped.
+ // Only meaningful in combination with eDispatchToContent.
+ eRequiresTargetConfirmation = 1 << 9,
+
// Used for IPDL serialization. This bitmask should include all the bits
// that are defined in the enum.
- ALL_BITS = (1 << 9) - 1,
+ ALL_BITS = (1 << 10) - 1,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CompositorHitTestInfo)
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_COMPOSITORHITTESTINFO_H_ */
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3678,16 +3678,27 @@ ScrollFrameHelper::BuildDisplayList(nsDi
// Make sure that APZ will dispatch events back to content so we can create
// a displayport for this frame. We'll add the item later on.
if (!mWillBuildScrollableLayer) {
int32_t zIndex =
MaxZIndexInListOfItemsContainedInFrame(scrolledContent.PositionedDescendants(), mOuter);
if (aBuilder->BuildCompositorHitTestInfo()) {
CompositorHitTestInfo info = CompositorHitTestInfo::eVisibleToHitTest
| CompositorHitTestInfo::eDispatchToContent;
+ // If the scroll frame has non-default overscroll-behavior, instruct
+ // APZ to require a target confirmation before processing events that
+ // hit this scroll frame (that is, to drop the events if a confirmation
+ // does not arrive within the timeout period). Otherwise, APZ's
+ // fallback behaviour of scrolling the enclosing scroll frame would
+ // violate the specified overscroll-behavior.
+ ScrollbarStyles scrollbarStyles = GetScrollbarStylesFromFrame();
+ if (scrollbarStyles.mOverscrollBehaviorX != StyleOverscrollBehavior::Auto ||
+ scrollbarStyles.mOverscrollBehaviorY != StyleOverscrollBehavior::Auto) {
+ info |= CompositorHitTestInfo::eRequiresTargetConfirmation;
+ }
nsDisplayCompositorHitTestInfo* hitInfo =
MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1,
Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter)));
AppendInternalItemToTop(scrolledContent, hitInfo, zIndex);
}
if (aBuilder->IsBuildingLayerEventRegions()) {
nsDisplayLayerEventRegions* inactiveRegionItem =
MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, mScrolledFrame, 1);