diff --git a/gfx/layers/wr/ClipManager.cpp b/gfx/layers/wr/ClipManager.cpp index 72c6bb9ff709..12e41bfa5880 100644 --- a/gfx/layers/wr/ClipManager.cpp +++ b/gfx/layers/wr/ClipManager.cpp @@ -147,7 +147,16 @@ wr::WrSpaceAndClipChain ClipManager::SwitchItem(nsDisplayItem* aItem) { // purposes we always want to use the ASR that would have been used if it // didn't have fixed descendants, which is stored as the "container ASR" on // the sticky item. - asr = static_cast(aItem)->GetContainerASR(); + nsDisplayStickyPosition* sticky = + static_cast(aItem); + asr = sticky->GetContainerASR(); + + // If the leafmost clip for the sticky item is just the displayport clip, + // then skip it. This allows sticky items to remain visible even if the + // rest of the content in the enclosing scrollframe is checkerboarding. + if (sticky->IsClippedToDisplayPort() && clip && clip->mASR == asr) { + clip = clip->mParent; + } } // In most cases we can combine the leaf of the clip chain with the clip rect diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 817062f57fcd..ce8f21d04d8e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -3893,7 +3893,8 @@ void nsIFrame::BuildDisplayListForStackingContext( containerItemASR, aBuilder->CurrentActiveScrolledRoot()); resultList.AppendNewToTop( aBuilder, this, &resultList, stickyASR, - aBuilder->CurrentActiveScrolledRoot()); + aBuilder->CurrentActiveScrolledRoot(), + clipState.IsClippedToDisplayPort()); ct.TrackContainer(resultList.GetTop()); // If the sticky element is inside a filter, annotate the scroll frame that diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 503ecabd5260..7a05be48f24e 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3636,6 +3636,7 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, GetUnsnappedScrolledRectInternal( mScrolledFrame->GetScrollableOverflowRect(), mScrollPort.Size()) + mScrolledFrame->GetPosition(); + bool clippedToDisplayPort = false; if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) { // Clip the contents to the display port. // The dirty rect already acts kind of like a clip, in that @@ -3654,9 +3655,16 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, // If there is no display port, we don't need this because the clip // from the scroll port is still applied. scrolledRectClip = scrolledRectClip.Intersect(visibleRect); + clippedToDisplayPort = scrolledRectClip.IsEqualEdges(visibleRect); } scrolledRectClipState.ClipContainingBlockDescendants( scrolledRectClip + aBuilder->ToReferenceFrame(mOuter)); + if (clippedToDisplayPort) { + // We have to do this after the ClipContainingBlockDescendants call + // above, otherwise that call will clobber the flag set by this call + // to SetClippedToDisplayPort. + scrolledRectClipState.SetClippedToDisplayPort(); + } nsDisplayListBuilder::AutoBuildingDisplayList building( aBuilder, mOuter, visibleRect, dirtyRect); diff --git a/layout/painting/DisplayListClipState.cpp b/layout/painting/DisplayListClipState.cpp index c9f897761036..8a87e2906b85 100644 --- a/layout/painting/DisplayListClipState.cpp +++ b/layout/painting/DisplayListClipState.cpp @@ -106,6 +106,7 @@ void DisplayListClipState::ClipContentDescendants( void DisplayListClipState::InvalidateCurrentCombinedClipChain( const ActiveScrolledRoot* aInvalidateUpTo) { + mClippedToDisplayPort = false; mCurrentCombinedClipChainIsValid = false; while (mCurrentCombinedClipChain && ActiveScrolledRoot::IsAncestor(aInvalidateUpTo, diff --git a/layout/painting/DisplayListClipState.h b/layout/painting/DisplayListClipState.h index f678a6196d46..4a9e8875d658 100644 --- a/layout/painting/DisplayListClipState.h +++ b/layout/painting/DisplayListClipState.h @@ -28,7 +28,11 @@ class DisplayListClipState { : mClipChainContentDescendants(nullptr), mClipChainContainingBlockDescendants(nullptr), mCurrentCombinedClipChain(nullptr), - mCurrentCombinedClipChainIsValid(false) {} + mCurrentCombinedClipChainIsValid(false), + mClippedToDisplayPort(false) {} + + void SetClippedToDisplayPort() { mClippedToDisplayPort = true; } + bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; } /** * Returns intersection of mClipChainContainingBlockDescendants and @@ -64,6 +68,7 @@ class DisplayListClipState { mClipChainContainingBlockDescendants = nullptr; mCurrentCombinedClipChain = nullptr; mCurrentCombinedClipChainIsValid = false; + mClippedToDisplayPort = false; } void SetClipChainForContainingBlockDescendants( @@ -127,6 +132,11 @@ class DisplayListClipState { */ const DisplayItemClipChain* mCurrentCombinedClipChain; bool mCurrentCombinedClipChainIsValid; + /** + * A flag that is used by sticky positioned items to know if the clip applied + * to them is just the displayport clip or if there is additional clipping. + */ + bool mClippedToDisplayPort; }; /** @@ -215,6 +225,11 @@ class DisplayListClipState::AutoSaveRestore { mClipChain, aFlags); } + void SetClippedToDisplayPort() { mState.SetClippedToDisplayPort(); } + bool IsClippedToDisplayPort() const { + return mState.IsClippedToDisplayPort(); + } + protected: nsDisplayListBuilder* mBuilder; DisplayListClipState& mState; diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index acaf2f01dfcc..02a3424b9781 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -4755,10 +4755,14 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) { clipPtr = &clipRectUntyped; } + bool isStickyNotClippedToDisplayPort = + itemType == DisplayItemType::TYPE_STICKY_POSITION && + !static_cast(item) + ->IsClippedToDisplayPort(); bool hasScrolledClip = layerClipChain && layerClipChain->mClip.HasClip() && (!ActiveScrolledRoot::IsAncestor(layerClipChain->mASR, itemASR) || - itemType == DisplayItemType::TYPE_STICKY_POSITION); + isStickyNotClippedToDisplayPort); if (hasScrolledClip) { // If the clip is scrolled, reserve just the area of the clip for diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 355b200b8b12..97f20df8302f 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -7382,9 +7382,10 @@ nsDisplayTableFixedPosition::CreateForFixedBackground( nsDisplayStickyPosition::nsDisplayStickyPosition( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, - const ActiveScrolledRoot* aContainerASR) + const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort) : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot), - mContainerASR(aContainerASR) { + mContainerASR(aContainerASR), + mClippedToDisplayPort(aClippedToDisplayPort) { MOZ_COUNT_CTOR(nsDisplayStickyPosition); } diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index f101b25727ef..f34ab4350712 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -6197,11 +6197,13 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const ActiveScrolledRoot* aActiveScrolledRoot, - const ActiveScrolledRoot* aContainerASR); + const ActiveScrolledRoot* aContainerASR, + bool aClippedToDisplayPort); nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, const nsDisplayStickyPosition& aOther) : nsDisplayOwnLayer(aBuilder, aOther), - mContainerASR(aOther.mContainerASR) { + mContainerASR(aOther.mContainerASR), + mClippedToDisplayPort(aOther.mClippedToDisplayPort) { MOZ_COUNT_CTOR(nsDisplayStickyPosition); } @@ -6209,6 +6211,7 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { void SetClipChain(const DisplayItemClipChain* aClipChain, bool aStore) override; + bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; } already_AddRefed BuildLayer( nsDisplayListBuilder* aBuilder, LayerManager* aManager, @@ -6248,6 +6251,17 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { // has no fixed descendants. This may be the same as the ASR returned by // GetActiveScrolledRoot(), or it may be a descendant of that. RefPtr mContainerASR; + // This flag tracks if this sticky item is just clipped to the enclosing + // scrollframe's displayport, or if there are additional clips in play. In + // the former case, we can skip setting the displayport clip as the scrolled- + // clip of the corresponding layer. This allows sticky items to remain + // unclipped when the enclosing scrollframe is scrolled past the displayport. + // i.e. when the rest of the scrollframe checkerboards, the sticky item will + // not. This makes sense to do because the sticky item has abnormal scrolling + // behavior and may still be visible even if the rest of the scrollframe is + // checkerboarded. Note that the sticky item will still be subject to the + // scrollport clip. + bool mClippedToDisplayPort; }; class nsDisplayFixedPosition : public nsDisplayOwnLayer {