gecko-dev/layout/painting/DisplayListClipState.cpp
Kartikaya Gupta 6f24092aae 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

--HG--
extra : moz-landing-system : lando
2020-04-08 04:59:14 +00:00

150 lines
5.6 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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 "DisplayListClipState.h"
#include "nsDisplayList.h"
namespace mozilla {
const DisplayItemClipChain* DisplayListClipState::GetCurrentCombinedClipChain(
nsDisplayListBuilder* aBuilder) {
if (mCurrentCombinedClipChainIsValid) {
return mCurrentCombinedClipChain;
}
if (!mClipChainContentDescendants && !mClipChainContainingBlockDescendants) {
mCurrentCombinedClipChain = nullptr;
mCurrentCombinedClipChainIsValid = true;
return nullptr;
}
mCurrentCombinedClipChain = aBuilder->CreateClipChainIntersection(
mCurrentCombinedClipChain, mClipChainContentDescendants,
mClipChainContainingBlockDescendants);
mCurrentCombinedClipChainIsValid = true;
return mCurrentCombinedClipChain;
}
static void ApplyClip(nsDisplayListBuilder* aBuilder,
const DisplayItemClipChain*& aClipToModify,
const ActiveScrolledRoot* aASR,
DisplayItemClipChain& aClipChainOnStack) {
aClipChainOnStack.mASR = aASR;
if (aClipToModify && aClipToModify->mASR == aASR) {
// Intersect with aClipToModify and replace the clip chain item.
aClipChainOnStack.mClip.IntersectWith(aClipToModify->mClip);
aClipChainOnStack.mParent = aClipToModify->mParent;
aClipToModify = &aClipChainOnStack;
} else if (!aClipToModify ||
ActiveScrolledRoot::IsAncestor(aClipToModify->mASR, aASR)) {
// Add a new clip chain item at the bottom.
aClipChainOnStack.mParent = aClipToModify;
aClipToModify = &aClipChainOnStack;
} else {
// We need to insert / intersect a DisplayItemClipChain in the middle of the
// aClipToModify chain. This is a very rare case.
// Find the common ancestor and have the builder create the
// DisplayItemClipChain intersection. This will create new
// DisplayItemClipChain objects for all descendants of ancestorSC and we
// will not hold on to a pointer to aClipChainOnStack.
const DisplayItemClipChain* ancestorSC = aClipToModify;
while (ancestorSC &&
ActiveScrolledRoot::IsAncestor(aASR, ancestorSC->mASR)) {
ancestorSC = ancestorSC->mParent;
}
ancestorSC = aBuilder->CopyWholeChain(ancestorSC);
aClipChainOnStack.mParent = nullptr;
aClipToModify = aBuilder->CreateClipChainIntersection(
ancestorSC, aClipToModify, &aClipChainOnStack);
}
}
void DisplayListClipState::ClipContainingBlockDescendants(
nsDisplayListBuilder* aBuilder, const nsRect& aRect, const nscoord* aRadii,
DisplayItemClipChain& aClipChainOnStack) {
if (aRadii) {
aClipChainOnStack.mClip.SetTo(aRect, aRadii);
} else {
aClipChainOnStack.mClip.SetTo(aRect);
}
const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
ApplyClip(aBuilder, mClipChainContainingBlockDescendants, asr,
aClipChainOnStack);
InvalidateCurrentCombinedClipChain(asr);
}
void DisplayListClipState::ClipContentDescendants(
nsDisplayListBuilder* aBuilder, const nsRect& aRect, const nscoord* aRadii,
DisplayItemClipChain& aClipChainOnStack) {
if (aRadii) {
aClipChainOnStack.mClip.SetTo(aRect, aRadii);
} else {
aClipChainOnStack.mClip.SetTo(aRect);
}
const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
InvalidateCurrentCombinedClipChain(asr);
}
void DisplayListClipState::ClipContentDescendants(
nsDisplayListBuilder* aBuilder, const nsRect& aRect,
const nsRect& aRoundedRect, const nscoord* aRadii,
DisplayItemClipChain& aClipChainOnStack) {
if (aRadii) {
aClipChainOnStack.mClip.SetTo(aRect, aRoundedRect, aRadii);
} else {
nsRect intersect = aRect.Intersect(aRoundedRect);
aClipChainOnStack.mClip.SetTo(intersect);
}
const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
ApplyClip(aBuilder, mClipChainContentDescendants, asr, aClipChainOnStack);
InvalidateCurrentCombinedClipChain(asr);
}
void DisplayListClipState::InvalidateCurrentCombinedClipChain(
const ActiveScrolledRoot* aInvalidateUpTo) {
mClippedToDisplayPort = false;
mCurrentCombinedClipChainIsValid = false;
while (mCurrentCombinedClipChain &&
ActiveScrolledRoot::IsAncestor(aInvalidateUpTo,
mCurrentCombinedClipChain->mASR)) {
mCurrentCombinedClipChain = mCurrentCombinedClipChain->mParent;
}
}
void DisplayListClipState::ClipContainingBlockDescendantsToContentBox(
nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
DisplayItemClipChain& aClipChainOnStack, uint32_t aFlags) {
nscoord radii[8];
bool hasBorderRadius = aFrame->GetContentBoxBorderRadii(radii);
if (!hasBorderRadius &&
(aFlags & ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT)) {
return;
}
nsRect clipRect = aFrame->GetContentRectRelativeToSelf() +
aBuilder->ToReferenceFrame(aFrame);
// If we have a border-radius, we have to clip our content to that
// radius.
ClipContainingBlockDescendants(
aBuilder, clipRect, hasBorderRadius ? radii : nullptr, aClipChainOnStack);
}
DisplayListClipState::AutoSaveRestore::AutoSaveRestore(
nsDisplayListBuilder* aBuilder)
: mBuilder(aBuilder),
mState(aBuilder->ClipState()),
mSavedState(aBuilder->ClipState())
#ifdef DEBUG
,
mClipUsed(false),
mRestored(false)
#endif
{
}
} // namespace mozilla