Bug 209694. Major rework of margin-collapsing and clearance to match CSS 2.1 rules. Also includes major incremental reflow fixes for those situations. See the bug for details. rubber-stamp r+sr=dbaron

git-svn-id: svn://10.0.0.236/trunk@165781 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
roc+%cs.cmu.edu 2004-11-25 14:51:00 +00:00
parent c5ebd56e85
commit d4a6c32e7b
28 changed files with 1734 additions and 1104 deletions

View File

@ -434,3 +434,33 @@ nsLayoutUtils::GetNearestScrollingView(nsIView* aView, Direction aDirection)
}
return scrollableView;
}
// Combine aNewBreakType with aOrigBreakType, but limit the break types
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
PRUint8
nsLayoutUtils::CombineBreakType(PRUint8 aOrigBreakType,
PRUint8 aNewBreakType)
{
PRUint8 breakType = aOrigBreakType;
switch(breakType) {
case NS_STYLE_CLEAR_LEFT:
if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
}
break;
case NS_STYLE_CLEAR_RIGHT:
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
}
break;
case NS_STYLE_CLEAR_NONE:
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
(NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = aNewBreakType;
}
}
return breakType;
}

View File

@ -236,6 +236,10 @@ public:
* otherwise return nsnull.
*/
static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPossiblePlaceholder);
// Combine aNewBreakType with aOrigBreakType, but limit the break types
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
};
#endif // nsLayoutUtils_h__

View File

@ -237,6 +237,13 @@ struct nsHTMLReflowState {
// a frame (e.g. nsTableFrame) which initiates a special reflow for percent height calculations
nsIFrame* mPercentHeightReflowInitiator;
// CSS margin collapsing sometimes requires us to reflow
// optimistically assuming that margins collapse to see if clearance
// is required. When we discover that clearance is required, we
// store the frame in which clearance was discovered to the location
// requested here.
nsIFrame** mDiscoveredClearance;
// This value keeps track of how deeply nested a given reflow state
// is from the top of the frame tree.
PRInt16 mReflowDepth;
@ -249,6 +256,7 @@ struct nsHTMLReflowState {
PRUint16 mIsTopOfPage:1; // is the current context at the top of a page?
PRUint16 mBlinks:1; // Keep track of text-decoration: blink
PRUint16 mVisualBidiFormControl:1; // Keep track of descendants of form controls on Visual Bidi pages
PRUint16 mHasClearance:1; // Block has clearance
} mFlags;
#ifdef IBMBIDI

View File

@ -236,6 +236,10 @@ public:
* otherwise return nsnull.
*/
static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPossiblePlaceholder);
// Combine aNewBreakType with aOrigBreakType, but limit the break types
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
};
#endif // nsLayoutUtils_h__

View File

@ -434,3 +434,33 @@ nsLayoutUtils::GetNearestScrollingView(nsIView* aView, Direction aDirection)
}
return scrollableView;
}
// Combine aNewBreakType with aOrigBreakType, but limit the break types
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
PRUint8
nsLayoutUtils::CombineBreakType(PRUint8 aOrigBreakType,
PRUint8 aNewBreakType)
{
PRUint8 breakType = aOrigBreakType;
switch(breakType) {
case NS_STYLE_CLEAR_LEFT:
if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
}
break;
case NS_STYLE_CLEAR_RIGHT:
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
}
break;
case NS_STYLE_CLEAR_NONE:
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
(NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
breakType = aNewBreakType;
}
}
return breakType;
}

View File

