From 5ebb7188812d36f555ad8a08897135e24a45e76d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 20 Nov 2014 16:58:18 -0800 Subject: [PATCH] Cache the current animated geometry root in nsDisplayListBuilder. (bug 1101260 part 1, r=roc) --- layout/base/nsDisplayList.cpp | 99 +++++++++++++++++++++++++++++ layout/base/nsDisplayList.h | 38 ++++++++++- layout/base/nsLayoutUtils.cpp | 54 +--------------- layout/base/nsLayoutUtils.h | 6 ++ layout/generic/nsGfxScrollFrame.cpp | 8 +++ 5 files changed, 153 insertions(+), 52 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2ac457e4aee..ca7dd683fbd 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -533,6 +533,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mCurrentTableItem(nullptr), mCurrentFrame(aReferenceFrame), mCurrentReferenceFrame(aReferenceFrame), + mCurrentAnimatedGeometryRoot(aReferenceFrame), mWillChangeBudgetCalculated(false), mDirtyRect(-1,-1,-1,-1), mGlassDisplayItem(nullptr), @@ -1088,6 +1089,104 @@ nsDisplayListBuilder::FindReferenceFrameFor(const nsIFrame *aFrame, return mReferenceFrame; } +// Sticky frames are active if their nearest scrollable frame is also active. +static bool +IsStickyFrameActive(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsIFrame* aParent) +{ + MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY); + + // Find the nearest scrollframe. + nsIFrame* cursor = aFrame; + nsIFrame* parent = aParent; + while (parent->GetType() != nsGkAtoms::scrollFrame) { + cursor = parent; + if ((parent = nsLayoutUtils::GetCrossDocParentFrame(cursor)) == nullptr) { + return false; + } + } + + nsIScrollableFrame* sf = do_QueryFrame(parent); + return sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == cursor; +} + +bool +nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent) +{ + if (nsLayoutUtils::IsPopup(aFrame)) + return true; + if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame)) + return true; + if (!aFrame->GetParent() && + nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) { + // Viewport frames in a display port need to be animated geometry roots + // for background-attachment:fixed elements. + return true; + } + + nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); + if (!parent) + return true; + + nsIAtom* parentType = parent->GetType(); + // Treat the slider thumb as being as an active scrolled root when it wants + // its own layer so that it can move without repainting. + if (parentType == nsGkAtoms::sliderFrame && nsLayoutUtils::IsScrollbarThumbLayerized(aFrame)) { + return true; + } + + if (aFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY && + IsStickyFrameActive(this, aFrame, parent)) + { + return true; + } + + if (parentType == nsGkAtoms::scrollFrame) { + nsIScrollableFrame* sf = do_QueryFrame(parent); + if (sf->IsScrollingActive(this) && sf->GetScrolledFrame() == aFrame) { + return true; + } + } + + // Fixed-pos frames are parented by the viewport frame, which has no parent. + if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(aFrame)) { + return true; + } + + if (aParent) { + *aParent = parent; + } + return false; +} + +static nsIFrame* +ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + const nsIFrame* aStopAtAncestor = nullptr) +{ + nsIFrame* cursor = aFrame; + while (cursor != aStopAtAncestor) { + nsIFrame* next; + if (aBuilder->IsAnimatedGeometryRoot(cursor, &next)) + return cursor; + cursor = next; + } + return cursor; +} + +nsIFrame* +nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor) +{ + if (aFrame == mCurrentFrame) { + return mCurrentAnimatedGeometryRoot; + } + return ComputeAnimatedGeometryRootFor(this, aFrame, aStopAtAncestor); +} + +void +nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() +{ + mCurrentAnimatedGeometryRoot = ComputeAnimatedGeometryRootFor(this, const_cast(mCurrentFrame)); +} + void nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame) { diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index d633d4032a4..a51318b4c8b 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -209,7 +209,19 @@ public: */ const nsIFrame* FindReferenceFrameFor(const nsIFrame *aFrame, nsPoint* aOffset = nullptr); - + + /** + * Returns whether a frame acts as an animated geometry root, optionally + * returning the next ancestor to check. + */ + bool IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent = nullptr); + + /** + * Returns the nearest ancestor frame to aFrame that is considered to have + * (or will have) animated geometry. This can return aFrame. + */ + nsIFrame* FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor = nullptr); + /** * @return the root of the display list's frame (sub)tree, whose origin * establishes the coordinate system for the display list @@ -311,6 +323,10 @@ public: const nsIFrame* GetCurrentFrame() { return mCurrentFrame; } const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; } const nsPoint& GetCurrentFrameOffsetToReferenceFrame() { return mCurrentOffsetToReferenceFrame; } + const nsIFrame* GetCurrentAnimatedGeometryRoot() { + return mCurrentAnimatedGeometryRoot; + } + void RecomputeCurrentAnimatedGeometryRoot(); /** * Returns true if merging and flattening of display lists should be @@ -539,6 +555,7 @@ public: : mBuilder(aBuilder), mPrevFrame(aBuilder->mCurrentFrame), mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame), + mPrevAnimatedGeometryRoot(mBuilder->mCurrentAnimatedGeometryRoot), mPrevLayerEventRegions(aBuilder->mLayerEventRegions), mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame), mPrevDirtyRect(aBuilder->mDirtyRect), @@ -555,6 +572,16 @@ public: aBuilder->FindReferenceFrameFor(aForChild, &aBuilder->mCurrentOffsetToReferenceFrame); } + if (aBuilder->mCurrentFrame == aForChild->GetParent()) { + if (aBuilder->IsAnimatedGeometryRoot(aForChild)) { + aBuilder->mCurrentAnimatedGeometryRoot = aForChild; + } + } else { + // Stop at the previous animated geometry root to help cases that + // aren't immediate descendents. + aBuilder->mCurrentAnimatedGeometryRoot = + aBuilder->FindAnimatedGeometryRootFor(aForChild, aBuilder->mCurrentAnimatedGeometryRoot); + } aBuilder->mCurrentFrame = aForChild; aBuilder->mDirtyRect = aDirtyRect; aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot; @@ -566,6 +593,11 @@ public: mBuilder->mCurrentReferenceFrame = aFrame; mBuilder->mCurrentOffsetToReferenceFrame = aOffset; } + // Return the previous frame's animated geometry root, whether or not the + // current frame is an immediate descendant. + const nsIFrame* GetPrevAnimatedGeometryRoot() const { + return mPrevAnimatedGeometryRoot; + } ~AutoBuildingDisplayList() { mBuilder->mCurrentFrame = mPrevFrame; mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame; @@ -574,11 +606,13 @@ public: mBuilder->mDirtyRect = mPrevDirtyRect; mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext; mBuilder->mAncestorHasTouchEventHandler = mPrevAncestorHasTouchEventHandler; + mBuilder->mCurrentAnimatedGeometryRoot = mPrevAnimatedGeometryRoot; } private: nsDisplayListBuilder* mBuilder; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; + nsIFrame* mPrevAnimatedGeometryRoot; nsDisplayLayerEventRegions* mPrevLayerEventRegions; nsPoint mPrevOffset; nsRect mPrevDirtyRect; @@ -795,6 +829,8 @@ private: const nsIFrame* mCurrentReferenceFrame; // The offset from mCurrentFrame to mCurrentReferenceFrame. nsPoint mCurrentOffsetToReferenceFrame; + // The animated geometry root for mCurrentFrame. + nsIFrame* mCurrentAnimatedGeometryRoot; // will-change budget tracker nsDataHashtable, DocumentWillChangeBudget> mWillChangeBudget; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index bbef2e4d519..8ca89b74af0 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1718,8 +1718,8 @@ nsLayoutUtils::SetScrollbarThumbLayerization(nsIFrame* aThumbFrame, bool aLayeri reinterpret_cast(intptr_t(aLayerize))); } -static bool -IsScrollbarThumbLayerized(nsIFrame* aThumbFrame) +bool +nsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame* aThumbFrame) { return reinterpret_cast(aThumbFrame->Properties().Get(ScrollbarThumbLayerized())); } @@ -1729,55 +1729,7 @@ nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsIFrame* aStopAtAncestor) { - nsIFrame* f = aFrame; - nsIFrame* stickyFrame = nullptr; - while (f != aStopAtAncestor) { - if (nsLayoutUtils::IsPopup(f)) - break; - if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(f)) - break; - if (!f->GetParent() && - nsLayoutUtils::ViewportHasDisplayPort(f->PresContext())) { - // Viewport frames in a display port need to be animated geometry roots - // for background-attachment:fixed elements. - break; - } - nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(f); - if (!parent) - break; - nsIAtom* parentType = parent->GetType(); - // Treat the slider thumb as being as an active scrolled root when it wants - // its own layer so that it can move without repainting. - if (parentType == nsGkAtoms::sliderFrame && IsScrollbarThumbLayerized(f)) { - break; - } - // Sticky frames are active if their nearest scrollable frame - // is also active, just keep a record of sticky frames that we - // encounter for now. - if (f->StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY && - !stickyFrame) { - stickyFrame = f; - } - if (parentType == nsGkAtoms::scrollFrame) { - nsIScrollableFrame* sf = do_QueryFrame(parent); - if (sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == f) { - // If we found a sticky frame inside this active scroll frame, - // then use that. Otherwise use the scroll frame. - if (stickyFrame) { - return stickyFrame; - } - return f; - } else { - stickyFrame = nullptr; - } - } - // Fixed-pos frames are parented by the viewport frame, which has no parent - if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) { - return f; - } - f = parent; - } - return f; + return aBuilder->FindAnimatedGeometryRootFor(aFrame, aStopAtAncestor); } nsIFrame* diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 41e58042a0d..c5cab856390 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -512,6 +512,12 @@ public: */ static void SetScrollbarThumbLayerization(nsIFrame* aThumbFrame, bool aLayerize); + /** + * Returns whether aThumbFrame wants its own layer due to having called + * SetScrollbarThumbLayerization. + */ + static bool IsScrollbarThumbLayerized(nsIFrame* aThumbFrame); + /** * Finds the nearest ancestor frame to aItem that is considered to have (or * will have) "animated geometry". For example the scrolled frames of diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 10f0693267b..610b659ffc3 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2879,6 +2879,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsRect displayPort; bool usingDisplayport = false; if (aBuilder->IsPaintingToWindow()) { + bool wasUsingDisplayPort = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), nullptr); + if (!mIsRoot) { // For a non-root scroll frame, override the value of the display port // base rect, and possibly create a display port if there isn't one @@ -2903,6 +2905,12 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, // Override the dirty rectangle if the displayport has been set. if (usingDisplayport) { dirtyRect = displayPort; + + // The cached animated geometry root for the display builder is out of + // date if we just introduced a new animated geometry root. + if (!wasUsingDisplayPort) { + aBuilder->RecomputeCurrentAnimatedGeometryRoot(); + } } }