Cache the current animated geometry root in nsDisplayListBuilder. (bug 1101260 part 1, r=roc)

This commit is contained in:
David Anderson 2014-11-20 16:58:18 -08:00
parent cd7ffbfae6
commit 5ebb718881
5 changed files with 153 additions and 52 deletions

View File

@ -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<nsIFrame *>(mCurrentFrame));
}
void
nsDisplayListBuilder::AdjustWindowDraggingRegion(nsIFrame* aFrame)
{

View File

@ -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<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
mWillChangeBudget;

View File

@ -1718,8 +1718,8 @@ nsLayoutUtils::SetScrollbarThumbLayerization(nsIFrame* aThumbFrame, bool aLayeri
reinterpret_cast<void*>(intptr_t(aLayerize)));
}
static bool
IsScrollbarThumbLayerized(nsIFrame* aThumbFrame)
bool
nsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame* aThumbFrame)
{
return reinterpret_cast<intptr_t>(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*

View File

@ -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

View File

@ -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();
}
}
}