From a091e3d5c637cc344137a8e36a4b51b785adf97b Mon Sep 17 00:00:00 2001 From: "roc+@cs.cmu.edu" Date: Fri, 18 Apr 2008 02:21:21 -0700 Subject: [PATCH] Bug 428156. Relanding. Make ComputeRepaintRegionForCopy handle non-moving frames that clip moving frames. r+sr=dbaron --- layout/base/nsDisplayList.cpp | 19 +++++----- layout/base/nsDisplayList.h | 19 ++++++++-- layout/base/nsLayoutUtils.cpp | 23 ++++++++++-- layout/base/nsPresShell.cpp | 2 +- layout/generic/nsFrame.cpp | 62 +++++++++++++++++++++++++++------ layout/generic/nsFrameFrame.cpp | 2 +- 6 files changed, 100 insertions(+), 27 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 194a5c0820cd..8236b149d53d 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -852,15 +852,17 @@ PRBool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* return PR_TRUE; } -nsDisplayClip::nsDisplayClip(nsIFrame* aFrame, nsDisplayItem* aItem, - const nsRect& aRect) - : nsDisplayWrapList(aFrame, aItem), mClip(aRect) { +nsDisplayClip::nsDisplayClip(nsIFrame* aFrame, nsIFrame* aClippingFrame, + nsDisplayItem* aItem, const nsRect& aRect) + : nsDisplayWrapList(aFrame, aItem), + mClippingFrame(aClippingFrame), mClip(aRect) { MOZ_COUNT_CTOR(nsDisplayClip); } -nsDisplayClip::nsDisplayClip(nsIFrame* aFrame, nsDisplayList* aList, - const nsRect& aRect) - : nsDisplayWrapList(aFrame, aList), mClip(aRect) { +nsDisplayClip::nsDisplayClip(nsIFrame* aFrame, nsIFrame* aClippingFrame, + nsDisplayList* aList, const nsRect& aRect) + : nsDisplayWrapList(aFrame, aList), + mClippingFrame(aClippingFrame), mClip(aRect) { MOZ_COUNT_CTOR(nsDisplayClip); } @@ -903,7 +905,7 @@ PRBool nsDisplayClip::TryMerge(nsDisplayListBuilder* aBuilder, if (aItem->GetType() != TYPE_CLIP) return PR_FALSE; nsDisplayClip* other = static_cast(aItem); - if (other->mClip != mClip) + if (other->mClip != mClip || other->mClippingFrame != mClippingFrame) return PR_FALSE; mList.AppendToBottom(&other->mList); return PR_TRUE; @@ -911,5 +913,6 @@ PRBool nsDisplayClip::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayWrapList* nsDisplayClip::WrapWithClone(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { - return new (aBuilder) nsDisplayClip(aItem->GetUnderlyingFrame(), aItem, mClip); + return new (aBuilder) + nsDisplayClip(aItem->GetUnderlyingFrame(), mClippingFrame, aItem, mClip); } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 5496bf4a3db3..52f2e74211b7 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1190,8 +1190,15 @@ private: */ class nsDisplayClip : public nsDisplayWrapList { public: - nsDisplayClip(nsIFrame* aFrame, nsDisplayItem* aItem, const nsRect& aRect); - nsDisplayClip(nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aRect); + /** + * @param aFrame the frame that should be considered the underlying + * frame for this content, e.g. the frame whose z-index we have. + * @param aClippingFrame the frame that is inducing the clipping. + */ + nsDisplayClip(nsIFrame* aFrame, nsIFrame* aClippingFrame, + nsDisplayItem* aItem, const nsRect& aRect); + nsDisplayClip(nsIFrame* aFrame, nsIFrame* aClippingFrame, + nsDisplayList* aList, const nsRect& aRect); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayClip(); #endif @@ -1207,12 +1214,18 @@ public: nsRect GetClipRect() { return mClip; } void SetClipRect(const nsRect& aRect) { mClip = aRect; } + nsIFrame* GetClippingFrame() { return mClippingFrame; } virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); private: - nsRect mClip; + // The frame that is responsible for the clipping. This may be different + // from mFrame because mFrame represents the content that is being + // clipped, and for example may be used to obtain the z-index of the + // content. + nsIFrame* mClippingFrame; + nsRect mClip; }; #endif /*NSDISPLAYLIST_H_*/ diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index c821bdb9c6a1..af3637dc7423 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1017,8 +1017,25 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList, nsDisplayList* sublist = item->GetList(); if (sublist) { if (item->GetType() == nsDisplayItem::TYPE_CLIP) { - nsRect clip; - clip.IntersectRect(aClipRect, static_cast(item)->GetClipRect()); + nsDisplayClip* clipItem = static_cast(item); + nsRect clip = aClipRect; + // If the clipping frame is moving, then it isn't clipping any + // non-moving content (see ApplyAbsPosClipping), so we don't need + // to do anything special, but we should not restrict aClipRect. + if (!aBuilder->IsMovingFrame(clipItem->GetClippingFrame())) { + clip.IntersectRect(clip, clipItem->GetClipRect()); + + // Invalidate the translation of the source area that was clipped out + nsRegion clippedOutSource; + clippedOutSource.Sub(aRect, clip); + clippedOutSource.MoveBy(aDelta); + aRegion->Or(*aRegion, clippedOutSource); + + // Invalidate the destination area that is clipped out + nsRegion clippedOutDestination; + clippedOutDestination.Sub(aRect + aDelta, clip); + aRegion->Or(*aRegion, clippedOutDestination); + } AddItemsToRegion(aBuilder, sublist, aRect, clip, aDelta, aRegion); } else { // opacity, or a generic sublist @@ -1075,7 +1092,7 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame, // to clip non-moving items --- this is enforced by the code that sets // up nsDisplayClip items, in particular see ApplyAbsPosClipping. // XXX but currently a non-moving clip item can incorrectly clip - // moving moving items! See bug 428156. + // moving items! See bug 428156. nsRect rect; rect.UnionRect(aCopyRect, aCopyRect + aDelta); nsDisplayListBuilder builder(aRootFrame, PR_FALSE, PR_TRUE); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 7d4e501f9429..4f723d43e08e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5101,7 +5101,7 @@ PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, // wrap the item in an nsDisplayClip so that it can be clipped to // the selection. If the allocation fails, fall through and delete // the item below. - itemToInsert = new (aBuilder)nsDisplayClip(frame, i, textRect); + itemToInsert = new (aBuilder)nsDisplayClip(frame, frame, i, textRect); } } else { diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 7274894dd222..f6595207560d 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1034,13 +1034,13 @@ public: // We are not a stacking context root. There is no valid underlying // frame for the whole list. These items are all in-flow descendants so // we can safely just clip them. - return new (aBuilder) nsDisplayClip(nsnull, aList, mRect); + return new (aBuilder) nsDisplayClip(nsnull, mContainer, aList, mRect); } virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { nsIFrame* f = aItem->GetUnderlyingFrame(); if (mClipAll || nsLayoutUtils::IsProperAncestorFrame(mContainer, f, nsnull)) - return new (aBuilder) nsDisplayClip(f, aItem, mRect); + return new (aBuilder) nsDisplayClip(f, mContainer, aItem, mRect); return aItem; } protected: @@ -1053,20 +1053,22 @@ protected: class nsAbsPosClipWrapper : public nsDisplayWrapper { public: - nsAbsPosClipWrapper(const nsRect& aRect) - : mRect(aRect) {} + nsAbsPosClipWrapper(nsIFrame* aContainer, const nsRect& aRect) + : mContainer(aContainer), mRect(aRect) {} virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList) { // We are not a stacking context root. There is no valid underlying // frame for the whole list. - return new (aBuilder) nsDisplayClip(nsnull, aList, mRect); + return new (aBuilder) nsDisplayClip(nsnull, mContainer, aList, mRect); } virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) { - return new (aBuilder) nsDisplayClip(aItem->GetUnderlyingFrame(), aItem, mRect); + return new (aBuilder) nsDisplayClip(aItem->GetUnderlyingFrame(), + mContainer, aItem, mRect); } protected: - nsRect mRect; + nsIFrame* mContainer; + nsRect mRect; }; nsresult @@ -1087,7 +1089,7 @@ nsIFrame::Clip(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aToSet, const nsRect& aClipRect) { - nsAbsPosClipWrapper wrapper(aClipRect); + nsAbsPosClipWrapper wrapper(this, aClipRect); return wrapper.WrapLists(aBuilder, this, aFromSet, aToSet); } @@ -1236,7 +1238,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, resultList.AppendToTop(set.PositionedDescendants()); if (applyAbsPosClipping) { - nsAbsPosClipWrapper wrapper(absPosClip); + nsAbsPosClipWrapper wrapper(this, absPosClip); nsDisplayItem* item = wrapper.WrapList(aBuilder, this, &resultList); if (!item) return NS_ERROR_OUT_OF_MEMORY; @@ -1253,6 +1255,34 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, return rv; } +class nsDisplaySummary : public nsDisplayItem +{ +public: + nsDisplaySummary(nsIFrame* aFrame) : nsDisplayItem(aFrame) { + MOZ_COUNT_CTOR(nsDisplaySummary); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplaySummary() { + MOZ_COUNT_DTOR(nsDisplaySummary); + } +#endif + + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); + NS_DISPLAY_DECL_NAME("Summary") +}; + +nsRect +nsDisplaySummary::GetBounds(nsDisplayListBuilder* aBuilder) { + return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame); +} + +static void +AddSummaryFrameToList(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, nsDisplayList* aList) +{ + aList->AppendNewToTop(new (aBuilder) nsDisplaySummary(aFrame)); +} + nsresult nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* aChild, @@ -1336,7 +1366,17 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // No position-varying content has been rendered in this prescontext. // Therefore there is no need to descend into analyzing the moving frame's // descendants looking for such content, because any bitblit will - // not be copying position-varying graphics. + // not be copying position-varying graphics. However, to keep things + // sane we still need display items representing the frame subtree. + // We need to add these summaries to every list that the child could + // contribute to. This avoids display list optimizations optimizing + // away entire lists because they appear to be empty. + AddSummaryFrameToList(aBuilder, aChild, aLists.BlockBorderBackgrounds()); + AddSummaryFrameToList(aBuilder, aChild, aLists.BorderBackground()); + AddSummaryFrameToList(aBuilder, aChild, aLists.Content()); + AddSummaryFrameToList(aBuilder, aChild, aLists.Floats()); + AddSummaryFrameToList(aBuilder, aChild, aLists.PositionedDescendants()); + AddSummaryFrameToList(aBuilder, aChild, aLists.Outlines()); return NS_OK; } } @@ -1427,7 +1467,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (NS_SUCCEEDED(rv)) { if (isPositioned && applyAbsPosClipping) { - nsAbsPosClipWrapper wrapper(clipRect); + nsAbsPosClipWrapper wrapper(aChild, clipRect); rv = wrapper.WrapListsInPlace(aBuilder, aChild, pseudoStack); } } diff --git a/layout/generic/nsFrameFrame.cpp b/layout/generic/nsFrameFrame.cpp index d1ba0ab46cd7..de198c8b6458 100644 --- a/layout/generic/nsFrameFrame.cpp +++ b/layout/generic/nsFrameFrame.cpp @@ -352,7 +352,7 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, &childItems); if (NS_SUCCEEDED(rv)) { rv = aLists.Content()->AppendNewToTop( - new (aBuilder) nsDisplayClip(nsnull, &childItems, + new (aBuilder) nsDisplayClip(nsnull, this, &childItems, nsRect(aBuilder->ToReferenceFrame(f), f->GetSize()))); // delete childItems in case of OOM childItems.DeleteAll();