Bug 1424714 - Prevent the displayport clip from clipping sticky items. r=mstange
The displayport clip that is applied to sticky items from the enclosing scrollframe can cause sticky items to checkerboard even when they might reasonably be left visible. This is because the displayport clip moves as the enclosing scrollframe is scrolled, but sticky items may remain fixed during such scrolling. The displayport clip can therefore clip out sticky items even if they are "stuck" and should be user-visible. This patch sets a flag to identify when a sticky item is being clipped by the displayport clip, and ensures that it doesn't actually get clipped. In the case where other clips are being applied to the sticky item, we leave the clips unaffected. This allows for other enclosing elements to clip the sticky item as before. Differential Revision: https://phabricator.services.mozilla.com/D68582
This commit is contained in:
@@ -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
|
// 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
|
// didn't have fixed descendants, which is stored as the "container ASR" on
|
||||||
// the sticky item.
|
// the sticky item.
|
||||||
asr = static_cast<nsDisplayStickyPosition*>(aItem)->GetContainerASR();
|
nsDisplayStickyPosition* sticky =
|
||||||
|
static_cast<nsDisplayStickyPosition*>(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
|
// In most cases we can combine the leaf of the clip chain with the clip rect
|
||||||
|
|||||||
@@ -3893,7 +3893,8 @@ void nsIFrame::BuildDisplayListForStackingContext(
|
|||||||
containerItemASR, aBuilder->CurrentActiveScrolledRoot());
|
containerItemASR, aBuilder->CurrentActiveScrolledRoot());
|
||||||
resultList.AppendNewToTop<nsDisplayStickyPosition>(
|
resultList.AppendNewToTop<nsDisplayStickyPosition>(
|
||||||
aBuilder, this, &resultList, stickyASR,
|
aBuilder, this, &resultList, stickyASR,
|
||||||
aBuilder->CurrentActiveScrolledRoot());
|
aBuilder->CurrentActiveScrolledRoot(),
|
||||||
|
clipState.IsClippedToDisplayPort());
|
||||||
ct.TrackContainer(resultList.GetTop());
|
ct.TrackContainer(resultList.GetTop());
|
||||||
|
|
||||||
// If the sticky element is inside a filter, annotate the scroll frame that
|
// If the sticky element is inside a filter, annotate the scroll frame that
|
||||||
|
|||||||
@@ -3636,6 +3636,7 @@ void ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||||||
GetUnsnappedScrolledRectInternal(
|
GetUnsnappedScrolledRectInternal(
|
||||||
mScrolledFrame->GetScrollableOverflowRect(), mScrollPort.Size()) +
|
mScrolledFrame->GetScrollableOverflowRect(), mScrollPort.Size()) +
|
||||||
mScrolledFrame->GetPosition();
|
mScrolledFrame->GetPosition();
|
||||||
|
bool clippedToDisplayPort = false;
|
||||||
if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
|
if (mWillBuildScrollableLayer && aBuilder->IsPaintingToWindow()) {
|
||||||
// Clip the contents to the display port.
|
// Clip the contents to the display port.
|
||||||
// The dirty rect already acts kind of like a clip, in that
|
// 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
|
// If there is no display port, we don't need this because the clip
|
||||||
// from the scroll port is still applied.
|
// from the scroll port is still applied.
|
||||||
scrolledRectClip = scrolledRectClip.Intersect(visibleRect);
|
scrolledRectClip = scrolledRectClip.Intersect(visibleRect);
|
||||||
|
clippedToDisplayPort = scrolledRectClip.IsEqualEdges(visibleRect);
|
||||||
}
|
}
|
||||||
scrolledRectClipState.ClipContainingBlockDescendants(
|
scrolledRectClipState.ClipContainingBlockDescendants(
|
||||||
scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
|
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(
|
nsDisplayListBuilder::AutoBuildingDisplayList building(
|
||||||
aBuilder, mOuter, visibleRect, dirtyRect);
|
aBuilder, mOuter, visibleRect, dirtyRect);
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ void DisplayListClipState::ClipContentDescendants(
|
|||||||
|
|
||||||
void DisplayListClipState::InvalidateCurrentCombinedClipChain(
|
void DisplayListClipState::InvalidateCurrentCombinedClipChain(
|
||||||
const ActiveScrolledRoot* aInvalidateUpTo) {
|
const ActiveScrolledRoot* aInvalidateUpTo) {
|
||||||
|
mClippedToDisplayPort = false;
|
||||||
mCurrentCombinedClipChainIsValid = false;
|
mCurrentCombinedClipChainIsValid = false;
|
||||||
while (mCurrentCombinedClipChain &&
|
while (mCurrentCombinedClipChain &&
|
||||||
ActiveScrolledRoot::IsAncestor(aInvalidateUpTo,
|
ActiveScrolledRoot::IsAncestor(aInvalidateUpTo,
|
||||||
|
|||||||
@@ -28,7 +28,11 @@ class DisplayListClipState {
|
|||||||
: mClipChainContentDescendants(nullptr),
|
: mClipChainContentDescendants(nullptr),
|
||||||
mClipChainContainingBlockDescendants(nullptr),
|
mClipChainContainingBlockDescendants(nullptr),
|
||||||
mCurrentCombinedClipChain(nullptr),
|
mCurrentCombinedClipChain(nullptr),
|
||||||
mCurrentCombinedClipChainIsValid(false) {}
|
mCurrentCombinedClipChainIsValid(false),
|
||||||
|
mClippedToDisplayPort(false) {}
|
||||||
|
|
||||||
|
void SetClippedToDisplayPort() { mClippedToDisplayPort = true; }
|
||||||
|
bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns intersection of mClipChainContainingBlockDescendants and
|
* Returns intersection of mClipChainContainingBlockDescendants and
|
||||||
@@ -64,6 +68,7 @@ class DisplayListClipState {
|
|||||||
mClipChainContainingBlockDescendants = nullptr;
|
mClipChainContainingBlockDescendants = nullptr;
|
||||||
mCurrentCombinedClipChain = nullptr;
|
mCurrentCombinedClipChain = nullptr;
|
||||||
mCurrentCombinedClipChainIsValid = false;
|
mCurrentCombinedClipChainIsValid = false;
|
||||||
|
mClippedToDisplayPort = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetClipChainForContainingBlockDescendants(
|
void SetClipChainForContainingBlockDescendants(
|
||||||
@@ -127,6 +132,11 @@ class DisplayListClipState {
|
|||||||
*/
|
*/
|
||||||
const DisplayItemClipChain* mCurrentCombinedClipChain;
|
const DisplayItemClipChain* mCurrentCombinedClipChain;
|
||||||
bool mCurrentCombinedClipChainIsValid;
|
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);
|
mClipChain, aFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetClippedToDisplayPort() { mState.SetClippedToDisplayPort(); }
|
||||||
|
bool IsClippedToDisplayPort() const {
|
||||||
|
return mState.IsClippedToDisplayPort();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsDisplayListBuilder* mBuilder;
|
nsDisplayListBuilder* mBuilder;
|
||||||
DisplayListClipState& mState;
|
DisplayListClipState& mState;
|
||||||
|
|||||||
@@ -4755,10 +4755,14 @@ void ContainerState::ProcessDisplayItems(nsDisplayList* aList) {
|
|||||||
clipPtr = &clipRectUntyped;
|
clipPtr = &clipRectUntyped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isStickyNotClippedToDisplayPort =
|
||||||
|
itemType == DisplayItemType::TYPE_STICKY_POSITION &&
|
||||||
|
!static_cast<nsDisplayStickyPosition*>(item)
|
||||||
|
->IsClippedToDisplayPort();
|
||||||
bool hasScrolledClip =
|
bool hasScrolledClip =
|
||||||
layerClipChain && layerClipChain->mClip.HasClip() &&
|
layerClipChain && layerClipChain->mClip.HasClip() &&
|
||||||
(!ActiveScrolledRoot::IsAncestor(layerClipChain->mASR, itemASR) ||
|
(!ActiveScrolledRoot::IsAncestor(layerClipChain->mASR, itemASR) ||
|
||||||
itemType == DisplayItemType::TYPE_STICKY_POSITION);
|
isStickyNotClippedToDisplayPort);
|
||||||
|
|
||||||
if (hasScrolledClip) {
|
if (hasScrolledClip) {
|
||||||
// If the clip is scrolled, reserve just the area of the clip for
|
// If the clip is scrolled, reserve just the area of the clip for
|
||||||
|
|||||||
@@ -7382,9 +7382,10 @@ nsDisplayTableFixedPosition::CreateForFixedBackground(
|
|||||||
nsDisplayStickyPosition::nsDisplayStickyPosition(
|
nsDisplayStickyPosition::nsDisplayStickyPosition(
|
||||||
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
|
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList,
|
||||||
const ActiveScrolledRoot* aActiveScrolledRoot,
|
const ActiveScrolledRoot* aActiveScrolledRoot,
|
||||||
const ActiveScrolledRoot* aContainerASR)
|
const ActiveScrolledRoot* aContainerASR, bool aClippedToDisplayPort)
|
||||||
: nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
|
: nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot),
|
||||||
mContainerASR(aContainerASR) {
|
mContainerASR(aContainerASR),
|
||||||
|
mClippedToDisplayPort(aClippedToDisplayPort) {
|
||||||
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
|
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6197,11 +6197,13 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer {
|
|||||||
nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||||
nsDisplayList* aList,
|
nsDisplayList* aList,
|
||||||
const ActiveScrolledRoot* aActiveScrolledRoot,
|
const ActiveScrolledRoot* aActiveScrolledRoot,
|
||||||
const ActiveScrolledRoot* aContainerASR);
|
const ActiveScrolledRoot* aContainerASR,
|
||||||
|
bool aClippedToDisplayPort);
|
||||||
nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
|
nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
|
||||||
const nsDisplayStickyPosition& aOther)
|
const nsDisplayStickyPosition& aOther)
|
||||||
: nsDisplayOwnLayer(aBuilder, aOther),
|
: nsDisplayOwnLayer(aBuilder, aOther),
|
||||||
mContainerASR(aOther.mContainerASR) {
|
mContainerASR(aOther.mContainerASR),
|
||||||
|
mClippedToDisplayPort(aOther.mClippedToDisplayPort) {
|
||||||
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
|
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6209,6 +6211,7 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer {
|
|||||||
|
|
||||||
void SetClipChain(const DisplayItemClipChain* aClipChain,
|
void SetClipChain(const DisplayItemClipChain* aClipChain,
|
||||||
bool aStore) override;
|
bool aStore) override;
|
||||||
|
bool IsClippedToDisplayPort() const { return mClippedToDisplayPort; }
|
||||||
|
|
||||||
already_AddRefed<Layer> BuildLayer(
|
already_AddRefed<Layer> BuildLayer(
|
||||||
nsDisplayListBuilder* aBuilder, LayerManager* aManager,
|
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
|
// has no fixed descendants. This may be the same as the ASR returned by
|
||||||
// GetActiveScrolledRoot(), or it may be a descendant of that.
|
// GetActiveScrolledRoot(), or it may be a descendant of that.
|
||||||
RefPtr<const ActiveScrolledRoot> mContainerASR;
|
RefPtr<const ActiveScrolledRoot> 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 {
|
class nsDisplayFixedPosition : public nsDisplayOwnLayer {
|
||||||
|
|||||||
Reference in New Issue
Block a user