Bug 428156. Relanding. Make ComputeRepaintRegionForCopy handle non-moving frames that clip moving frames. r+sr=dbaron

This commit is contained in:
roc+@cs.cmu.edu 2008-04-18 02:21:21 -07:00
parent bb0afdab7b
commit a091e3d5c6
6 changed files with 100 additions and 27 deletions

View File

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

View File

@ -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_*/

View File

@ -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<nsDisplayClip*>(item)->GetClipRect());
nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(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);

View File

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

View File

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

View File

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