Bug 335140. Set 'force descend into' bit on frames and then leave it set until we're done with the display list ... then we can be sure of descending to frames whose container is not an ancestor of the placeholder. r=mrbkap

This commit is contained in:
roc+%cs.cmu.edu 2006-04-27 02:45:03 +00:00
Родитель 65c414720c
Коммит 211581d5a4
9 изменённых файлов: 137 добавлений и 204 удалений

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIViewManager.h"
#include "nsIBlender.h"
#include "nsTransform2D.h"
#include "nsFrameManager.h"
#include "nsPlaceholderFrame.h"
#ifdef MOZ_CAIRO_GFX
#include "gfxContext.h"
@ -83,7 +85,68 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
}
}
// Destructor function for the dirty rect property
static void
DestroyRectFunc(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
delete NS_STATIC_CAST(nsRect*, aPropertyValue);
}
static nsIFrame* GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
nsIFrame* aFrame) {
if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
return aFrameManager->GetPlaceholderFrameFor(aFrame);
}
return aFrame->GetParent();
}
static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
nsFrameManager* frameManager = aFrame->GetPresContext()->PresShell()->FrameManager();
for (nsIFrame* f = aFrame; f; f = GetParentOrPlaceholderFor(frameManager, f)) {
if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)
return;
f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
if (f == aStopAtFrame) {
// we've reached a frame that we know will be painted, so we can stop.
break;
}
}
}
static void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
const nsRect& aDirtyRect) {
nsRect dirty = aDirtyRect - aFrame->GetOffsetTo(aDirtyFrame);
nsRect overflowRect = aFrame->GetOverflowRect();
if (!dirty.IntersectRect(dirty, overflowRect))
return;
// if "new nsRect" fails, this won't do anything, but that's okay
aFrame->SetProperty(nsLayoutAtoms::outOfFlowDirtyRectProperty,
new nsRect(dirty), DestroyRectFunc);
MarkFrameForDisplay(aFrame, aDirtyFrame);
}
static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
aFrame->DeleteProperty(nsLayoutAtoms::outOfFlowDirtyRectProperty);
nsFrameManager* frameManager = aFrame->GetPresContext()->PresShell()->FrameManager();
for (nsIFrame* f = aFrame; f; f = GetParentOrPlaceholderFor(frameManager, f)) {
if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
return;
f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
}
}
nsDisplayListBuilder::~nsDisplayListBuilder() {
for (PRUint32 i = 0; i < mFramesMarkedForDisplay.Length(); ++i) {
UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
}
PL_FreeArenaPool(&mPool);
PL_FinishArenaPool(&mPool);
}
@ -106,18 +169,24 @@ nsDisplayListBuilder::GetCaret() {
void
nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
const nsRect& aDirtyRect) {
if (!mBuildCaret) {
if (!mBuildCaret)
return;
}
nsIPresShell* shell = aReferenceFrame->GetPresContext()->PresShell();
nsCOMPtr<nsICaret> caret;
shell->GetCaret(getter_AddRefs(caret));
nsIFrame* frame = caret->GetCaretFrame();
nsLayoutUtils::MarkCaretSubtreeForPainting(this, aReferenceFrame,
frame, caret->GetCaretRect(),
aDirtyRect, PR_TRUE);
if (frame) {
// Check if the dirty rect intersects with the caret's dirty rect.
nsRect caretRect =
caret->GetCaretRect() + frame->GetOffsetTo(aReferenceFrame);
if (caretRect.Intersects(aDirtyRect)) {
// Okay, our rects intersect, let's mark the frame and all of its ancestors.
mFramesMarkedForDisplay.AppendElement(frame);
MarkFrameForDisplay(frame, nsnull);
}
}
mCaretStates.AppendElement(frame);
}
@ -126,26 +195,27 @@ void
nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
const nsRect& aDirtyRect)
{
if (!mBuildCaret) {
if (!mBuildCaret)
return;
}
// Pop the state off.
NS_ASSERTION(mCaretStates.Length() > 0, "Leaving too many PresShell");
nsICaret* caret = GetCaret();
if (caret) {
nsLayoutUtils::MarkCaretSubtreeForPainting(this, aReferenceFrame,
GetCaretFrame(),
caret->GetCaretRect(),
aDirtyRect, PR_FALSE);
} else {
NS_ASSERTION(GetCaretFrame() == nsnull,
"GetCaret and LeavePresShell diagree");
}
NS_ASSERTION(GetCaret() || GetCaretFrame() == nsnull,
"GetCaret and LeavePresShell diagree");
mCaretStates.SetLength(mCaretStates.Length() - 1);
}
void
nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, nsIFrame* aFrames,
const nsRect& aDirtyRect) {
while (aFrames) {
mFramesMarkedForDisplay.AppendElement(aFrames);
MarkOutOfFlowFrameForDisplay(aDirtyFrame, aFrames, aDirtyRect);
aFrames = aFrames->GetNextSibling();
}
}
void*
nsDisplayListBuilder::Allocate(size_t aSize) {
void *tmp;

Просмотреть файл

@ -235,6 +235,17 @@ public:
*/
void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
/**
* Mark aFrames and its (next) siblings to be displayed if they
* intersect aDirtyRect (which is relative to aDirtyFrame). If the
* frame(s) have placeholders that might not be displayed, we mark the
* placeholders and their ancestors to ensure that display list construction
* descends into them anyway. nsDisplayListBuilder will take care of
* unmarking them when it is destroyed.
*/
void MarkFramesForDisplayList(nsIFrame* aDirtyFrame, nsIFrame* aFrames,
const nsRect& aDirtyRect);
/**
* Allocate memory in our arena. It will only be freed when this display list
* builder is destroyed. This memory holds nsDisplayItems. nsDisplayItem
@ -272,7 +283,8 @@ private:
nsIFrame* mIgnoreScrollFrame;
PLArenaPool mPool;
nsCOMPtr<nsISelection> mBoundingSelection;
nsTArray<nsIFrame *> mCaretStates;
nsTArray<nsIFrame*> mCaretStates;
nsTArray<nsIFrame*> mFramesMarkedForDisplay;
PRPackedBool mBuildCaret;
PRPackedBool mEventDelivery;
PRPackedBool mIsBackgroundOnly;

Просмотреть файл

@ -960,40 +960,3 @@ nsLayoutUtils::ScrollIntoView(nsIFormControlFrame* aFormFrame)
}
}
}
void
nsLayoutUtils::MarkCaretSubtreeForPainting(nsDisplayListBuilder* aBuilder,
nsIFrame* aReferenceFrame,
nsIFrame* aCaretFrame,
const nsRect& aCaretRect,
const nsRect& aRealDirtyRect,
PRBool aMark)
{
// Easy test: If there is no caret, we don't have anything to do.
if (!aCaretFrame) {
return;
}
NS_ASSERTION(aReferenceFrame, "We must have a reference frame");
// Check if the dirty rect intersects with the caret's dirty rect.
nsRect caretRect = aCaretRect + aCaretFrame->GetOffsetTo(aReferenceFrame);
if (!caretRect.Intersects(aRealDirtyRect)) {
return;
}
if (aMark) {
// Okay, our rects intersect, let's mark the frame and all of its ancestors.
do {
aCaretFrame->AddStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
aCaretFrame = aCaretFrame->GetParent();
} while (aCaretFrame);
return;
}
do {
aCaretFrame->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
aCaretFrame = aCaretFrame->GetParent();
} while (aCaretFrame);
}

Просмотреть файл

@ -413,26 +413,6 @@ public:
* @param aFormFrame Frame to scroll into view.
*/
static void ScrollIntoView(nsIFormControlFrame* aFormFrame);
/**
* Ensure that the caret frame's subtree is painted by the next paint.
* @param aBuilder The display list builder that we're going to be painting
* with.
* @param aCaretFrame The frame that the caret is currently in.
* @param aReferenceFrame The frame whose coodinate space aRealDirtyRect is
* in.
* @param aCaretRect The rect (in aCaretFrame's coordinates) that the caret
* wants to be in.
* @param aRealDirtyRect The rect (in aReferenceFrame's coordinates) that is
* the original dirty rect.
* @param aMark Whether we're marking or unmarking the frames.
*/
static void MarkCaretSubtreeForPainting(nsDisplayListBuilder* aBuilder,
nsIFrame* aReferenceFrame,
nsIFrame* aCaretFrame,
const nsRect& aCaretRect,
const nsRect& aRealDirtyRect,
PRBool aMark);
};
#endif // nsLayoutUtils_h__