@ -1080,6 +1080,22 @@ nsSpaceManager::PopState()
}
}
void
nsSpaceManager::DiscardState()
{
NS_ASSERTION(mSavedStates, "Invalid call to DiscardState()!");
if (!mSavedStates) {
return;
}
SpaceManagerState *state = mSavedStates;
mSavedStates = mSavedStates->mNext;
if(state != &mAutoState) {
delete state;
}
}
nscoord
nsSpaceManager::GetLowestRegionTop()
{

View File

@ -311,6 +311,12 @@ public:
*/
void PopState();
/**
* Pops the state off the stack without restoring it. Useful for speculative
* reflow where we're not sure if we're going to keep the result.
*/
void DiscardState();
/**
* Get the top of the last region placed into the space manager, to
* enforce the rule that a float can't be above an earlier float.

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,11 @@ class nsIntervalSet;
#define NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS 0x04000000
#define NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS 0x08000000
// Set on any block that has descendant frames in the normal
// flow with 'clear' set to something other than 'none'
// (including <BR CLEAR="..."> frames)
#define NS_BLOCK_HAS_CLEAR_CHILDREN 0x10000000
#define nsBlockFrameSuper nsHTMLContainerFrame
#define NS_BLOCK_FRAME_CID \
@ -224,6 +229,11 @@ public:
virtual void DeleteNextInFlowChild(nsPresContext* aPresContext,
nsIFrame* aNextInFlow);
// Determines whether the collapsed margin carried out of the last
// line includes the margin-top of a line with clearance (in which
// case we must avoid collapsing that margin with our bottom margin)
PRBool CheckForCollapsedBottomMarginFromClearanceLine();
/** return the topmost block child based on y-index.
* almost always the first or second line, if there is one.
* accounts for lines that hold only compressed white space, etc.

View File

@ -52,6 +52,7 @@
#include "nsIDOMHTMLBodyElement.h"
#include "nsLayoutAtoms.h"
#include "nsCOMPtr.h"
#include "nsLayoutUtils.h"
#ifdef NS_DEBUG
#undef NOISY_MAX_ELEMENT_SIZE
@ -79,13 +80,12 @@ nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH;
}
void
nsBlockReflowContext::ComputeCollapsedTopMargin(nsPresContext* aPresContext,
nsHTMLReflowState& aRS,
/* inout */ nsCollapsingMargin& aMargin)
PRBool
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry)
{
// Get aFrame's top margin
aMargin.Include(aRS.mComputedMargin.top);
// Include frame's top margin
aMargin->Include(aRS.mComputedMargin.top);
// The inclusion of the bottom margin when empty is done by the caller
// since it doesn't need to be done by the top-level (non-recursive)
@ -93,48 +93,77 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsPresContext* aPresContext,
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame);
printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin.get());
printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get());
#endif
// Calculate aFrame's generational top-margin from its child
// blocks. Note that if aFrame has a non-zero top-border or
PRBool dirtiedLine = PR_FALSE;
// Calculate the frame's generational top-margin from its child
// blocks. Note that if the frame has a non-zero top-border or
// top-padding then this step is skipped because it will be a margin
// root. It is also skipped if the frame is a margin root for other
// reasons.
void* bf;
if (0 == aRS.mComputedBorderPadding.top &&
!(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID,
NS_REINTERPRET_CAST(void**, &bf)))) {
for (nsBlockFrame::line_iterator line = bf->begin_lines(),
line_end = bf->end_lines();
line != line_end; ++line) {
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
// Here is where we recur. Now that we have determined that a
// generational collapse is required we need to compute the
// child blocks margin and so in so that we can look into
// it. For its margins to be computed we need to have a reflow
// state for it. Since the reflow reason is irrelevant, we'll
// arbitrarily make it a `resize' to avoid the path-plucking
// behavior if we're in an incremental reflow.
nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
nsHTMLReflowState reflowState(aPresContext, aRS, line->mFirstChild,
availSpace, eReflowReason_Resize);
ComputeCollapsedTopMargin(aPresContext, reflowState, aMargin);
if (isEmpty)
aMargin.Include(reflowState.mComputedMargin.bottom);
}
if (!isEmpty)
break;
!(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) &&
NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, &bf))) {
nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, aRS.frame);
for (nsBlockFrame::line_iterator line = block->begin_lines(),
line_end = block->end_lines();
line != line_end; ++line) {
if (!aClearanceFrame && line->HasClearance()) {
// If we don't have a clearance frame, then we're computing
// the collapsed margin in the first pass, assuming that all
// lines have no clearance. So clear their clearance flags.
line->ClearHasClearance();
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
nsBlockFrame* kidBlock = NS_STATIC_CAST(nsBlockFrame*, line->mFirstChild);
if (kidBlock == aClearanceFrame) {
line->SetHasClearance();
line->MarkDirty();
dirtiedLine = PR_TRUE;
break;
}
// Here is where we recur. Now that we have determined that a
// generational collapse is required we need to compute the
// child blocks margin and so in so that we can look into
// it. For its margins to be computed we need to have a reflow
// state for it. Since the reflow reason is irrelevant, we'll
// arbitrarily make it a `resize' to avoid the path-plucking
// behavior if we're in an incremental reflow.
nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
nsHTMLReflowState reflowState(kidBlock->GetPresContext(),
aRS, kidBlock,
availSpace, eReflowReason_Resize);
// Record that we're being optimistic by assuming the kid
// has no clearance
if (kidBlock->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
*aMayNeedRetry = PR_TRUE;
}
if (ComputeCollapsedTopMargin(reflowState, aMargin, aClearanceFrame, aMayNeedRetry)) {
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
if (isEmpty)
aMargin->Include(reflowState.mComputedMargin.bottom);
}
if (!isEmpty)
break;
}
}
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame);
printf(": => %d\n", aMargin.get());
#endif
return dirtiedLine;
}
struct nsBlockHorizontalAlign {
@ -302,7 +331,8 @@ nsPointDtor(void *aFrame, nsIAtom *aPropertyName,
nsresult
nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
PRBool aApplyTopMargin,
nsCollapsingMargin& aPrevBottomMargin,
nsCollapsingMargin& aPrevMargin,
nscoord aClearance,
PRBool aIsAdjacentWithTop,
nsMargin& aComputedOffsets,
nsHTMLReflowState& aFrameRS,
@ -354,32 +384,8 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
aFrameRS.reason = eReflowReason_Dirty;
}
/* We build a different reflow context based on the width attribute of the block,
* if it's a float.
* Auto-width floats need to have their containing-block size set explicitly,
* factoring in other floats that impact it.
* It's possible this should be quirks-only.
* All other blocks proceed normally.
*/
// XXXldb We should really fix this in nsHTMLReflowState::InitConstraints instead.
const nsStylePosition* position = mFrame->GetStylePosition();
nsStyleUnit widthUnit = position->mWidth.GetUnit();
const nsStyleDisplay* display = mFrame->GetStyleDisplay();
if ((eStyleUnit_Auto == widthUnit) &&
((NS_STYLE_FLOAT_LEFT == display->mFloats) ||
(NS_STYLE_FLOAT_RIGHT == display->mFloats))) {
// Initialize the reflow state and constrain the containing block's
// width and height to the available width and height.
aFrameRS.Init(mPresContext, mSpace.width, mSpace.height);
}
else {
// Initialize the reflow state and use the containing block's computed
// width and height (or derive appropriate values for an
// absolutely positioned frame).
aFrameRS.Init(mPresContext);
}
aComputedOffsets = aFrameRS.mComputedOffsets;
if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
nsPropertyTable *propTable = mPresContext->PropertyTable();
@ -404,23 +410,20 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
mComputedWidth = aFrameRS.mComputedWidth;
if (aApplyTopMargin) {
// Compute the childs collapsed top margin (its margin collpased
// with its first childs top-margin -- recursively).
ComputeCollapsedTopMargin(mPresContext, aFrameRS, aPrevBottomMargin);
mTopMargin = aPrevMargin;
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, mOuterReflowState.frame);
printf(": reflowing ");
nsFrame::ListTag(stdout, mFrame);
printf(" margin => %d\n", aPrevBottomMargin.get());
printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
#endif
// Adjust the available height if its constrained so that the
// child frame doesn't think it can reflow into its margin area.
if (NS_UNCONSTRAINEDSIZE != aFrameRS.availableHeight) {
aFrameRS.availableHeight -= aPrevBottomMargin.get();
aFrameRS.availableHeight -= mTopMargin.get() + aClearance;
}
mTopMargin = aPrevBottomMargin;
}
// Compute x/y coordinate where reflow will begin. Use the rules
@ -431,7 +434,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
mStyleMargin = aFrameRS.mStyleMargin;
mStylePadding = aFrameRS.mStylePadding;
nscoord x;
nscoord y = mSpace.y + mTopMargin.get();
nscoord y = mSpace.y + mTopMargin.get() + aClearance;
// If it's a right floated element, then calculate the x-offset
// differently
@ -647,6 +650,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
PRBool
nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool aForceFit,
nsLineBox* aLine,
const nsMargin& aComputedOffsets,
nsCollapsingMargin& aBottomMarginResult,
nsRect& aInFlowBounds,
@ -660,15 +664,13 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool fits = PR_TRUE;
nscoord x = mX;
nscoord y = mY;
// When deciding whether it's empty we also need to take into
// account the overflow area
// XXXldb What should really matter is whether there exist non-
// empty frames in the block (with appropriate whitespace munging).
// Consider the case where we clip off the overflow with
// 'overflow: -moz-hidden-unscrollable' (which doesn't currently
// affect mOverflowArea, but probably should.
if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height))
// Check whether the block's bottom margin collapses with its top
// margin. See CSS 2.1 section 8.3.1; those rules seem to match
// nsBlockFrame::IsEmpty(). Any such block must have zero height so
// check that first.
if (0 == mMetrics.height && !aLine->HasClearance() &&
aLine->CachedIsEmpty())
{
// Collapse the bottom margin with the top margin that was already
// applied.

View File

@ -39,10 +39,12 @@
#define nsBlockReflowContext_h___
#include "nsIFrame.h"
#include "nsHTMLReflowState.h"
#include "nsHTMLReflowMetrics.h"
class nsBlockFrame;
class nsBlockReflowState;
class nsHTMLReflowState;
class nsLineBox;
class nsIFrame;
class nsPresContext;
class nsLineLayout;
@ -62,7 +64,8 @@ public:
nsresult ReflowBlock(const nsRect& aSpace,
PRBool aApplyTopMargin,
nsCollapsingMargin& aPrevBottomMargin,
nsCollapsingMargin& aPrevMargin,
nscoord aClearance,
PRBool aIsAdjacentWithTop,
nsMargin& aComputedOffsets,
nsHTMLReflowState& aReflowState,
@ -70,6 +73,7 @@ public:
PRBool PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool aForceFit,
nsLineBox* aLine,
const nsMargin& aComputedOffsets,
nsCollapsingMargin& aBottomMarginResult /* out */,
nsRect& aInFlowBounds,
@ -101,9 +105,23 @@ public:
return mMetrics.mMaximumWidth;
}
static void ComputeCollapsedTopMargin(nsPresContext* aPresContext,
nsHTMLReflowState& aRS,
/* inout */ nsCollapsingMargin& aMargin);
/**
* Computes the collapsed top margin for a block whose reflow state is in aRS.
* The computed margin is added into aMargin.
* If aClearanceFrame is null then this is the first optimistic pass which shall assume
* that no frames have clearance, and we clear the HasClearance on all frames encountered.
* If non-null, this is the second pass and
* the caller has decided aClearanceFrame needs clearance (and we will
* therefore stop collapsing there); also, this function is responsible for marking
* it with SetHasClearance.
* If in the optimistic pass any frame is encountered that might possibly need
* clearance (i.e., if we really needed the optimism assumption) then we set aMayNeedRetry
* to true.
* We return PR_TRUE if we changed the clearance state of any line and marked it dirty.
*/
static PRBool ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
PRBool* aMayNeedRetry);
protected:
nsPresContext* mPresContext;

