Bug 769541 - Fix display list creation for positioned descendants of fixed position frames. r=roc

Positioned descendants of positioned frames escape the child display list to
their parent's positioned descendants list when they don't have a z-index set.
This caused their invalidations to be logged against the incorrect layer.

To fix this, wrap each extra positioned descendant of a fixed-position frame
in its own nsDisplayFixedPosition so they receive their own layers.
This commit is contained in:
Chris Lord 2012-07-25 08:06:19 +01:00
parent aca5b79b25
commit f61563bc07
3 changed files with 51 additions and 12 deletions

View File

@ -65,6 +65,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsPaintingToWindow(false),
mHasDisplayPort(false),
mHasFixedItems(false),
mIsInFixedPosition(false),
mIsCompositingCheap(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
@ -2072,8 +2073,10 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsIFrame* aFixedPosFrame,
nsDisplayList* aList)
: nsDisplayOwnLayer(aBuilder, aFrame, aList) {
: nsDisplayOwnLayer(aBuilder, aFrame, aList)
, mFixedPosFrame(aFixedPosFrame) {
MOZ_COUNT_CTOR(nsDisplayFixedPosition);
}
@ -2094,7 +2097,7 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
// any positioning set (left/top/right/bottom) indicates that the
// corresponding side of its container should be the anchor point,
// defaulting to top-left.
nsIFrame* viewportFrame = mFrame->GetParent();
nsIFrame* viewportFrame = mFixedPosFrame->GetParent();
nsPresContext *presContext = viewportFrame->PresContext();
// Fixed position frames are reflowed into the scroll-port size if one has
@ -2121,7 +2124,7 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
gfxPoint anchor(anchorRect.x, anchorRect.y);
const nsStylePosition* position = mFrame->GetStylePosition();
const nsStylePosition* position = mFixedPosFrame->GetStylePosition();
if (position->mOffset.GetRightUnit() != eStyleUnit_Auto)
anchor.x = anchorRect.XMost();
if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto)

View File

@ -222,6 +222,12 @@ public:
void SetPaintingToWindow(bool aToWindow) { mIsPaintingToWindow = aToWindow; }
bool IsPaintingToWindow() const { return mIsPaintingToWindow; }
/**
* @return Returns if the builder is currently building an
* nsDisplayFixedPosition sub-tree.
*/
bool IsInFixedPosition() const { return mIsInFixedPosition; }
bool SetIsCompositingCheap(bool aCompositingCheap) {
bool temp = mIsCompositingCheap;
mIsCompositingCheap = aCompositingCheap;
@ -391,8 +397,8 @@ public:
/**
* A helper class to temporarily set the value of
* mIsAtRootOfPseudoStackingContext and temporarily update
* mCachedOffsetFrame/mCachedOffset from a frame to its child.
* mIsAtRootOfPseudoStackingContext and mIsInFixedPosition, and temporarily
* update mCachedOffsetFrame/mCachedOffset from a frame to its child.
*/
class AutoBuildingDisplayList;
friend class AutoBuildingDisplayList;
@ -406,11 +412,13 @@ public:
aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
}
AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder,
nsIFrame* aForChild, bool aIsRoot)
nsIFrame* aForChild, bool aIsRoot,
bool aIsInFixedPosition)
: mBuilder(aBuilder),
mPrevCachedOffsetFrame(aBuilder->mCachedOffsetFrame),
mPrevCachedOffset(aBuilder->mCachedOffset),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext) {
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevIsInFixedPosition(aBuilder->mIsInFixedPosition) {
if (mPrevCachedOffsetFrame == aForChild->GetParent()) {
aBuilder->mCachedOffset += aForChild->GetPosition();
} else {
@ -418,17 +426,22 @@ public:
}
aBuilder->mCachedOffsetFrame = aForChild;
aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
if (aIsInFixedPosition) {
aBuilder->mIsInFixedPosition = aIsInFixedPosition;
}
}
~AutoBuildingDisplayList() {
mBuilder->mCachedOffsetFrame = mPrevCachedOffsetFrame;
mBuilder->mCachedOffset = mPrevCachedOffset;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mIsInFixedPosition = mPrevIsInFixedPosition;
}
private:
nsDisplayListBuilder* mBuilder;
const nsIFrame* mPrevCachedOffsetFrame;
nsPoint mPrevCachedOffset;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevIsInFixedPosition;
};
/**
@ -535,6 +548,7 @@ private:
bool mIsPaintingToWindow;
bool mHasDisplayPort;
bool mHasFixedItems;
bool mIsInFixedPosition;
bool mIsCompositingCheap;
};
@ -1920,7 +1934,7 @@ public:
class nsDisplayFixedPosition : public nsDisplayOwnLayer {
public:
nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList);
nsIFrame* aFixedPosFrame, nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayFixedPosition();
#endif
@ -1929,6 +1943,9 @@ public:
LayerManager* aManager,
const ContainerParameters& aContainerParameters);
NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_FIXED_POSITION)
protected:
nsIFrame* mFixedPosFrame;
};
/**

View File

@ -2103,8 +2103,15 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
pseudoStackingContext = true;
}
// This controls later whether we build an nsDisplayWrapList or an
// nsDisplayFixedPosition. We check if we're already building a fixed-pos
// item and disallow nesting, to prevent the situation of bug #769541
// occurring.
bool buildFixedPositionItem = disp->mPosition == NS_STYLE_POSITION_FIXED
&& !child->GetParent()->GetParent() && !isSVG && !aBuilder->IsInFixedPosition();
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, pseudoStackingContext);
buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem);
nsRect overflowClip;
nscoord overflowClipRadii[8];
@ -2204,9 +2211,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// Make sure the root of a fixed position frame sub-tree gets the
// correct displaylist item type.
nsDisplayItem* item;
if (!isSVG && !child->GetParent()->GetParent() &&
disp->mPosition == NS_STYLE_POSITION_FIXED) {
item = new (aBuilder) nsDisplayFixedPosition(aBuilder, child, &list);
if (buildFixedPositionItem) {
item = new (aBuilder) nsDisplayFixedPosition(aBuilder, child, child, &list);
} else {
item = new (aBuilder) nsDisplayWrapList(aBuilder, child, &list);
}
@ -2216,6 +2222,19 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
rv = aLists.PositionedDescendants()->AppendNewToTop(item);
}
NS_ENSURE_SUCCESS(rv, rv);
// Make sure that extra positioned descendants don't escape having
// their fixed-position metadata applied to them.
if (buildFixedPositionItem) {
while (!extraPositionedDescendants.IsEmpty()) {
item = extraPositionedDescendants.RemoveBottom();
nsDisplayList fixedPosDescendantList;
fixedPosDescendantList.AppendToTop(item);
aLists.PositionedDescendants()->AppendNewToTop(
new (aBuilder) nsDisplayFixedPosition(aBuilder, item->GetUnderlyingFrame(),
child, &fixedPosDescendantList));
}
}
}
} else if (!isSVG && disp->IsFloating()) {
if (!list.IsEmpty()) {