Просмотреть файл

@ -6301,7 +6301,11 @@ DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
}
DebugOutputDrawLine(aDepth, aLine.get(), intersect);
#endif
if (!intersect && !(aFrame->GetStateBits() & NS_FRAME_HAS_DESCENDANT_PLACEHOLDER))
// The line might contain a placeholder for a visible out-of-flow, in which
// case we need to descend into it. If there is such a placeholder, we will
// have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
if (!intersect &&
!(aFrame->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
return NS_OK;
nsresult rv;
@ -6360,13 +6364,13 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
DisplayBorderBackgroundOutline(aBuilder, aLists);
MarkOutOfFlowChildrenForDisplayList(mFloats.FirstChild(), aDirtyRect);
MarkOutOfFlowChildrenForDisplayList(mAbsoluteContainer.GetFirstChild(), aDirtyRect);
aBuilder->MarkFramesForDisplayList(this, mFloats.FirstChild(), aDirtyRect);
aBuilder->MarkFramesForDisplayList(this, mAbsoluteContainer.GetFirstChild(), aDirtyRect);
// Don't use the line cursor if we have a descendant placeholder ... we could
// skip lines that contain placeholders but don't themselves intersect with
// the dirty area.
nsLineBox* cursor = GetStateBits() & NS_FRAME_HAS_DESCENDANT_PLACEHOLDER
// Don't use the line cursor if we might have a descendant placeholder ...
// it might skip lines that contain placeholders but don't themselves
// intersect with the dirty area.
nsLineBox* cursor = GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO
? nsnull : GetFirstLineContaining(aDirtyRect.y);
line_iterator line_end = end_lines();
nsresult rv = NS_OK;
@ -6443,9 +6447,6 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
#endif
UnmarkOutOfFlowChildrenForDisplayList(mFloats.FirstChild());
UnmarkOutOfFlowChildrenForDisplayList(mAbsoluteContainer.GetFirstChild());
return rv;
}

Просмотреть файл

@ -973,7 +973,6 @@ static PRBool ApplyAbsPosClipping(nsDisplayListBuilder* aBuilder,
// for nsLayoutUtils::ComputeRepaintRegionForCopy ... but this is a rare
// situation.
if (aBuilder->HasMovingFrames() &&
(aFrame->GetStateBits() & NS_FRAME_HAS_DESCENDANT_PLACEHOLDER) &&
aFrame->GetPresContext()->FrameManager()->GetRootFrame()->
GetFirstChild(nsLayoutAtoms::fixedList) &&
aBuilder->IsMovingFrame(aFrame))
@ -1138,10 +1137,8 @@ nsresult
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
nsDisplayList* aList) {
if (GetStateBits() & NS_FRAME_IS_UNFLOWABLE) {
RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
if (GetStateBits() & NS_FRAME_IS_UNFLOWABLE)
return NS_OK;
}
// Replaced elements have their visibility handled here, because
// they're visually atomic
@ -1167,7 +1164,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder::AutoIsRootSetter rootSetter(aBuilder, PR_TRUE);
rv = BuildDisplayList(aBuilder, dirtyRect, set);
}
RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
NS_ENSURE_SUCCESS(rv, rv);
if (aBuilder->IsBackgroundOnly()) {
@ -1255,70 +1251,6 @@ static nsIFrame* GetParentOrPlaceholderFor(nsFrameManager* aFrameManager, nsIFra
return aFrame->GetParent();
}
// Destructor function for the overflow area property
static void
DestroyRectFunc(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
delete NS_STATIC_CAST(nsRect*, aPropertyValue);
}
static void MarkOutOfFlowChild(nsIFrame* aFrame, nsIFrame* aChild,
const nsRect& aDirtyRect, PRBool aMark) {
if (aMark) {
nsRect dirty = aDirtyRect - aChild->GetOffsetTo(aFrame);
nsRect overflowRect = aChild->GetOverflowRect();
if (!dirty.IntersectRect(dirty, overflowRect))
return;
// if "new nsRect" fails, this won't do anything, but that's okay
aChild->SetProperty(nsLayoutAtoms::outOfFlowDirtyRectProperty,
new nsRect(dirty), DestroyRectFunc);
} else {
aChild->DeleteProperty(nsLayoutAtoms::outOfFlowDirtyRectProperty);
}
nsFrameManager* frameManager = aChild->GetPresContext()->PresShell()->FrameManager();
nsIFrame* placeholder = frameManager->GetPlaceholderFrameFor(aChild);
NS_ASSERTION(placeholder, "No placeholder for out of flow?");
if (!placeholder)
return;
nsIFrame* f;
for (f = placeholder; f; f = GetParentOrPlaceholderFor(frameManager, f)) {
if (((f->GetStateBits() & NS_FRAME_HAS_DESCENDANT_PLACEHOLDER) != 0)
== aMark)
return;
if (aMark) {
f->AddStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
} else {
f->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
}
if (f == aFrame)
break;
}
NS_ASSERTION(f, "Did not find ourselves on the placeholder's ancestor chain");
}
void
nsIFrame::MarkOutOfFlowChildrenForDisplayList(nsIFrame* aFirstChild,
const nsRect& aDirtyRect) {
while (aFirstChild) {
MarkOutOfFlowChild(this, aFirstChild, aDirtyRect, PR_TRUE);
aFirstChild = aFirstChild->GetNextSibling();
}
}
void
nsIFrame::UnmarkOutOfFlowChildrenForDisplayList(nsIFrame* aFirstChild) {
nsRect empty;
while (aFirstChild) {
MarkOutOfFlowChild(this, aFirstChild, empty, PR_FALSE);
aFirstChild = aFirstChild->GetNextSibling();
}
}
#ifdef NS_DEBUG
static void PaintDebugBorder(nsIFrame* aFrame, nsIRenderingContext* aCtx,
const nsRect& aDirtyRect, nsPoint aPt) {
@ -1394,13 +1326,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// to descend into it because its scrolled child may intersect the dirty
// area even if the scrollframe itself doesn't.
if (dirty.IsEmpty() &&
!(aChild->GetStateBits() & NS_FRAME_HAS_DESCENDANT_PLACEHOLDER) &&
!(aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
aChild != aBuilder->GetIgnoreScrollFrame())
return NS_OK;
// Don't remove NS_FRAME_HAS_DESCENDANT_PLACEHOLDER until after we've
// processed the frame ... it could be useful for frames to know this
if (aChild->GetStyleVisibility()->mVisible == NS_STYLE_VISIBILITY_COLLAPSE)
return NS_OK;
// XXX need to have inline-block and inline-table set pseudoStackingContext
@ -1455,7 +1384,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
rv = aBuilder->DisplayCaret(aChild, dirty, aLists);
}
}
aChild->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
return rv;
}
@ -1495,7 +1423,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
rv = aBuilder->DisplayCaret(aChild, dirty, aLists);
}
}
aChild->RemoveStateBits(NS_FRAME_HAS_DESCENDANT_PLACEHOLDER);
if (NS_SUCCEEDED(rv)) {
if (isPositioned && applyAbsPosClipping) {
@ -4610,6 +4537,16 @@ nsFrame::GetAccessible(nsIAccessible** aAccessible)
}
#endif
// Destructor function for the overflow area property
static void
DestroyRectFunc(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
delete NS_STATIC_CAST(nsRect*, aPropertyValue);
}
nsRect*
nsIFrame::GetOverflowAreaProperty(PRBool aCreateIfNecessary)
{

Просмотреть файл

@ -140,7 +140,7 @@ typedef PRUint32 nsFrameState;
#define NS_FRAME_IN_REFLOW 0x00000001
// This is only set during painting
#define NS_FRAME_HAS_DESCENDANT_PLACEHOLDER 0x00000001
#define NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO 0x00000001
// This bit is set when a frame is created. After it has been reflowed
// once (during the DidReflow with a finished state) the bit is
@ -752,35 +752,6 @@ public:
const nsDisplayListSet& aToSet,
const nsRect& aClipRect);
/**
* Out-of-flow elements are painted as if they were part of their
* containing element, for clipping, compositing and z-ordering (modulo
* special z-order rules for floats and positioned elements). Therefore
* we paint out-of-flow frames through their placeholders. During
* display list construction, a container for out-of-flow frames will invoke
* this method on itself to mark ancestors of relevant placeholders --- up to
* and including itself --- with NS_FRAME_HAS_DESCENDANT_PLACEHOLDER, to
* ensure that those frames are descended into during display list
* construction (they might otherwise be excluded if they do not
* intersect the dirty rect).
*
* After display list construction of descendants has finished, we call
* UnmarkOutOfFlowChildrenForDisplayList on each out-of-flow child. For
* performance reasons we also unmark each frame after we have finished
* building a display list for it; this allows
* UnmarkOutOfFlowChildrenForDisplayList to terminate early when it reaches
* a frame in its parent chain without the NS_FRAME_HAS_DESCENDANT_PLACEHOLDER
* bit. If CSS 'visiblity' is not being used and there are no failures during
* painting, each out-of-flow's placeholder will have been unmarked and so
* UnmarkOutOfFlowChildrenForDisplayList won't need to do any real work.
*
* @param aFirstChild the first frame to mark; we mark the entire sibling
* list
*/
void MarkOutOfFlowChildrenForDisplayList(nsIFrame* aFirstChild,
const nsRect& aDirtyRect);
void UnmarkOutOfFlowChildrenForDisplayList(nsIFrame* aFirstChild);
enum {
DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT = 0x01,
DISPLAY_CHILD_INLINE = 0x02

Просмотреть файл

@ -57,6 +57,7 @@
#include "nsIServiceManager.h"
#include "nsIAccessibilityService.h"
#endif
#include "nsDisplayList.h"
#ifdef DEBUG
#undef NOISY_PUSHING
@ -1085,10 +1086,8 @@ nsPositionedInlineFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
MarkOutOfFlowChildrenForDisplayList(mAbsoluteContainer.GetFirstChild(), aDirtyRect);
nsresult rv = nsHTMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
UnmarkOutOfFlowChildrenForDisplayList(mAbsoluteContainer.GetFirstChild());
return rv;
aBuilder->MarkFramesForDisplayList(this, mAbsoluteContainer.GetFirstChild(), aDirtyRect);
return nsHTMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
}
nsIAtom*

Просмотреть файл

@ -50,6 +50,7 @@
#include "nsPresContext.h"
#include "nsReflowPath.h"
#include "nsIPresShell.h"
#include "nsDisplayList.h"
nsIFrame*
NS_NewViewportFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@ -93,17 +94,16 @@ ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// mark our visible out-of-flow frames (i.e., the fixed position frames) so
// that display list construction is guaranteed to recurse into their
// ancestors.
MarkOutOfFlowChildrenForDisplayList(mFixedContainer.GetFirstChild(), aDirtyRect);
aBuilder->MarkFramesForDisplayList(this, mFixedContainer.GetFirstChild(), aDirtyRect);
nsIFrame* kid = mFrames.FirstChild();
nsresult rv = NS_OK;
if (kid) {
// make the kid's BorderBackground our own. This ensures that the canvas
// frame's background becomes our own background and therefore appears
// below negative z-index elements.
rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
}
UnmarkOutOfFlowChildrenForDisplayList(mFixedContainer.GetFirstChild());
return rv;
if (!kid)
return NS_OK;
// make the kid's BorderBackground our own. This ensures that the canvas
// frame's background becomes our own background and therefore appears
// below negative z-index elements.
return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
}
NS_IMETHODIMP