Bug 1300864 - Disable paint skipping for scroll frames that contain out-of-flow content inside a CSS filter. r=mstange
MozReview-Commit-ID: 5TwC75wzOsm
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2931,16 +2931,18 @@ nsIFrame::BuildDisplayListForStackingCon
nsDisplayListCollection set(aBuilder);
{
DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
nsDisplayListBuilder::AutoInTransformSetter
inTransformSetter(aBuilder, inTransform);
nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex
perspectiveIndex(aBuilder, this);
+ nsDisplayListBuilder::AutoFilterASRSetter
+ filterASRSetter(aBuilder, usingFilter);
CheckForApzAwareEventHandlers(aBuilder, this);
Maybe<nsRect> contentClip =
GetClipPropClipRect(disp, effects, GetSize());
if (usingMask) {
contentClip = IntersectMaybeRects(contentClip, clipForMask);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1296,16 +1296,22 @@ ScrollFrameHelper::SetZoomableByAPZ(bool
if (mZoomableByAPZ != aZoomable) {
// We might be changing the result of WantAsyncScroll() so schedule a
// paint to make sure we pick up the result of that change.
mZoomableByAPZ = aZoomable;
mOuter->SchedulePaint();
}
}
+void
+ScrollFrameHelper::SetHasOutOfFlowContentInsideFilter()
+{
+ mHasOutOfFlowContentInsideFilter = true;
+}
+
bool
ScrollFrameHelper::WantAsyncScroll() const
{
// If zooming is allowed, and this is a frame that's allowed to zoom, then
// we want it to be async-scrollable or zooming will not be permitted.
if (mZoomableByAPZ) {
return true;
}
@@ -2070,16 +2076,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
, mWillBuildScrollableLayer(false)
, mIsScrollParent(false)
, mIsScrollableLayerInRootContainer(false)
, mHasBeenScrolled(false)
, mIgnoreMomentumScroll(false)
, mTransformingByAPZ(false)
, mScrollableByAPZ(false)
, mZoomableByAPZ(false)
+ , mHasOutOfFlowContentInsideFilter(false)
, mSuppressScrollbarRepaints(false)
, mVelocityQueue(aOuter->PresContext())
{
if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
}
EnsureFrameVisPrefsCached();
@@ -2874,23 +2881,25 @@ 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 bglocal %d\n",
+ PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins"
+ "%d perspective %d bglocal %d filter %d\n",
Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
mScrollableByAPZ, HasPluginFrames(), HasPerspective(),
- HasBgAttachmentLocal());
+ HasBgAttachmentLocal(), mHasOutOfFlowContentInsideFilter);
if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) &&
- !HasPerspective() && !HasBgAttachmentLocal()) {
+ !HasPerspective() && !HasBgAttachmentLocal() &&
+ !mHasOutOfFlowContentInsideFilter) {
bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
if (!apzDisabled && !HasPluginFrames()) {
if (LastScrollOrigin() == nsGkAtoms::apz) {
schedulePaint = false;
PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
} else if (mScrollableByAPZ) {
nsIWidget* widget = presContext->GetNearestWidget();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -421,16 +421,17 @@ public:
mOuter->SchedulePaint();
}
}
bool IsTransformingByAPZ() const {
return mTransformingByAPZ;
}
void SetScrollableByAPZ(bool aScrollable);
void SetZoomableByAPZ(bool aZoomable);
+ void SetHasOutOfFlowContentInsideFilter();
bool UsesContainerScrolling() const;
ScrollSnapInfo GetScrollSnapInfo() const;
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aVisibleRect,
nsRect* aDirtyRect,
@@ -627,16 +628,20 @@ public:
// True if APZ can scroll this frame asynchronously (i.e. it has an APZC
// set up for this frame and it's not a scrollinfo layer).
bool mScrollableByAPZ:1;
// True if the APZ is allowed to zoom this scrollframe.
bool mZoomableByAPZ:1;
+ // True if the scroll frame contains out-of-flow content and is inside
+ // a CSS filter.
+ bool mHasOutOfFlowContentInsideFilter:1;
+
// True if we don't want the scrollbar to repaint itself right now.
bool mSuppressScrollbarRepaints:1;
mozilla::layout::ScrollVelocityQueue mVelocityQueue;
protected:
class AutoScrollbarRepaintSuppression;
friend class AutoScrollbarRepaintSuppression;
@@ -1057,16 +1062,19 @@ public:
return mHelper.IsTransformingByAPZ();
}
void SetScrollableByAPZ(bool aScrollable) override {
mHelper.SetScrollableByAPZ(aScrollable);
}
void SetZoomableByAPZ(bool aZoomable) override {
mHelper.SetZoomableByAPZ(aZoomable);
}
+ void SetHasOutOfFlowContentInsideFilter() override {
+ mHelper.SetHasOutOfFlowContentInsideFilter();
+ }
ScrollSnapInfo GetScrollSnapInfo() const override {
return mHelper.GetScrollSnapInfo();
}
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
return mHelper.DragScroll(aEvent);
}
@@ -1492,16 +1500,19 @@ public:
return mHelper.IsTransformingByAPZ();
}
void SetScrollableByAPZ(bool aScrollable) override {
mHelper.SetScrollableByAPZ(aScrollable);
}
void SetZoomableByAPZ(bool aZoomable) override {
mHelper.SetZoomableByAPZ(aZoomable);
}
+ void SetHasOutOfFlowContentInsideFilter() override {
+ mHelper.SetHasOutOfFlowContentInsideFilter();
+ }
virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aVisibleRect,
nsRect* aDirtyRect,
bool aSetBase) override {
return mHelper.DecideScrollableLayer(aBuilder, aVisibleRect, aDirtyRect, aSetBase);
}
virtual void NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort) override {
mHelper.NotifyApproximateFrameVisibilityUpdate(aIgnoreDisplayPort);
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -444,16 +444,23 @@ public:
virtual void SetScrollableByAPZ(bool aScrollable) = 0;
/**
* Notify this scroll frame that it can be zoomed by APZ.
*/
virtual void SetZoomableByAPZ(bool aZoomable) = 0;
/**
+ * Mark this scroll frame as having out-of-flow content inside a CSS filter.
+ * Such content will move incorrectly during async-scrolling; to mitigate
+ * this, paint skipping is disabled for such scroll frames.
+ */
+ virtual void SetHasOutOfFlowContentInsideFilter() = 0;
+
+ /**
* Whether or not this frame uses containerful scrolling.
*/
virtual bool UsesContainerScrolling() const = 0;
/**
* Determine if we should build a scrollable layer for this scroll frame and
* return the result. It will also record this result on the scroll frame.
* Pass the visible rect in aVisibleRect. On return it will be set to the
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -914,16 +914,27 @@ nsDisplayListBuilder::AutoCurrentActiveS
const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
mContentClipASR, aActiveScrolledRoot);
// mCurrentContainerASR is adjusted so that it's still an ancestor of
// finiteBoundsASR.
mBuilder->mCurrentContainerASR = ActiveScrolledRoot::PickAncestor(
mBuilder->mCurrentContainerASR, finiteBoundsASR);
+ // If we are entering out-of-flow content inside a CSS filter, mark
+ // scroll frames wrt. which the content is fixed as containing such content.
+ if (mBuilder->mFilterASR &&
+ ActiveScrolledRoot::IsAncestor(aActiveScrolledRoot, mBuilder->mFilterASR)) {
+ for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
+ asr && asr != aActiveScrolledRoot;
+ asr = asr->mParent) {
+ asr->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
+ }
+ }
+
mUsed = true;
}
void
nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::InsertScrollFrame(nsIScrollableFrame* aScrollableFrame)
{
MOZ_ASSERT(!mUsed);
size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
@@ -965,16 +976,17 @@ nsDisplayListBuilder::nsDisplayListBuild
mScrollInfoItemsForHoisting(nullptr),
mActiveScrolledRootForRootScrollframe(nullptr),
mMode(aMode),
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(nsDisplayOwnLayerFlags::eNone),
mPerspectiveItemIndex(0),
mSVGEffectsBuildingDepth(0),
+ mFilterASR(nullptr),
mContainsBlendMode(false),
mIsBuildingScrollbar(false),
mCurrentScrollbarWillHaveLayer(false),
mBuildCaret(aBuildCaret),
mRetainingDisplayList(aRetainingDisplayList),
mPartialUpdate(false),
mIgnoreSuppression(false),
mIsAtRootOfPseudoStackingContext(false),
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -1120,16 +1120,38 @@ public:
~AutoInTransformSetter() {
mBuilder->mInTransform = mOldValue;
}
private:
nsDisplayListBuilder* mBuilder;
bool mOldValue;
};
+ /**
+ * A helper class to temporarily set the value of mFilterASR.
+ */
+ class AutoFilterASRSetter;
+ friend class AutoFilterASRSetter;
+ class AutoFilterASRSetter {
+ public:
+ AutoFilterASRSetter(nsDisplayListBuilder* aBuilder, bool aUsingFilter)
+ : mBuilder(aBuilder), mOldValue(aBuilder->mFilterASR)
+ {
+ if (!aBuilder->mFilterASR && aUsingFilter) {
+ aBuilder->mFilterASR = aBuilder->CurrentActiveScrolledRoot();
+ }
+ }
+ ~AutoFilterASRSetter() {
+ mBuilder->mFilterASR = mOldValue;
+ }
+ private:
+ nsDisplayListBuilder* mBuilder;
+ const ActiveScrolledRoot* mOldValue;
+ };
+
class AutoSaveRestorePerspectiveIndex;
friend class AutoSaveRestorePerspectiveIndex;
class AutoSaveRestorePerspectiveIndex {
public:
AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: mBuilder(nullptr)
{
if (aFrame->ChildrenHavePerspective()) {
@@ -1766,16 +1788,19 @@ private:
const ActiveScrolledRoot* mActiveScrolledRootForRootScrollframe;
nsDisplayListBuilderMode mMode;
ViewID mCurrentScrollParentId;
ViewID mCurrentScrollbarTarget;
nsDisplayOwnLayerFlags mCurrentScrollbarFlags;
Preserves3DContext mPreserves3DCtx;
uint32_t mPerspectiveItemIndex;
int32_t mSVGEffectsBuildingDepth;
+ // When we are inside a filter, the current ASR at the time we entered the
+ // filter. Otherwise nullptr.
+ const ActiveScrolledRoot* mFilterASR;
bool mContainsBlendMode;
bool mIsBuildingScrollbar;
bool mCurrentScrollbarWillHaveLayer;
bool mBuildCaret;
bool mRetainingDisplayList;
bool mPartialUpdate;
bool mIgnoreSuppression;
bool mIsAtRootOfPseudoStackingContext;