Bug 1284586 - Disable paint-skipping for scrollframes that we detect as having a CSS-clipped descendant. r?mstange
MozReview-Commit-ID: 5YjFLb1o8Nu
--- a/layout/base/DisplayListClipState.cpp
+++ b/layout/base/DisplayListClipState.cpp
@@ -31,29 +31,48 @@ DisplayListClipState::GetCurrentCombined
} else {
mCurrentCombinedClip =
aBuilder->AllocateDisplayItemClip(*mClipContainingBlockDescendants);
}
return mCurrentCombinedClip;
}
void
+DisplayListClipState::SetScrollClipForContainingBlockDescendants(
+ nsDisplayListBuilder* aBuilder,
+ const DisplayItemScrollClip* aScrollClip)
+{
+ if (aBuilder->IsPaintingToWindow() &&
+ mClipContentDescendants &&
+ aScrollClip != mScrollClipContainingBlockDescendants &&
+ !DisplayItemScrollClip::IsAncestor(mClipContentDescendantsScrollClip, aScrollClip)) {
+ if (mClipContentDescendantsScrollClip && mClipContentDescendantsScrollClip->mScrollableFrame) {
+ mClipContentDescendantsScrollClip->mScrollableFrame->SetHasCSSClippedDescendant();
+ }
+ mClipContentDescendantsScrollClip = nullptr;
+ }
+ mScrollClipContainingBlockDescendants = aScrollClip;
+ mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
+}
+
+void
DisplayListClipState::ClipContainingBlockDescendants(const nsRect& aRect,
const nscoord* aRadii,
DisplayItemClip& aClipOnStack)
{
if (aRadii) {
aClipOnStack.SetTo(aRect, aRadii);
} else {
aClipOnStack.SetTo(aRect);
}
if (mClipContainingBlockDescendants) {
aClipOnStack.IntersectWith(*mClipContainingBlockDescendants);
}
mClipContainingBlockDescendants = &aClipOnStack;
+ mClipContentDescendantsScrollClip = GetCurrentInnermostScrollClip();
mCurrentCombinedClip = nullptr;
}
void
DisplayListClipState::ClipContentDescendants(const nsRect& aRect,
const nscoord* aRadii,
DisplayItemClip& aClipOnStack)
{
--- a/layout/base/DisplayListClipState.h
+++ b/layout/base/DisplayListClipState.h
@@ -24,16 +24,17 @@ namespace mozilla {
class DisplayListClipState {
public:
DisplayListClipState()
: mClipContentDescendants(nullptr)
, mClipContainingBlockDescendants(nullptr)
, mCurrentCombinedClip(nullptr)
, mScrollClipContentDescendants(nullptr)
, mScrollClipContainingBlockDescendants(nullptr)
+ , mClipContentDescendantsScrollClip(nullptr)
, mStackingContextAncestorSC(nullptr)
{}
/**
* Returns intersection of mClipContainingBlockDescendants and
* mClipContentDescendants, allocated on aBuilder's arena.
*/
const DisplayItemClip* GetCurrentCombinedClip(nsDisplayListBuilder* aBuilder);
@@ -69,21 +70,18 @@ public:
private:
void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
{
mClipContainingBlockDescendants = aClip;
mCurrentCombinedClip = nullptr;
}
- void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
- {
- mScrollClipContainingBlockDescendants = aScrollClip;
- mStackingContextAncestorSC = DisplayItemScrollClip::PickAncestor(mStackingContextAncestorSC, aScrollClip);
- }
+ void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+ const DisplayItemScrollClip* aScrollClip);
void Clear()
{
mClipContentDescendants = nullptr;
mClipContainingBlockDescendants = nullptr;
mCurrentCombinedClip = nullptr;
// We do not clear scroll clips.
}
@@ -179,16 +177,21 @@ private:
/**
* The same for scroll clips.
*/
const DisplayItemScrollClip* mScrollClipContentDescendants;
const DisplayItemScrollClip* mScrollClipContainingBlockDescendants;
/**
+ * Magical property from Markus.
+ */
+ const DisplayItemScrollClip* mClipContentDescendantsScrollClip;
+
+ /**
* A scroll clip that is an ancestor of all the scroll clips that were
* "current" on this clip state since EnterStackingContextContents was
* called.
*/
const DisplayItemScrollClip* mStackingContextAncestorSC;
};
/**
@@ -426,19 +429,20 @@ public:
/**
* *aClip must survive longer than this object. Be careful!!!
*/
void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
{
mState.SetClipForContainingBlockDescendants(aClip);
}
- void SetScrollClipForContainingBlockDescendants(const DisplayItemScrollClip* aScrollClip)
+ void SetScrollClipForContainingBlockDescendants(nsDisplayListBuilder* aBuilder,
+ const DisplayItemScrollClip* aScrollClip)
{
- mState.SetScrollClipForContainingBlockDescendants(aScrollClip);
+ mState.SetScrollClipForContainingBlockDescendants(aBuilder, aScrollClip);
}
/**
* Intersects the given clip rect (with optional aRadii) with the current
* mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
* the result, stored in aClipOnStack.
*/
void ClipContainingBlockDescendantsExtra(const nsRect& aRect,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2794,26 +2794,26 @@ nsIFrame::BuildDisplayListForChild(nsDis
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
CheckForApzAwareEventHandlers(aBuilder, child);
if (savedOutOfFlowData) {
clipState.SetClipForContainingBlockDescendants(
&savedOutOfFlowData->mContainingBlockClip);
- clipState.SetScrollClipForContainingBlockDescendants(
+ clipState.SetScrollClipForContainingBlockDescendants(aBuilder,
savedOutOfFlowData->mContainingBlockScrollClip);
} else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
isPlaceholder) {
// If we have nested out-of-flow frames and the outer one isn't visible
// then we won't have stored clip data for it. We can just clear the clip
// instead since we know we won't render anything, and the inner out-of-flow
// frame will setup the correct clip for itself.
clipState.Clear();
- clipState.SetScrollClipForContainingBlockDescendants(nullptr);
+ clipState.SetScrollClipForContainingBlockDescendants(aBuilder, nullptr);
}
// Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
// or overflow:hidden on elements that don't support scrolling (and therefore
// don't create nsHTML/XULScrollFrame). This clipping needs to not clip
// anything directly rendered by the parent, only the rendering of its
// children.
// Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1886,16 +1886,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
, mWillBuildScrollableLayer(false)
, mIsScrollParent(false)
, mIsScrollableLayerInRootContainer(false)
, mHasBeenScrolled(false)
, mIgnoreMomentumScroll(false)
, mTransformingByAPZ(false)
, mScrollableByAPZ(false)
, mZoomableByAPZ(false)
+ , mHasCSSClippedDescendant(false)
, mVelocityQueue(aOuter->PresContext())
, mAsyncScrollEvent(END_DOM)
{
if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
}
EnsureFrameVisPrefsCached();
@@ -2114,16 +2115,22 @@ ScrollFrameHelper::HasPluginFrames()
bool
ScrollFrameHelper::HasPerspective() const
{
const nsStyleDisplay* disp = mOuter->StyleDisplay();
return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
}
void
+ScrollFrameHelper::SetHasCSSClippedDescendant()
+{
+ mHasCSSClippedDescendant = true;
+}
+
+void
ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
nsIScrollableFrame::ScrollMode aMode)
{
nsPoint current = GetScrollPosition();
CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
@@ -2750,21 +2757,21 @@ ScrollFrameHelper::ScrollToImpl(nsPoint
// that needs to be painted. So even if the final tile-aligned displayport
// is the same, we force a repaint for these elements. Bug 1254260 tracks
// fixing this properly.
nsRect displayPort;
bool usingDisplayPort =
nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort);
displayPort.MoveBy(-mScrolledFrame->GetPosition());
- PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d\n",
+ PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d clip %d\n",
Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
- mScrollableByAPZ, HasPluginFrames(), HasPerspective());
- if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective()) {
+ mScrollableByAPZ, HasPluginFrames(), HasPerspective(), mHasCSSClippedDescendant);
+ if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) && !HasPerspective() && !mHasCSSClippedDescendant) {
bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
if (!apzDisabled) {
if (LastScrollOrigin() == nsGkAtoms::apz) {
schedulePaint = false;
PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
} else if (mScrollableByAPZ && !HasPluginFrames()) {
nsIWidget* widget = presContext->GetNearestWidget();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -381,16 +381,17 @@ public:
}
NotifyPluginFrames(aTransforming ? BEGIN_APZ : END_APZ);
}
bool IsTransformingByAPZ() const {
return mTransformingByAPZ;
}
void SetScrollableByAPZ(bool aScrollable);
void SetZoomableByAPZ(bool aZoomable);
+ void SetHasCSSClippedDescendant();
bool UsesContainerScrolling() const;
ScrollSnapInfo GetScrollSnapInfo() const;
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aDirtyRect,
bool aAllowCreateDisplayPort);
@@ -576,16 +577,18 @@ public:
bool mScrollableByAPZ:1;
// True if the APZ is allowed to zoom this scrollframe.
bool mZoomableByAPZ:1;
// True if we don't want the scrollbar to repaint itself right now.
bool mSuppressScrollbarRepaints:1;
+ bool mHasCSSClippedDescendant:1;
+
mozilla::layout::ScrollVelocityQueue mVelocityQueue;
protected:
class AutoScrollbarRepaintSuppression;
friend class AutoScrollbarRepaintSuppression;
class AutoScrollbarRepaintSuppression {
public:
AutoScrollbarRepaintSuppression(ScrollFrameHelper* aHelper, bool aSuppress)
@@ -1000,16 +1003,19 @@ public:
return mHelper.IsTransformingByAPZ();
}
void SetScrollableByAPZ(bool aScrollable) override {
mHelper.SetScrollableByAPZ(aScrollable);
}
void SetZoomableByAPZ(bool aZoomable) override {
mHelper.SetZoomableByAPZ(aZoomable);
}
+ void SetHasCSSClippedDescendant() override {
+ mHelper.SetHasCSSClippedDescendant();
+ }
ScrollSnapInfo GetScrollSnapInfo() const override {
return mHelper.GetScrollSnapInfo();
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
@@ -1403,16 +1409,19 @@ public:
return mHelper.IsTransformingByAPZ();
}
void SetScrollableByAPZ(bool aScrollable) override {
mHelper.SetScrollableByAPZ(aScrollable);
}
void SetZoomableByAPZ(bool aZoomable) override {
mHelper.SetZoomableByAPZ(aZoomable);
}
+ void SetHasCSSClippedDescendant() override {
+ mHelper.SetHasCSSClippedDescendant();
+ }
virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aDirtyRect,
bool aAllowCreateDisplayPort) override {
return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
}
virtual void NotifyApproximateFrameVisibilityUpdate() override {
mHelper.NotifyApproximateFrameVisibilityUpdate();
}
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -459,11 +459,13 @@ public:
* own displayport and schedule a timer to do that if it is safe.
*/
virtual void TriggerDisplayPortExpiration() = 0;
/**
* Returns information required to determine where to snap to after a scroll.
*/
virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
+
+ virtual void SetHasCSSClippedDescendant() = 0;
};
#endif
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -102,17 +102,17 @@ BuildDisplayListForTopLayerFrame(nsDispl
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
nsDisplayListBuilder::OutOfFlowDisplayData*
savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(aFrame);
if (savedOutOfFlowData) {
dirty = savedOutOfFlowData->mDirtyRect;
clipState.SetClipForContainingBlockDescendants(
&savedOutOfFlowData->mContainingBlockClip);
clipState.SetScrollClipForContainingBlockDescendants(
- savedOutOfFlowData->mContainingBlockScrollClip);
+ aBuilder, savedOutOfFlowData->mContainingBlockScrollClip);
}
nsDisplayList list;
aFrame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
aList->AppendToTop(&list);
}
void
ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,