View File

@ -60,7 +60,8 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsPresContext* aPresContext,
nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics,
PRBool aBlockMarginRoot)
PRBool aTopMarginRoot,
PRBool aBottomMarginRoot)
: mBlock(aFrame),
mPresContext(aPresContext),
mReflowState(aReflowState),
@ -71,14 +72,10 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
{
const nsMargin& borderPadding = BorderPadding();
if (aBlockMarginRoot) {
SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE);
SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE);
}
if (0 != aReflowState.mComputedBorderPadding.top) {
if (aTopMarginRoot || 0 != aReflowState.mComputedBorderPadding.top) {
SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE);
}
if (0 != aReflowState.mComputedBorderPadding.bottom) {
if (aBottomMarginRoot || 0 != aReflowState.mComputedBorderPadding.bottom) {
SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE);
}
if (GetFlag(BRS_ISTOPMARGINROOT)) {
@ -338,68 +335,6 @@ nsBlockReflowState::GetAvailableSpace(nscoord aY)
#endif
}
PRBool
nsBlockReflowState::ClearPastFloats(PRUint8 aBreakType)
{
nscoord saveY, deltaY;
PRBool applyTopMargin = PR_FALSE;
switch (aBreakType) {
case NS_STYLE_CLEAR_LEFT:
case NS_STYLE_CLEAR_RIGHT:
case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
// Apply the previous margin before clearing
saveY = mY + mPrevBottomMargin.get();
ClearFloats(saveY, aBreakType);
#ifdef NOISY_FLOAT_CLEARING
nsFrame::ListTag(stdout, mBlock);
printf(": ClearPastFloats: mPrevBottomMargin=%d saveY=%d oldY=%d newY=%d deltaY=%d\n",
mPrevBottomMargin.get(), saveY, saveY - mPrevBottomMargin.get(), mY,
mY - saveY);
#endif
// Determine how far we just moved. If we didn't move then there
// was nothing to clear to don't mess with the normal margin
// collapsing behavior. In either case we need to restore the Y
// coordinate to what it was before the clear.
deltaY = mY - saveY;
if (0 != deltaY) {
// Pretend that the distance we just moved is a previous
// blocks bottom margin. Note that GetAvailableSpace has been
// done so that the available space calculations will be done
// after clearing the appropriate floats.
//
// What we are doing here is applying CSS2 section 9.5.2's
// rules for clearing - "The top margin of the generated box
// is increased enough that the top border edge is below the
// bottom outer edge of the floating boxes..."
//
// What this will do is cause the top-margin of the block
// frame we are about to reflow to be collapsed with that
// distance.
// XXXldb This doesn't handle collapsing with negative margins
// correctly, although it's arguable what "correct" is.
// XXX Are all the other margins included by this point?
mPrevBottomMargin.Zero();
mPrevBottomMargin.Include(deltaY);
mY = saveY;
// Force margin to be applied in this circumstance
applyTopMargin = PR_TRUE;
}
else {
// Put mY back to its original value since no clearing
// happened. That way the previous blocks bottom margin will
// be applied properly.
mY = saveY - mPrevBottomMargin.get();
}
break;
}
return applyTopMargin;
}
/*
* Reconstruct the vertical margin before the line |aLine| in order to
* do an incremental reflow that begins with |aLine| without reflowing
@ -851,12 +786,10 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache,
// See if the float should clear any preceeding floats...
if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
// XXXldb Does this handle vertical margins correctly?
ClearFloats(mY, floatDisplay->mBreakType);
mY = ClearFloats(mY, floatDisplay->mBreakType);
}
else {
// Get the band of available space
GetAvailableSpace();
}
NS_ASSERTION(floatFrame->GetParent() == mBlock,
"Float frame has wrong parent");
@ -1154,14 +1087,14 @@ nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheList& aList)
return PR_TRUE;
}
void
nscoord
nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType)
{
#ifdef DEBUG
if (nsBlockFrame::gNoisyReflow) {
nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
printf("clear floats: in: mY=%d aY=%d(%d)\n",
mY, aY, aY - BorderPadding().top);
printf("clear floats: in: aY=%d(%d)\n",
aY, aY - BorderPadding().top);
}
#endif
@ -1173,14 +1106,15 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType)
const nsMargin& bp = BorderPadding();
nscoord newY = mSpaceManager->ClearFloats(aY - bp.top, aBreakType);
mY = newY + bp.top;
GetAvailableSpace();
newY += bp.top;
#ifdef DEBUG
if (nsBlockFrame::gNoisyReflow) {
nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
printf("clear floats: out: mY=%d(%d)\n", mY, mY - bp.top);
printf("clear floats: out: y=%d(%d)\n", newY, newY - bp.top);
}
#endif
return newY;
}

View File

@ -53,7 +53,7 @@ public:
nsPresContext* aPresContext,
nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics,
PRBool aBlockMarginRoot);
PRBool aTopMarginRoot, PRBool aBottomMarginRoot);
~nsBlockReflowState();
@ -85,12 +85,9 @@ public:
PRBool PlaceBelowCurrentLineFloats(nsFloatCacheList& aFloats);
// called when clearing a line with a break type caused by a BR past
// floats, and also used internally by ClearPastFloats
void ClearFloats(nscoord aY, PRUint8 aBreakType);
// called when clearing a block past floats
PRBool ClearPastFloats(PRUint8 aBreakType);
// Returns the first coordinate >= aY that clears the
// indicated floats.
nscoord ClearFloats(nscoord aY, PRUint8 aBreakType);
PRBool IsAdjacentWithTop() const {
return mY == mReflowState.mComputedBorderPadding.top;

View File

@ -111,6 +111,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mNextInFlowUntouched = PR_FALSE;
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -143,6 +145,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mNextInFlowUntouched = PR_FALSE;
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -193,6 +197,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -240,6 +246,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -287,6 +295,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;

View File

@ -237,6 +237,13 @@ struct nsHTMLReflowState {
// a frame (e.g. nsTableFrame) which initiates a special reflow for percent height calculations
nsIFrame* mPercentHeightReflowInitiator;
// CSS margin collapsing sometimes requires us to reflow
// optimistically assuming that margins collapse to see if clearance
// is required. When we discover that clearance is required, we
// store the frame in which clearance was discovered to the location
// requested here.
nsIFrame** mDiscoveredClearance;
// This value keeps track of how deeply nested a given reflow state
// is from the top of the frame tree.
PRInt16 mReflowDepth;
@ -249,6 +256,7 @@ struct nsHTMLReflowState {
PRUint16 mIsTopOfPage:1; // is the current context at the top of a page?
PRUint16 mBlinks:1; // Keep track of text-decoration: blink
PRUint16 mVisualBidiFormControl:1; // Keep track of descendants of form controls on Visual Bidi pages
PRUint16 mHasClearance:1; // Block has clearance
} mFlags;
#ifdef IBMBIDI

View File

@ -181,13 +181,14 @@ BreakTypeToString(PRUint8 aBreakType)
char*
nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const
{
PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,%s[0x%x]",
PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
IsBlock() ? "block" : "inline",
IsDirty() ? "dirty" : "clean",
IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
IsImpactedByFloat() ? "impacted" : "not impacted",
IsLineWrapped() ? "wrapped" : "not wrapped",
BreakTypeToString(GetBreakType()),
BreakTypeToString(GetBreakTypeBefore()),
BreakTypeToString(GetBreakTypeAfter()),
mAllFlags);
return aBuf;
}
@ -604,7 +605,7 @@ nsLineIterator::GetLine(PRInt32 aLineNumber,
flags |= NS_LINE_FLAG_IS_BLOCK;
}
else {
if (line->HasBreak())
if (line->HasBreakAfter())
flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
}
*aLineFlags = flags;

View File

@ -246,6 +246,17 @@ public:
return mFlags.mPreviousMarginDirty;
}
// mHasClearance bit
void SetHasClearance() {
mFlags.mHasClearance = 1;
}
void ClearHasClearance() {
mFlags.mHasClearance = 0;
}
PRBool HasClearance() const {
return mFlags.mHasClearance;
}
// mImpactedByFloat bit
void SetLineIsImpactedByFloat(PRBool aValue) {
NS_ASSERTION((PR_FALSE==aValue || PR_TRUE==aValue), "somebody is playing fast and loose with bools and bits!");
@ -300,20 +311,37 @@ public:
}
// mBreakType value
PRBool HasBreak() const {
return NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
// Break information is applied *before* the line if the line is a block,
// or *after* the line if the line is an inline. Confusing, I know, but
// using different names should help.
PRBool HasBreakBefore() const {
return IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
}
PRBool HasFloatBreak() const {
return NS_STYLE_CLEAR_LEFT == mFlags.mBreakType ||
NS_STYLE_CLEAR_RIGHT == mFlags.mBreakType ||
NS_STYLE_CLEAR_LEFT_AND_RIGHT == mFlags.mBreakType;
}
void SetBreakType(PRUint8 aBreakType) {
NS_WARN_IF_FALSE(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
void SetBreakTypeBefore(PRUint8 aBreakType) {
NS_ASSERTION(IsBlock(), "Only blocks have break-before");
NS_ASSERTION(aBreakType <= NS_STYLE_CLEAR_LEFT_AND_RIGHT,
"Only float break types are allowed before a line");
mFlags.mBreakType = aBreakType;
}
PRUint8 GetBreakType() const {
return mFlags.mBreakType;
PRUint8 GetBreakTypeBefore() const {
return IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
}
PRBool HasBreakAfter() const {
return !IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
}
void SetBreakTypeAfter(PRUint8 aBreakType) {
NS_ASSERTION(!IsBlock(), "Only inlines have break-after");
NS_ASSERTION(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
mFlags.mBreakType = aBreakType;
}
PRBool HasFloatBreakAfter() const {
return !IsBlock() && (NS_STYLE_CLEAR_LEFT == mFlags.mBreakType ||
NS_STYLE_CLEAR_RIGHT == mFlags.mBreakType ||
NS_STYLE_CLEAR_LEFT_AND_RIGHT == mFlags.mBreakType);
}
PRUint8 GetBreakTypeAfter() const {
return !IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
}
// mCarriedOutBottomMargin value
@ -427,6 +455,7 @@ public:
struct FlagBits {
PRUint32 mDirty : 1;
PRUint32 mPreviousMarginDirty : 1;
PRUint32 mHasClearance : 1;
PRUint32 mBlock : 1;
PRUint32 mImpactedByFloat : 1;
PRUint32 mHasPercentageChild : 1;
@ -436,7 +465,7 @@ public:
PRUint32 mEmptyCacheState: 1;
PRUint32 mBreakType : 4;
PRUint32 mChildCount : 19;
PRUint32 mChildCount : 18;
};
struct ExtraData {

View File

@ -1080,6 +1080,22 @@ nsSpaceManager::PopState()
}
}
void
nsSpaceManager::DiscardState()
{
NS_ASSERTION(mSavedStates, "Invalid call to DiscardState()!");
if (!mSavedStates) {
return;
}
SpaceManagerState *state = mSavedStates;
mSavedStates = mSavedStates->mNext;
if(state != &mAutoState) {
delete state;
}
}
nscoord
nsSpaceManager::GetLowestRegionTop()
{

View File

@ -311,6 +311,12 @@ public:
*/
void PopState();
/**
* Pops the state off the stack without restoring it. Useful for speculative
* reflow where we're not sure if we're going to keep the result.
*/
void DiscardState();
/**
* Get the top of the last region placed into the space manager, to
* enforce the rule that a float can't be above an earlier float.

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,11 @@ class nsIntervalSet;
#define NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS 0x04000000
#define NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS 0x08000000
// Set on any block that has descendant frames in the normal
// flow with 'clear' set to something other than 'none'
// (including <BR CLEAR="..."> frames)
#define NS_BLOCK_HAS_CLEAR_CHILDREN 0x10000000
#define nsBlockFrameSuper nsHTMLContainerFrame
#define NS_BLOCK_FRAME_CID \
@ -224,6 +229,11 @@ public:
virtual void DeleteNextInFlowChild(nsPresContext* aPresContext,
nsIFrame* aNextInFlow);
// Determines whether the collapsed margin carried out of the last
// line includes the margin-top of a line with clearance (in which
// case we must avoid collapsing that margin with our bottom margin)
PRBool CheckForCollapsedBottomMarginFromClearanceLine();
/** return the topmost block child based on y-index.
* almost always the first or second line, if there is one.
* accounts for lines that hold only compressed white space, etc.

View File

@ -52,6 +52,7 @@
#include "nsIDOMHTMLBodyElement.h"
#include "nsLayoutAtoms.h"
#include "nsCOMPtr.h"
#include "nsLayoutUtils.h"
#ifdef NS_DEBUG
#undef NOISY_MAX_ELEMENT_SIZE
@ -79,13 +80,12 @@ nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH;
}
void
nsBlockReflowContext::ComputeCollapsedTopMargin(nsPresContext* aPresContext,
nsHTMLReflowState& aRS,
/* inout */ nsCollapsingMargin& aMargin)
PRBool
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry)
{
// Get aFrame's top margin
aMargin.Include(aRS.mComputedMargin.top);
// Include frame's top margin
aMargin->Include(aRS.mComputedMargin.top);
// The inclusion of the bottom margin when empty is done by the caller
// since it doesn't need to be done by the top-level (non-recursive)
@ -93,48 +93,77 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(nsPresContext* aPresContext,
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame);
printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin.get());
printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get());
#endif
// Calculate aFrame's generational top-margin from its child
// blocks. Note that if aFrame has a non-zero top-border or
PRBool dirtiedLine = PR_FALSE;
// Calculate the frame's generational top-margin from its child
// blocks. Note that if the frame has a non-zero top-border or
// top-padding then this step is skipped because it will be a margin
// root. It is also skipped if the frame is a margin root for other
// reasons.
void* bf;
if (0 == aRS.mComputedBorderPadding.top &&
!(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID,
NS_REINTERPRET_CAST(void**, &bf)))) {
for (nsBlockFrame::line_iterator line = bf->begin_lines(),
line_end = bf->end_lines();
line != line_end; ++line) {
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
// Here is where we recur. Now that we have determined that a
// generational collapse is required we need to compute the
// child blocks margin and so in so that we can look into
// it. For its margins to be computed we need to have a reflow
// state for it. Since the reflow reason is irrelevant, we'll
// arbitrarily make it a `resize' to avoid the path-plucking
// behavior if we're in an incremental reflow.
nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
nsHTMLReflowState reflowState(aPresContext, aRS, line->mFirstChild,
availSpace, eReflowReason_Resize);
ComputeCollapsedTopMargin(aPresContext, reflowState, aMargin);
if (isEmpty)
aMargin.Include(reflowState.mComputedMargin.bottom);
}
if (!isEmpty)
break;
!(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) &&
NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, &bf))) {
nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, aRS.frame);
for (nsBlockFrame::line_iterator line = block->begin_lines(),
line_end = block->end_lines();
line != line_end; ++line) {
if (!aClearanceFrame && line->HasClearance()) {
// If we don't have a clearance frame, then we're computing
// the collapsed margin in the first pass, assuming that all
// lines have no clearance. So clear their clearance flags.
line->ClearHasClearance();
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
nsBlockFrame* kidBlock = NS_STATIC_CAST(nsBlockFrame*, line->mFirstChild);
if (kidBlock == aClearanceFrame) {
line->SetHasClearance();
line->MarkDirty();
dirtiedLine = PR_TRUE;
break;
}
// Here is where we recur. Now that we have determined that a
// generational collapse is required we need to compute the
// child blocks margin and so in so that we can look into
// it. For its margins to be computed we need to have a reflow
// state for it. Since the reflow reason is irrelevant, we'll
// arbitrarily make it a `resize' to avoid the path-plucking
// behavior if we're in an incremental reflow.
nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
nsHTMLReflowState reflowState(kidBlock->GetPresContext(),
aRS, kidBlock,
availSpace, eReflowReason_Resize);
// Record that we're being optimistic by assuming the kid
// has no clearance
if (kidBlock->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
*aMayNeedRetry = PR_TRUE;
}
if (ComputeCollapsedTopMargin(reflowState, aMargin, aClearanceFrame, aMayNeedRetry)) {
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
if (isEmpty)
aMargin->Include(reflowState.mComputedMargin.bottom);
}
if (!isEmpty)
break;
}
}
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame);
printf(": => %d\n", aMargin.get());
#endif
return dirtiedLine;
}
struct nsBlockHorizontalAlign {
@ -302,7 +331,8 @@ nsPointDtor(void *aFrame, nsIAtom *aPropertyName,
nsresult
nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
PRBool aApplyTopMargin,
nsCollapsingMargin& aPrevBottomMargin,
nsCollapsingMargin& aPrevMargin,
nscoord aClearance,
PRBool aIsAdjacentWithTop,
nsMargin& aComputedOffsets,
nsHTMLReflowState& aFrameRS,
@ -354,32 +384,8 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
aFrameRS.reason = eReflowReason_Dirty;
}
/* We build a different reflow context based on the width attribute of the block,
* if it's a float.
* Auto-width floats need to have their containing-block size set explicitly,
* factoring in other floats that impact it.
* It's possible this should be quirks-only.
* All other blocks proceed normally.
*/
// XXXldb We should really fix this in nsHTMLReflowState::InitConstraints instead.
const nsStylePosition* position = mFrame->GetStylePosition();
nsStyleUnit widthUnit = position->mWidth.GetUnit();
const nsStyleDisplay* display = mFrame->GetStyleDisplay();
if ((eStyleUnit_Auto == widthUnit) &&
((NS_STYLE_FLOAT_LEFT == display->mFloats) ||
(NS_STYLE_FLOAT_RIGHT == display->mFloats))) {
// Initialize the reflow state and constrain the containing block's
// width and height to the available width and height.
aFrameRS.Init(mPresContext, mSpace.width, mSpace.height);
}
else {
// Initialize the reflow state and use the containing block's computed
// width and height (or derive appropriate values for an
// absolutely positioned frame).
aFrameRS.Init(mPresContext);
}
aComputedOffsets = aFrameRS.mComputedOffsets;
if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
nsPropertyTable *propTable = mPresContext->PropertyTable();
@ -404,23 +410,20 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
mComputedWidth = aFrameRS.mComputedWidth;
if (aApplyTopMargin) {
// Compute the childs collapsed top margin (its margin collpased
// with its first childs top-margin -- recursively).
ComputeCollapsedTopMargin(mPresContext, aFrameRS, aPrevBottomMargin);
mTopMargin = aPrevMargin;
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, mOuterReflowState.frame);
printf(": reflowing ");
nsFrame::ListTag(stdout, mFrame);
printf(" margin => %d\n", aPrevBottomMargin.get());
printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
#endif
// Adjust the available height if its constrained so that the
// child frame doesn't think it can reflow into its margin area.
if (NS_UNCONSTRAINEDSIZE != aFrameRS.availableHeight) {
aFrameRS.availableHeight -= aPrevBottomMargin.get();
aFrameRS.availableHeight -= mTopMargin.get() + aClearance;
}
mTopMargin = aPrevBottomMargin;
}
// Compute x/y coordinate where reflow will begin. Use the rules
@ -431,7 +434,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
mStyleMargin = aFrameRS.mStyleMargin;
mStylePadding = aFrameRS.mStylePadding;
nscoord x;
nscoord y = mSpace.y + mTopMargin.get();
nscoord y = mSpace.y + mTopMargin.get() + aClearance;
// If it's a right floated element, then calculate the x-offset
// differently
@ -647,6 +650,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
PRBool
nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool aForceFit,
nsLineBox* aLine,
const nsMargin& aComputedOffsets,
nsCollapsingMargin& aBottomMarginResult,
nsRect& aInFlowBounds,
@ -660,15 +664,13 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool fits = PR_TRUE;
nscoord x = mX;
nscoord y = mY;
// When deciding whether it's empty we also need to take into
// account the overflow area
// XXXldb What should really matter is whether there exist non-
// empty frames in the block (with appropriate whitespace munging).
// Consider the case where we clip off the overflow with
// 'overflow: -moz-hidden-unscrollable' (which doesn't currently
// affect mOverflowArea, but probably should.
if ((0 == mMetrics.height) && (0 == mMetrics.mOverflowArea.height))
// Check whether the block's bottom margin collapses with its top
// margin. See CSS 2.1 section 8.3.1; those rules seem to match
// nsBlockFrame::IsEmpty(). Any such block must have zero height so
// check that first.
if (0 == mMetrics.height && !aLine->HasClearance() &&
aLine->CachedIsEmpty())
{
// Collapse the bottom margin with the top margin that was already
// applied.

View File

@ -39,10 +39,12 @@
#define nsBlockReflowContext_h___
#include "nsIFrame.h"
#include "nsHTMLReflowState.h"
#include "nsHTMLReflowMetrics.h"
class nsBlockFrame;
class nsBlockReflowState;
class nsHTMLReflowState;
class nsLineBox;
class nsIFrame;
class nsPresContext;
class nsLineLayout;
@ -62,7 +64,8 @@ public:
nsresult ReflowBlock(const nsRect& aSpace,
PRBool aApplyTopMargin,
nsCollapsingMargin& aPrevBottomMargin,
nsCollapsingMargin& aPrevMargin,
nscoord aClearance,
PRBool aIsAdjacentWithTop,
nsMargin& aComputedOffsets,
nsHTMLReflowState& aReflowState,
@ -70,6 +73,7 @@ public:
PRBool PlaceBlock(const nsHTMLReflowState& aReflowState,
PRBool aForceFit,
nsLineBox* aLine,
const nsMargin& aComputedOffsets,
nsCollapsingMargin& aBottomMarginResult /* out */,
nsRect& aInFlowBounds,
@ -101,9 +105,23 @@ public:
return mMetrics.mMaximumWidth;
}
static void ComputeCollapsedTopMargin(nsPresContext* aPresContext,
nsHTMLReflowState& aRS,
/* inout */ nsCollapsingMargin& aMargin);
/**
* Computes the collapsed top margin for a block whose reflow state is in aRS.
* The computed margin is added into aMargin.
* If aClearanceFrame is null then this is the first optimistic pass which shall assume
* that no frames have clearance, and we clear the HasClearance on all frames encountered.
* If non-null, this is the second pass and
* the caller has decided aClearanceFrame needs clearance (and we will
* therefore stop collapsing there); also, this function is responsible for marking
* it with SetHasClearance.
* If in the optimistic pass any frame is encountered that might possibly need
* clearance (i.e., if we really needed the optimism assumption) then we set aMayNeedRetry
* to true.
* We return PR_TRUE if we changed the clearance state of any line and marked it dirty.
*/
static PRBool ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
PRBool* aMayNeedRetry);
protected:
nsPresContext* mPresContext;

View File

@ -60,7 +60,8 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsPresContext* aPresContext,
nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics,
PRBool aBlockMarginRoot)
PRBool aTopMarginRoot,
PRBool aBottomMarginRoot)
: mBlock(aFrame),
mPresContext(aPresContext),
mReflowState(aReflowState),
@ -71,14 +72,10 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
{
const nsMargin& borderPadding = BorderPadding();
if (aBlockMarginRoot) {
SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE);
SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE);
}
if (0 != aReflowState.mComputedBorderPadding.top) {
if (aTopMarginRoot || 0 != aReflowState.mComputedBorderPadding.top) {
SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE);
}
if (0 != aReflowState.mComputedBorderPadding.bottom) {
if (aBottomMarginRoot || 0 != aReflowState.mComputedBorderPadding.bottom) {
SetFlag(BRS_ISBOTTOMMARGINROOT, PR_TRUE);
}
if (GetFlag(BRS_ISTOPMARGINROOT)) {
@ -338,68 +335,6 @@ nsBlockReflowState::GetAvailableSpace(nscoord aY)
#endif
}
PRBool
nsBlockReflowState::ClearPastFloats(PRUint8 aBreakType)
{
nscoord saveY, deltaY;
PRBool applyTopMargin = PR_FALSE;
switch (aBreakType) {
case NS_STYLE_CLEAR_LEFT:
case NS_STYLE_CLEAR_RIGHT:
case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
// Apply the previous margin before clearing
saveY = mY + mPrevBottomMargin.get();
ClearFloats(saveY, aBreakType);
#ifdef NOISY_FLOAT_CLEARING
nsFrame::ListTag(stdout, mBlock);
printf(": ClearPastFloats: mPrevBottomMargin=%d saveY=%d oldY=%d newY=%d deltaY=%d\n",
mPrevBottomMargin.get(), saveY, saveY - mPrevBottomMargin.get(), mY,
mY - saveY);
#endif
// Determine how far we just moved. If we didn't move then there
// was nothing to clear to don't mess with the normal margin
// collapsing behavior. In either case we need to restore the Y
// coordinate to what it was before the clear.
deltaY = mY - saveY;
if (0 != deltaY) {
// Pretend that the distance we just moved is a previous
// blocks bottom margin. Note that GetAvailableSpace has been
// done so that the available space calculations will be done
// after clearing the appropriate floats.
//
// What we are doing here is applying CSS2 section 9.5.2's
// rules for clearing - "The top margin of the generated box
// is increased enough that the top border edge is below the
// bottom outer edge of the floating boxes..."
//
// What this will do is cause the top-margin of the block
// frame we are about to reflow to be collapsed with that
// distance.
// XXXldb This doesn't handle collapsing with negative margins
// correctly, although it's arguable what "correct" is.
// XXX Are all the other margins included by this point?
mPrevBottomMargin.Zero();
mPrevBottomMargin.Include(deltaY);
mY = saveY;
// Force margin to be applied in this circumstance
applyTopMargin = PR_TRUE;
}
else {
// Put mY back to its original value since no clearing
// happened. That way the previous blocks bottom margin will
// be applied properly.
mY = saveY - mPrevBottomMargin.get();
}
break;
}
return applyTopMargin;
}
/*
* Reconstruct the vertical margin before the line |aLine| in order to
* do an incremental reflow that begins with |aLine| without reflowing
@ -851,12 +786,10 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache,
// See if the float should clear any preceeding floats...
if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) {
// XXXldb Does this handle vertical margins correctly?
ClearFloats(mY, floatDisplay->mBreakType);
mY = ClearFloats(mY, floatDisplay->mBreakType);
}
else {
// Get the band of available space
GetAvailableSpace();
}
NS_ASSERTION(floatFrame->GetParent() == mBlock,
"Float frame has wrong parent");
@ -1154,14 +1087,14 @@ nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheList& aList)
return PR_TRUE;
}
void
nscoord
nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType)
{
#ifdef DEBUG
if (nsBlockFrame::gNoisyReflow) {
nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
printf("clear floats: in: mY=%d aY=%d(%d)\n",
mY, aY, aY - BorderPadding().top);
printf("clear floats: in: aY=%d(%d)\n",
aY, aY - BorderPadding().top);
}
#endif
@ -1173,14 +1106,15 @@ nsBlockReflowState::ClearFloats(nscoord aY, PRUint8 aBreakType)
const nsMargin& bp = BorderPadding();
nscoord newY = mSpaceManager->ClearFloats(aY - bp.top, aBreakType);
mY = newY + bp.top;
GetAvailableSpace();
newY += bp.top;
#ifdef DEBUG
if (nsBlockFrame::gNoisyReflow) {
nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
printf("clear floats: out: mY=%d(%d)\n", mY, mY - bp.top);
printf("clear floats: out: y=%d(%d)\n", newY, newY - bp.top);
}
#endif
return newY;
}

View File

@ -53,7 +53,7 @@ public:
nsPresContext* aPresContext,
nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics,
PRBool aBlockMarginRoot);
PRBool aTopMarginRoot, PRBool aBottomMarginRoot);
~nsBlockReflowState();
@ -85,12 +85,9 @@ public:
PRBool PlaceBelowCurrentLineFloats(nsFloatCacheList& aFloats);
// called when clearing a line with a break type caused by a BR past
// floats, and also used internally by ClearPastFloats
void ClearFloats(nscoord aY, PRUint8 aBreakType);
// called when clearing a block past floats
PRBool ClearPastFloats(PRUint8 aBreakType);
// Returns the first coordinate >= aY that clears the
// indicated floats.
nscoord ClearFloats(nscoord aY, PRUint8 aBreakType);
PRBool IsAdjacentWithTop() const {
return mY == mReflowState.mComputedBorderPadding.top;

View File

@ -111,6 +111,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mNextInFlowUntouched = PR_FALSE;
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -143,6 +145,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mNextInFlowUntouched = PR_FALSE;
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -193,6 +197,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -240,6 +246,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -287,6 +295,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mFlags.mHasClearance = PR_FALSE;
mDiscoveredClearance = nsnull;
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;

View File

@ -181,13 +181,14 @@ BreakTypeToString(PRUint8 aBreakType)
char*
nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const
{
PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,%s[0x%x]",
PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
IsBlock() ? "block" : "inline",
IsDirty() ? "dirty" : "clean",
IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
IsImpactedByFloat() ? "impacted" : "not impacted",
IsLineWrapped() ? "wrapped" : "not wrapped",
BreakTypeToString(GetBreakType()),
BreakTypeToString(GetBreakTypeBefore()),
BreakTypeToString(GetBreakTypeAfter()),
mAllFlags);
return aBuf;
}
@ -604,7 +605,7 @@ nsLineIterator::GetLine(PRInt32 aLineNumber,
flags |= NS_LINE_FLAG_IS_BLOCK;
}
else {
if (line->HasBreak())
if (line->HasBreakAfter())
flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
}
*aLineFlags = flags;

View File

@ -246,6 +246,17 @@ public:
return mFlags.mPreviousMarginDirty;
}
// mHasClearance bit
void SetHasClearance() {
mFlags.mHasClearance = 1;
}
void ClearHasClearance() {
mFlags.mHasClearance = 0;
}
PRBool HasClearance() const {
return mFlags.mHasClearance;
}
// mImpactedByFloat bit
void SetLineIsImpactedByFloat(PRBool aValue) {
NS_ASSERTION((PR_FALSE==aValue || PR_TRUE==aValue), "somebody is playing fast and loose with bools and bits!");
@ -300,20 +311,37 @@ public:
}
// mBreakType value
PRBool HasBreak() const {
return NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
// Break information is applied *before* the line if the line is a block,
// or *after* the line if the line is an inline. Confusing, I know, but
// using different names should help.
PRBool HasBreakBefore() const {
return IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
}
PRBool HasFloatBreak() const {
return NS_STYLE_CLEAR_LEFT == mFlags.mBreakType ||
NS_STYLE_CLEAR_RIGHT == mFlags.mBreakType ||
NS_STYLE_CLEAR_LEFT_AND_RIGHT == mFlags.mBreakType;
}
void SetBreakType(PRUint8 aBreakType) {
NS_WARN_IF_FALSE(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
void SetBreakTypeBefore(PRUint8 aBreakType) {
NS_ASSERTION(IsBlock(), "Only blocks have break-before");
NS_ASSERTION(aBreakType <= NS_STYLE_CLEAR_LEFT_AND_RIGHT,
"Only float break types are allowed before a line");
mFlags.mBreakType = aBreakType;
}
PRUint8 GetBreakType() const {
return mFlags.mBreakType;
PRUint8 GetBreakTypeBefore() const {
return IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
}
PRBool HasBreakAfter() const {
return !IsBlock() && NS_STYLE_CLEAR_NONE != mFlags.mBreakType;
}
void SetBreakTypeAfter(PRUint8 aBreakType) {
NS_ASSERTION(!IsBlock(), "Only inlines have break-after");
NS_ASSERTION(aBreakType <= LINE_MAX_BREAK_TYPE, "bad break type");
mFlags.mBreakType = aBreakType;
}
PRBool HasFloatBreakAfter() const {
return !IsBlock() && (NS_STYLE_CLEAR_LEFT == mFlags.mBreakType ||
NS_STYLE_CLEAR_RIGHT == mFlags.mBreakType ||
NS_STYLE_CLEAR_LEFT_AND_RIGHT == mFlags.mBreakType);
}
PRUint8 GetBreakTypeAfter() const {
return !IsBlock() ? mFlags.mBreakType : NS_STYLE_CLEAR_NONE;
}
// mCarriedOutBottomMargin value
@ -427,6 +455,7 @@ public:
struct FlagBits {
PRUint32 mDirty : 1;
PRUint32 mPreviousMarginDirty : 1;
PRUint32 mHasClearance : 1;
PRUint32 mBlock : 1;
PRUint32 mImpactedByFloat : 1;
PRUint32 mHasPercentageChild : 1;
@ -436,7 +465,7 @@ public:
PRUint32 mEmptyCacheState: 1;
PRUint32 mBreakType : 4;
PRUint32 mChildCount : 19;
PRUint32 mChildCount : 18;
};
struct ExtraData {