diff --git a/mozilla/layout/base/nsLayoutUtils.cpp b/mozilla/layout/base/nsLayoutUtils.cpp
index 51ee20b741d..239897fc072 100644
--- a/mozilla/layout/base/nsLayoutUtils.cpp
+++ b/mozilla/layout/base/nsLayoutUtils.cpp
@@ -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;
+}
diff --git a/mozilla/layout/base/nsLayoutUtils.h b/mozilla/layout/base/nsLayoutUtils.h
index 1641961e266..548f1a7a0e0 100644
--- a/mozilla/layout/base/nsLayoutUtils.h
+++ b/mozilla/layout/base/nsLayoutUtils.h
@@ -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__
diff --git a/mozilla/layout/base/public/nsHTMLReflowState.h b/mozilla/layout/base/public/nsHTMLReflowState.h
index 5cb0de47324..39d93fcda2d 100644
--- a/mozilla/layout/base/public/nsHTMLReflowState.h
+++ b/mozilla/layout/base/public/nsHTMLReflowState.h
@@ -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
diff --git a/mozilla/layout/base/public/nsLayoutUtils.h b/mozilla/layout/base/public/nsLayoutUtils.h
index 1641961e266..548f1a7a0e0 100644
--- a/mozilla/layout/base/public/nsLayoutUtils.h
+++ b/mozilla/layout/base/public/nsLayoutUtils.h
@@ -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__
diff --git a/mozilla/layout/base/src/nsLayoutUtils.cpp b/mozilla/layout/base/src/nsLayoutUtils.cpp
index 51ee20b741d..239897fc072 100644
--- a/mozilla/layout/base/src/nsLayoutUtils.cpp
+++ b/mozilla/layout/base/src/nsLayoutUtils.cpp
@@ -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;
+}
diff --git a/mozilla/layout/base/src/nsSpaceManager.cpp b/mozilla/layout/base/src/nsSpaceManager.cpp
index 6b79a1890b6..3e50acf7289 100644
--- a/mozilla/layout/base/src/nsSpaceManager.cpp
+++ b/mozilla/layout/base/src/nsSpaceManager.cpp
@@ -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()
{
diff --git a/mozilla/layout/base/src/nsSpaceManager.h b/mozilla/layout/base/src/nsSpaceManager.h
index e825ad9cd0d..f903308497c 100644
--- a/mozilla/layout/base/src/nsSpaceManager.h
+++ b/mozilla/layout/base/src/nsSpaceManager.h
@@ -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.
diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp
index 49eae095f6a..b7d327a7176 100644
--- a/mozilla/layout/generic/nsBlockFrame.cpp
+++ b/mozilla/layout/generic/nsBlockFrame.cpp
@@ -695,7 +695,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
- NS_BLOCK_MARGIN_ROOT & mState);
+ aReflowState.mFlags.mHasClearance || (NS_BLOCK_MARGIN_ROOT & mState),
+ (NS_BLOCK_MARGIN_ROOT & mState));
// The condition for doing Bidi resolutions includes a test for the
// dirtiness flags, because blocks sometimes send a resize reflow
@@ -1133,6 +1134,27 @@ IsPercentageAwareChild(const nsIFrame* aFrame)
return PR_FALSE;
}
+PRBool
+nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
+{
+ line_iterator begin = begin_lines();
+ line_iterator line = end_lines();
+
+ while (PR_TRUE) {
+ if (begin == line) {
+ return PR_FALSE;
+ }
+ --line;
+ if (line->mBounds.height != 0 || !line->CachedIsEmpty()) {
+ return PR_FALSE;
+ }
+ if (line->HasClearance()) {
+ return PR_TRUE;
+ }
+ }
+ // not reached
+}
+
void
nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
nsBlockReflowState& aState,
@@ -1260,7 +1282,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
nscoord oldDesiredWidth = aMetrics.width;
#endif
nsBlockReflowState state(reflowState, aState.mPresContext, this,
- aMetrics, NS_BLOCK_MARGIN_ROOT & mState);
+ aMetrics,
+ aReflowState.mFlags.mHasClearance || (NS_BLOCK_MARGIN_ROOT & mState),
+ (NS_BLOCK_MARGIN_ROOT & mState));
ReflowDirtyLines(state);
aState.mY = state.mY;
NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width");
@@ -1268,6 +1292,29 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
}
}
+ // Return bottom margin information
+ // rbs says he hit this assertion occasionally (see bug 86947), so
+ // just set the margin to zero and we'll figure out why later
+ //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
+ // "someone else set the margin");
+ nscoord nonCarriedOutVerticalMargin = 0;
+ if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
+ // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
+ // line with clearance and a non-zero top margin and all
+ // subsequent lines are empty, then we do not allow our childrens'
+ // carried out bottom margin to be carried out of us and collapse
+ // with our own bottom margin.
+ if (CheckForCollapsedBottomMarginFromClearanceLine()) {
+ // Convert the children's carried out margin to something that
+ // we will include in our height
+ nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+ }
+ aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
+ } else {
+ aMetrics.mCarriedOutBottomMargin.Zero();
+ }
+
// Compute final height
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) {
if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
@@ -1296,14 +1343,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
}
else {
// Use the current height; continuations will take up the rest.
- aMetrics.height = aState.mY;
+ aMetrics.height = aState.mY + nonCarriedOutVerticalMargin;
}
// Don't carry out a bottom margin when our height is fixed.
- aState.mPrevBottomMargin.Zero();
+ aMetrics.mCarriedOutBottomMargin.Zero();
}
else {
- nscoord autoHeight = aState.mY;
+ nscoord autoHeight = aState.mY + nonCarriedOutVerticalMargin;
// Shrink wrap our height around our contents.
if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
@@ -1357,16 +1404,6 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
#endif
}
- // Return bottom margin information
- // rbs says he hit this assertion occasionally (see bug 86947), so
- // just set the margin to zero and we'll figure out why later
- //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
- // "someone else set the margin");
- if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT))
- aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
- else
- aMetrics.mCarriedOutBottomMargin.Zero();
-
#ifdef DEBUG_blocks
if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
ListTag(stdout);
@@ -1537,7 +1574,7 @@ nsBlockFrame::RetargetInlineIncrementalReflow(nsReflowPath::iterator &aTarget,
// continuations will be preserved during an unconstrained reflow.
// XXXwaterson should this be `!= NS_STYLE_CLEAR_NONE'?
--aLine;
- if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE)
+ if (aLine->GetBreakTypeAfter() == NS_STYLE_CLEAR_LINE)
break;
*aTarget = aPrevInFlow;
@@ -1793,7 +1830,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
if (line->IsBlock() ||
line->HasPercentageChild() ||
line->HasFloats() ||
- (line != mLines.back() && !line->HasBreak()) ||
+ (line != mLines.back() && !line->HasBreakAfter()) ||
line->ResizeReflowOptimizationDisabled() ||
line->IsImpactedByFloat() ||
(line->mBounds.XMost() > newAvailWidth)) {
@@ -1809,14 +1846,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
#ifdef DEBUG
if (gNoisyReflow && !line->IsDirty()) {
IndentBy(stdout, gNoiseIndent + 1);
- printf("skipped: line=%p next=%p %s %s%s%s breakType=%d xmost=%d\n",
+ printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
NS_STATIC_CAST(void*, line.get()),
NS_STATIC_CAST(void*, (line.next() != end_lines() ? line.next().get() : nsnull)),
line->IsBlock() ? "block" : "inline",
- line->HasBreak() ? "has-break " : "",
+ line->HasBreakAfter() ? "has-break-after " : "",
line->HasFloats() ? "has-floats " : "",
line->IsImpactedByFloat() ? "impacted " : "",
- line->GetBreakType(),
+ line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
line->mBounds.XMost());
}
#endif
@@ -1970,10 +2007,15 @@ DirtyLineIfWrappedLinesAreDirty(const nsLineList::iterator& aLine,
}
static void PlaceFrameView(nsPresContext* aPresContext, nsIFrame* aFrame);
-static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent,
nsIFrame** aHead, nsIFrame** aTail);
+static PRBool LineHasClear(nsLineBox* aLine) {
+ return aLine->GetBreakTypeBefore() || aLine->HasFloatBreakAfter()
+ || (aLine->IsBlock() && (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN));
+}
+
+
static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent,
nsIFrame* aNewParent) {
aFrame->SetParent(aNewParent);
@@ -2026,6 +2068,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE;
PRBool repositionViews = PR_FALSE; // should we really need this?
+ PRBool foundAnyClears = PR_FALSE;
#ifdef DEBUG
if (gNoisyReflow) {
@@ -2066,8 +2109,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// reflow it or if its previous margin is dirty
PRBool needToRecoverState = PR_FALSE;
PRBool lastLineMovedUp = PR_FALSE;
- PRUint8 floatBreakType = NS_STYLE_CLEAR_NONE;
-
+ // We save up information about BR-clearance here
+ PRUint8 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
+
// Reflow the lines that are already ours
line_iterator line = begin_lines(), line_end = end_lines();
for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
@@ -2097,15 +2141,59 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
::DirtyLineIfWrappedLinesAreDirty(line, line_end);
}
+ // This really sucks, but we have to look inside any blocks that have clear
+ // elements inside them.
+ // XXX what can we do smarter here?
+ if (line->IsBlock() &&
+ (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
+ line->MarkDirty();
+ }
+
+ // We have to reflow the line if it's a block whose clearance
+ // might have changed, so detect that.
+ if (!line->IsDirty() && line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE) {
+ nscoord curY = aState.mY;
+ // See where we would be after applying any clearance due to
+ // BRs.
+ if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+ curY = aState.ClearFloats(curY, inlineFloatBreakType);
+ }
+
+ nscoord newY = aState.ClearFloats(curY, line->GetBreakTypeBefore());
+
+ if (line->HasClearance()) {
+ // Reflow the line if it might not have clearance anymore.
+ if (newY == curY
+ // aState.mY is the clearance point which should be the
+ // top border-edge of the block frame. If sliding the
+ // block by deltaY isn't going to put it in the predicted
+ // position, then we'd better reflow the line.
+ || newY != line->mBounds.y + deltaY) {
+ line->MarkDirty();
+ }
+ } else {
+ // Reflow the line if the line might have clearance now.
+ if (curY != newY) {
+ line->MarkDirty();
+ }
+ }
+ }
+
+ // We might have to reflow a line that is after a clearing BR.
+ if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+ aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
+ if (aState.mY != line->mBounds.y + deltaY) {
+ // SlideLine is not going to put the line where the clearance
+ // put it. Reflow the line to be sure.
+ line->MarkDirty();
+ }
+ inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
+ }
+
// Make sure |aState.mPrevBottomMargin| is at the correct position
// before calling PropagateFloatDamage.
if (needToRecoverState &&
(line->IsDirty() || line->IsPreviousMarginDirty())) {
- if (floatBreakType != NS_STYLE_CLEAR_NONE) {
- aState.ClearFloats(aState.mY, floatBreakType);
- floatBreakType = NS_STYLE_CLEAR_NONE;
- }
-
// We need to reconstruct the bottom margin only if we didn't
// reflow the previous line and we do need to reflow (or repair
// the top position of) the next line.
@@ -2218,25 +2306,29 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.RecoverStateFrom(line, deltaY);
// Keep mY up to date in case we're propagating reflow damage
- // and also because our final height may depend on it. Only
- // update mY if the line is not empty, because that's what
- // PlaceLine does.
- if (!line->CachedIsEmpty()) {
+ // and also because our final height may depend on it. If the
+ // line is inlines, then only update mY if the line is not
+ // empty, because that's what PlaceLine does. (Empty blocks may
+ // want to update mY, e.g. if they have clearance.)
+ if (line->IsBlock() || !line->CachedIsEmpty()) {
aState.mY = line->mBounds.YMost();
- // This will include any pending float clearing height, so
- // don't bother clearing previous lines' floats
- floatBreakType = NS_STYLE_CLEAR_NONE;
}
// Record if we need to clear floats before reflowing the next
- // line
- if (line->HasFloatBreak()) {
- floatBreakType = ::CombineBreakType(floatBreakType, line->GetBreakType());
+ // line. Note that inlineFloatBreakType will be handled and
+ // cleared before the next line is processed, so there is no
+ // need to combine break types here.
+ if (line->HasFloatBreakAfter()) {
+ inlineFloatBreakType = line->GetBreakTypeAfter();
}
needToRecoverState = PR_TRUE;
}
+ if (LineHasClear(line.get())) {
+ foundAnyClears = PR_TRUE;
+ }
+
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@@ -2399,6 +2491,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
break;
}
+ if (LineHasClear(line.get())) {
+ foundAnyClears = PR_TRUE;
+ }
+
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
@@ -2420,6 +2516,12 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.mY += metrics.height;
}
+ if (foundAnyClears) {
+ AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ } else {
+ RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ }
+
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@@ -3101,38 +3203,6 @@ nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState,
}
}
-// Combine aNewBreakType with aOrigBreakType, but limit the break types
-// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT. When there is a
right
-// after a float and the float splits, then the
's break type is combined
-// with the break type of the frame right after the floats next-in-flow.
-static PRUint8
-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;
-}
-
nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
line_iterator aLine,
@@ -3154,320 +3224,434 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH),
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
- // See if we should apply the top margin. If the block frame being
- // reflowed is a continuation (non-null prev-in-flow) then we don't
- // apply its top margin because its not significant. Otherwise, dig
- // deeper.
- PRBool applyTopMargin = PR_FALSE;
- nsIFrame* framePrevInFlow = frame->GetPrevInFlow();
- if (nsnull == framePrevInFlow) {
- applyTopMargin = ShouldApplyTopMargin(aState, aLine);
- }
-
PRUint8 breakType = display->mBreakType;
// If a float split and its prev-in-flow was followed by a
, then combine
// the
's break type with the block's break type (the block will be the very
// next frame after the split float).
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
- breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
}
+
// Clear past floats before the block if the clear style is not none
- aLine->SetBreakType(breakType);
- if (NS_STYLE_CLEAR_NONE != breakType) {
- PRBool alsoApplyTopMargin = aState.ClearPastFloats(breakType);
- if (alsoApplyTopMargin) {
- applyTopMargin = PR_TRUE;
- }
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": y=%d child ", aState.mY);
- ListTag(stdout, frame);
- printf(" has clear of %d => %s, mPrevBottomMargin=%d\n",
- breakType,
- applyTopMargin ? "applyTopMargin" : "nope",
- aState.mPrevBottomMargin);
-#endif
- }
+ aLine->SetBreakTypeBefore(breakType);
+
+ // See if we should apply the top margin. If the block frame being
+ // reflowed is a continuation (non-null prev-in-flow) then we don't
+ // apply its top margin because its not significant. Otherwise, dig
+ // deeper.
+ PRBool applyTopMargin =
+ !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
- nscoord topMargin = 0;
if (applyTopMargin) {
- // Precompute the blocks top margin value so that we can get the
- // correct available space (there might be a float that's
- // already been placed below the aState.mPrevBottomMargin
-
- // Setup a reflowState to get the style computed margin-top
- // value. We'll use a reason of `resize' so that we don't fudge
- // any incremental reflow state.
-
- // The availSpace here is irrelevant to our needs - all we want
- // out if this setup is the margin-top value which doesn't depend
- // on the childs available space.
- nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
- nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
- frame, availSpace, eReflowReason_Resize);
-
- // Now compute the collapsed margin-top value into aState.mPrevBottomMargin
- nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin;
- nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext,
- reflowState,
- aState.mPrevBottomMargin);
- topMargin = aState.mPrevBottomMargin.get();
- aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed
-
- // Temporarily advance the running Y value so that the
- // GetAvailableSpace method will return the right available
- // space. This undone as soon as the margin is computed.
- aState.mY += topMargin;
+ // The HasClearance setting is only valid if ShouldApplyTopMargin
+ // returned PR_FALSE (in which case the top-margin-root set our
+ // clearance flag). Otherwise clear it now. We'll set it later on
+ // ourselves if necessary.
+ aLine->ClearHasClearance();
+ }
+ PRBool treatWithClearance = aLine->HasClearance();
+ // If our top margin was counted as part of some parents top-margin
+ // collapse and we are being speculatively reflowed assuming this
+ // frame DID NOT need clearance, then we need to check that
+ // assumption.
+ if (!treatWithClearance && !applyTopMargin && breakType != NS_STYLE_CLEAR_NONE &&
+ aState.mReflowState.mDiscoveredClearance) {
+ nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
+ nscoord clearY = aState.ClearFloats(curY, breakType);
+ if (clearY != curY) {
+ // Looks like that assumption was invalid, we do need
+ // clearance. Tell our ancestor so it can reflow again. It is
+ // responsible for actually setting our clearance flag before
+ // the next reflow.
+ treatWithClearance = PR_TRUE;
+ // Only record the first frame that requires clearance
+ if (!*aState.mReflowState.mDiscoveredClearance) {
+ *aState.mReflowState.mDiscoveredClearance = frame;
+ }
+ // Exactly what we do now is flexible since we'll definitely be
+ // reflowed.
+ }
+ }
+ if (treatWithClearance) {
+ applyTopMargin = PR_TRUE;
}
- // Compute the available space for the block
- aState.GetAvailableSpace();
-#ifdef REALLY_NOISY_REFLOW
- printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false");
-#endif
- PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
- aLine->SetLineIsImpactedByFloat(isImpacted);
- nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
- frame->IsSplittable(splitType);
- nsRect availSpace;
- aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
+ nsIFrame* clearanceFrame = nsnull;
+ nscoord startingY = aState.mY;
+ nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
+ while (PR_TRUE) {
+ nscoord clearance = 0;
+ nscoord topMargin = 0;
+ PRBool mayNeedRetry = PR_FALSE;
+ if (applyTopMargin) {
+ // Precompute the blocks top margin value so that we can get the
+ // correct available space (there might be a float that's
+ // already been placed below the aState.mPrevBottomMargin
- // Now put the Y coordinate back and flow the block letting the
- // block reflow context compute the same top margin value we just
- // computed (sigh).
- if (topMargin) {
+ // Setup a reflowState to get the style computed margin-top
+ // value. We'll use a reason of `resize' so that we don't fudge
+ // any incremental reflow state.
+
+ // The availSpace here is irrelevant to our needs - all we want
+ // out if this setup is the margin-top value which doesn't depend
+ // on the childs available space.
+ // XXX building a complete nsHTMLReflowState just to get the margin-top
+ // seems like a waste. And we do this for almost every block!
+ nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
+ nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
+ frame, availSpace, eReflowReason_Resize);
+
+ if (treatWithClearance) {
+ aState.mY += aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+ }
+
+ // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
+ // that all child margins collapse down to clearanceFrame.
+ nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
+ &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
+
+ // XXX optimization; we could check the collapsing children to see if they are sure
+ // to require clearance, and so avoid retrying them
+
+ if (clearanceFrame) {
+ // Don't allow retries on the second pass. The clearance decisions for the
+ // blocks whose top-margins collapse with ours are now fixed.
+ mayNeedRetry = PR_FALSE;
+ }
+
+ if (!treatWithClearance && !clearanceFrame && breakType != NS_STYLE_CLEAR_NONE) {
+ // We don't know if we need clearance and this is the first,
+ // optimistic pass. So determine whether *this block* needs
+ // clearance. Note that we do not allow the decision for whether
+ // this block has clearance to change on the second pass; that
+ // decision is only allowed to be made under the optimistic
+ // first pass.
+ nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
+ nscoord clearY = aState.ClearFloats(curY, breakType);
+ if (clearY != curY) {
+ // Looks like we need clearance and we didn't know about it already. So
+ // recompute collapsed margin
+ treatWithClearance = PR_TRUE;
+ // Remember this decision, needed for incremental reflow
+ aLine->SetHasClearance();
+
+ // Apply incoming margins
+ aState.mY += aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+
+ // Compute the collapsed margin again, ignoring the incoming margin this time
+ mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
+ &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
+ }
+ }
+
+ // Temporarily advance the running Y value so that the
+ // GetAvailableSpace method will return the right available
+ // space. This undone as soon as the horizontal margins are
+ // computed.
+ topMargin = aState.mPrevBottomMargin.get();
+
+ if (treatWithClearance) {
+ nscoord currentY = aState.mY;
+ // advance mY to the clear position.
+ aState.mY = aState.ClearFloats(aState.mY, breakType);
+
+ // Compute clearance. It's the amount we need to add to the top
+ // border-edge of the frame, after applying collapsed margins
+ // from the frame and its children, to get it to line up with
+ // the bottom of the floats. The former is currentY + topMargin,
+ // the latter is the current aState.mY.
+ // Note that negative clearance is possible
+ clearance = aState.mY - (currentY + topMargin);
+
+ // Add clearance to our top margin while we compute available
+ // space for the frame
+ topMargin += clearance;
+
+ // Note that aState.mY should stay where it is: at the top
+ // border-edge of the frame
+ } else {
+ // Advance aState.mY to the top border-edge of the frame.
+ aState.mY += topMargin;
+ }
+ }
+
+ // Here aState.mY is the top border-edge of the block.
+ // Compute the available space for the block
+ aState.GetAvailableSpace();
+#ifdef REALLY_NOISY_REFLOW
+ printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false");
+#endif
+ PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
+ aLine->SetLineIsImpactedByFloat(isImpacted);
+ nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
+ frame->IsSplittable(splitType);
+ nsRect availSpace;
+ aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
+
+ // Now put the Y coordinate back to the top of the top-margin +
+ // clearance, and flow the block.
aState.mY -= topMargin;
availSpace.y -= topMargin;
if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
availSpace.height += topMargin;
}
- }
-
- // keep track of the last overflow float in case we need to undo any new additions
- nsFrameList* overflowPlace = GetOverflowPlaceholders();
- nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
-
- // Reflow the block into the available space
- nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
- nsMargin computedOffsets;
- // construct the html reflow state for the block. ReflowBlock
- // will initialize it and set its reason.
- nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
- nsSize(availSpace.width, availSpace.height),
- aState.mReflowState.reason, PR_FALSE);
- rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
- aState.IsAdjacentWithTop(), computedOffsets,
- blockHtmlRS, frameReflowStatus);
-
+
+ // keep track of the last overflow float in case we need to undo any new additions
+ nsFrameList* overflowPlace = GetOverflowPlaceholders();
+ nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
+
+ // Reflow the block into the available space
+ nsMargin computedOffsets;
+ // construct the html reflow state for the block. ReflowBlock
+ // will initialize it
+ nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
+ nsSize(availSpace.width, availSpace.height),
+ aState.mReflowState.reason, PR_TRUE);
+ blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
+
+ if (mayNeedRetry) {
+ blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
+ aState.mSpaceManager->PushState();
+ } else if (!applyTopMargin) {
+ blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
+ }
+
+ nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
+ rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
+ clearance, aState.IsAdjacentWithTop(), computedOffsets,
+ blockHtmlRS, frameReflowStatus);
+
// Remove the frame from the reflow tree.
- if (aState.mReflowState.path)
- aState.mReflowState.path->RemoveChild(frame);
-
- if (NS_FAILED(rv)) {
- return rv;
- }
- aState.mPrevChild = frame;
-
+ if (aState.mReflowState.path)
+ aState.mReflowState.path->RemoveChild(frame);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mayNeedRetry) {
+ if (clearanceFrame) {
+ aState.mSpaceManager->PopState();
+ aState.mY = startingY;
+ aState.mPrevBottomMargin = incomingMargin;
+ continue;
+ } else {
+ // pop the saved state off the stack and discard it, because we
+ // want to keep the current state, since our speculation
+ // succeeded
+ aState.mSpaceManager->DiscardState();
+ }
+ }
+
+ aState.mPrevChild = frame;
+
#if defined(REFLOW_STATUS_COVERAGE)
- RecordReflowStatus(PR_TRUE, frameReflowStatus);
+ RecordReflowStatus(PR_TRUE, frameReflowStatus);
#endif
-
- if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
- // None of the child block fits.
- UndoSplitPlaceholders(aState, lastPlaceholder);
- PushLines(aState, aLine.prev());
- *aKeepReflowGoing = PR_FALSE;
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
- }
- else {
- // Note: line-break-after a block is a nop
-
- // Try to place the child block
- PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
- nsCollapsingMargin collapsedBottomMargin;
- nsRect combinedArea(0,0,0,0);
- *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop,
- computedOffsets, collapsedBottomMargin,
- aLine->mBounds, combinedArea);
- if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
- line_iterator nextLine = aLine;
- ++nextLine;
- if (nextLine != end_lines()) {
- nextLine->MarkPreviousMarginDirty();
- }
- }
-
- if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
- // Mark the line as dirty so once we known the final shrink wrap width
- // we can reflow the block to the correct size
- // XXX We don't always need to do this...
- aLine->MarkDirty();
- aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
- }
- if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
- // Add the right margin to the line's bounds. That way it will be
- // taken into account when we compute our shrink wrap size.
- nscoord marginRight = brc.GetMargin().right;
- if (marginRight != NS_UNCONSTRAINEDSIZE) {
- aLine->mBounds.width += marginRight;
- }
- }
- aLine->SetCombinedArea(combinedArea);
- if (*aKeepReflowGoing) {
- // Some of the child block fit
-
- // Advance to new Y position
- nscoord newY = aLine->mBounds.YMost();
- aState.mY = newY;
-
- // Continue the block frame now if it didn't completely fit in
- // the available space.
- if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
- PRBool madeContinuation;
- rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
- if (NS_FAILED(rv))
- return rv;
-
- nsIFrame* nextFrame = frame->GetNextInFlow();
-
- // Push continuation to a new line, but only if we actually made one.
- if (madeContinuation) {
- nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
- if (nsnull == line) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- mLines.after_insert(aLine, line);
- }
-
- // Advance to next line since some of the block fit. That way
- // only the following lines will be pushed.
- PushLines(aState, aLine);
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
- // If we need to reflow the continuation of the block child,
- // then we'd better reflow our continuation
- if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
- aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
- // We also need to make that continuation's line dirty so it
- // gets reflowed when we reflow our next in flow. The
- // nif's line must always be either the first line
- // of the nif's parent block or else one of our own overflow
- // lines. In the latter case the line is already marked dirty,
- // so just detect and handle the first case.
- nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent());
- NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame
- || nifBlock->GetType() == nsLayoutAtoms::areaFrame,
- "A block's child's next in flow's parent must be a block!");
- line_iterator firstLine = nifBlock->begin_lines();
- if (firstLine != nifBlock->end_lines() && firstLine->Contains(nextFrame)) {
- firstLine->MarkDirty();
- }
- }
- *aKeepReflowGoing = PR_FALSE;
-
- // The bottom margin for a block is only applied on the last
- // flow block. Since we just continued the child block frame,
- // we know that line->mFirstChild is not the last flow block
- // therefore zero out the running margin value.
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": reflow incomplete, frame=");
- nsFrame::ListTag(stdout, frame);
- printf(" prevBottomMargin=%d, setting to zero\n",
- aState.mPrevBottomMargin);
-#endif
- aState.mPrevBottomMargin.Zero();
- }
- else {
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": reflow complete for ");
- nsFrame::ListTag(stdout, frame);
- printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
- aState.mPrevBottomMargin, collapsedBottomMargin.get());
-#endif
- aState.mPrevBottomMargin = collapsedBottomMargin;
- }
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": frame=");
- nsFrame::ListTag(stdout, frame);
- printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
- brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
- aState.mPrevBottomMargin);
-#endif
-
- // Post-process the "line"
- nscoord maxElementWidth = 0;
- if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
- maxElementWidth = brc.GetMaxElementWidth();
- }
- // If we asked the block to update its maximum width, then record the
- // updated value in the line, and update the current maximum width
- if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
- aLine->mMaximumWidth = brc.GetMaximumWidth();
- aState.UpdateMaximumWidth(aLine->mMaximumWidth);
- }
- PostPlaceLine(aState, aLine, maxElementWidth);
-
- // If the block frame that we just reflowed happens to be our
- // first block, then its computed ascent is ours
- if (frame == GetTopBlockChild(aState.mPresContext)) {
- const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
- mAscent = metrics.ascent;
- }
-
- // Place the "marker" (bullet) frame.
- //
- // According to the CSS2 spec, section 12.6.1, the "marker" box
- // participates in the height calculation of the list-item box's
- // first line box.
- //
- // There are exactly two places a bullet can be placed: near the
- // first or second line. Its only placed on the second line in a
- // rare case: an empty first line followed by a second line that
- // contains a block (example:
... ). This is where - // the second case can happen. - if (mBullet && HaveOutsideBullet() && - ((aLine == mLines.front()) || - ((0 == mLines.front()->mBounds.height) && - (aLine == begin_lines().next())))) { - // Reflow the bullet - nsHTMLReflowMetrics metrics(nsnull); - ReflowBullet(aState, metrics); - - // Doing the alignment using |mAscent| will also cater for bullets - // that are placed next to a child block (bug 92896) - // (Note that mAscent should be set by now, otherwise why would - // we be placing the bullet yet?) - - // Tall bullets won't look particularly nice here... - nsRect bbox = mBullet->GetRect(); - nscoord bulletTopMargin = applyTopMargin - ? collapsedBottomMargin.get() - : 0; - bbox.y = aState.BorderPadding().top + mAscent - - metrics.ascent + bulletTopMargin; - mBullet->SetRect(bbox); - } + + if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { + // None of the child block fits. + UndoSplitPlaceholders(aState, lastPlaceholder); + PushLines(aState, aLine.prev()); + *aKeepReflowGoing = PR_FALSE; + aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } else { - // None of the block fits. Determine the correct reflow status. - if (aLine == mLines.front()) { - // If it's our very first line then we need to be pushed to - // our parents next-in-flow. Therefore, return break-before - // status for our reflow status. - aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); + // Note: line-break-after a block is a nop + + // Try to place the child block + PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); + nsCollapsingMargin collapsedBottomMargin; + nsRect combinedArea(0,0,0,0); + *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop, + aLine.get(), + computedOffsets, collapsedBottomMargin, + aLine->mBounds, combinedArea); + if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) { + line_iterator nextLine = aLine; + ++nextLine; + if (nextLine != end_lines()) { + nextLine->MarkPreviousMarginDirty(); + } + } + + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { + // Mark the line as dirty so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); + } + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { + // Add the right margin to the line's bounds. That way it will be + // taken into account when we compute our shrink wrap size. + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + } + aLine->SetCombinedArea(combinedArea); + if (*aKeepReflowGoing) { + // Some of the child block fit + + // Advance to new Y position + nscoord newY = aLine->mBounds.YMost(); + aState.mY = newY; + + // Continue the block frame now if it didn't completely fit in + // the available space. + if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) { + PRBool madeContinuation; + rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation); + if (NS_FAILED(rv)) + return rv; + + nsIFrame* nextFrame = frame->GetNextInFlow(); + + // Push continuation to a new line, but only if we actually made one. + if (madeContinuation) { + nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE); + if (nsnull == line) { + return NS_ERROR_OUT_OF_MEMORY; + } + mLines.after_insert(aLine, line); + } + + // Advance to next line since some of the block fit. That way + // only the following lines will be pushed. + PushLines(aState, aLine); + aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; + // If we need to reflow the continuation of the block child, + // then we'd better reflow our continuation + if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; + // We also need to make that continuation's line dirty so it + // gets reflowed when we reflow our next in flow. The + // nif's line must always be either the first line + // of the nif's parent block or else one of our own overflow + // lines. In the latter case the line is already marked dirty, + // so just detect and handle the first case. + nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent()); + NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame + || nifBlock->GetType() == nsLayoutAtoms::areaFrame, + "A block's child's next in flow's parent must be a block!"); + line_iterator firstLine = nifBlock->begin_lines(); + if (firstLine != nifBlock->end_lines() && firstLine->Contains(nextFrame)) { + firstLine->MarkDirty(); + } + } + *aKeepReflowGoing = PR_FALSE; + + // The bottom margin for a block is only applied on the last + // flow block. Since we just continued the child block frame, + // we know that line->mFirstChild is not the last flow block + // therefore zero out the running margin value. +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": reflow incomplete, frame="); + nsFrame::ListTag(stdout, frame); + printf(" prevBottomMargin=%d, setting to zero\n", + aState.mPrevBottomMargin); +#endif + aState.mPrevBottomMargin.Zero(); + } + else { +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": reflow complete for "); + nsFrame::ListTag(stdout, frame); + printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", + aState.mPrevBottomMargin, collapsedBottomMargin.get()); +#endif + aState.mPrevBottomMargin = collapsedBottomMargin; + } +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": frame="); + nsFrame::ListTag(stdout, frame); + printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n", + brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(), + aState.mPrevBottomMargin); +#endif + + // Post-process the "line" + nscoord maxElementWidth = 0; + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) { + maxElementWidth = brc.GetMaxElementWidth(); + } + // If we asked the block to update its maximum width, then record the + // updated value in the line, and update the current maximum width + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { + aLine->mMaximumWidth = brc.GetMaximumWidth(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + } + PostPlaceLine(aState, aLine, maxElementWidth); + + // If the block frame that we just reflowed happens to be our + // first block, then its computed ascent is ours + if (frame == GetTopBlockChild(aState.mPresContext)) { + const nsHTMLReflowMetrics& metrics = brc.GetMetrics(); + mAscent = metrics.ascent; + } + + // Place the "marker" (bullet) frame. + // + // According to the CSS2 spec, section 12.6.1, the "marker" box + // participates in the height calculation of the list-item box's + // first line box. + // + // There are exactly two places a bullet can be placed: near the + // first or second line. Its only placed on the second line in a + // rare case: an empty first line followed by a second line that + // contains a block (example:
... ). This is where
+ // the second case can happen.
+ if (mBullet && HaveOutsideBullet() &&
+ ((aLine == mLines.front()) ||
+ ((0 == mLines.front()->mBounds.height) &&
+ (aLine == begin_lines().next())))) {
+ // Reflow the bullet
+ nsHTMLReflowMetrics metrics(nsnull);
+ ReflowBullet(aState, metrics);
+
+ // Doing the alignment using |mAscent| will also cater for bullets
+ // that are placed next to a child block (bug 92896)
+ // (Note that mAscent should be set by now, otherwise why would
+ // we be placing the bullet yet?)
+
+ // Tall bullets won't look particularly nice here...
+ nsRect bbox = mBullet->GetRect();
+ nscoord bulletTopMargin = applyTopMargin
+ ? collapsedBottomMargin.get()
+ : 0;
+ bbox.y = aState.BorderPadding().top + mAscent -
+ metrics.ascent + bulletTopMargin;
+ mBullet->SetRect(bbox);
+ }
}
else {
- // Push the line that didn't fit and any lines that follow it
- // to our next-in-flow.
- UndoSplitPlaceholders(aState, lastPlaceholder);
- PushLines(aState, aLine.prev());
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
+ // None of the block fits. Determine the correct reflow status.
+ if (aLine == mLines.front()) {
+ // If it's our very first line then we need to be pushed to
+ // our parents next-in-flow. Therefore, return break-before
+ // status for our reflow status.
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ }
+ else {
+ // Push the line that didn't fit and any lines that follow it
+ // to our next-in-flow.
+ UndoSplitPlaceholders(aState, lastPlaceholder);
+ PushLines(aState, aLine.prev());
+ aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
+ }
}
}
+ break; // out of the reflow retry loop
}
+
#ifdef DEBUG
VerifyLines(PR_TRUE);
#endif
@@ -3795,7 +3979,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// break-after-not-complete. There are two situations: we are a
// block or we are an inline. This makes a total of 10 cases
// (fortunately, there is some overlap).
- aLine->SetBreakType(NS_STYLE_CLEAR_NONE);
+ aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
(NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
// Always abort the line reflow (because a line break is the
@@ -3838,7 +4022,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// the
's break type with the inline's break type (the inline will be the very
// next frame after the split float).
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
- breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
}
// Break-after cases
@@ -3847,7 +4032,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
breakType = NS_STYLE_CLEAR_NONE;
}
}
- aLine->SetBreakType(breakType);
+ aLine->SetBreakTypeAfter(breakType);
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
// Create a continuation for the incomplete frame. Note that the
// frame may already have a continuation.
@@ -4335,8 +4520,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// Apply break-after clearing if necessary
// This must stay in sync with |ReflowDirtyLines|.
- if (aLine->HasFloatBreak()) {
- aState.ClearFloats(aState.mY, aLine->GetBreakType());
+ if (aLine->HasFloatBreakAfter()) {
+ aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
}
return PR_FALSE;
@@ -5228,6 +5413,30 @@ nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
////////////////////////////////////////////////////////////////////////
// Float support
+static void InitReflowStateForFloat(nsHTMLReflowState* aState, nsPresContext* aPresContext)
+{
+ /* We build a different reflow context based on the width attribute of the block
+ * when 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.
+ */
+ // XXXldb We should really fix this in nsHTMLReflowState::InitConstraints instead.
+ const nsStylePosition* position = aState->frame->GetStylePosition();
+ nsStyleUnit widthUnit = position->mWidth.GetUnit();
+
+ if (eStyleUnit_Auto == widthUnit) {
+ // Initialize the reflow state and constrain the containing block's
+ // width and height to the available width and height.
+ aState->Init(aPresContext, aState->availableWidth, aState->availableHeight);
+ } else {
+ // Initialize the reflow state and use the containing block's
+ // computed width and height (or derive appropriate values for an
+ // absolutely positioned frame).
+ aState->Init(aPresContext);
+ }
+}
+
nsresult
nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
nsPlaceholderFrame* aPlaceholder,
@@ -5298,11 +5507,14 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
availWidth, availHeight);
// construct the html reflow state for the float. ReflowBlock will
- // initialize it and set its reason.
+ // initialize it.
nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
floatFrame,
nsSize(availSpace.width, availSpace.height),
aState.mReflowState.reason, PR_FALSE);
+
+ InitReflowStateForFloat(&floatRS, aState.mPresContext);
+
// Setup a block reflow state to reflow the float.
nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
computeMaxElementWidth,
@@ -5311,10 +5523,28 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
// Reflow the float
PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
- nsCollapsingMargin margin;
- nsresult rv = brc.ReflowBlock(availSpace, PR_TRUE, margin, isAdjacentWithTop,
- aFloatCache->mOffsets, floatRS,
- aReflowStatus);
+ nsIFrame* clearanceFrame = nsnull;
+ nsresult rv;
+ do {
+ nsCollapsingMargin margin;
+ PRBool mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
+ clearanceFrame, &mayNeedRetry);
+
+ if (mayNeedRetry && !clearanceFrame) {
+ floatRS.mDiscoveredClearance = &clearanceFrame;
+ // We don't need to push the space manager state because the the block has its own
+ // space manager that will be destroyed and recreated
+ } else {
+ floatRS.mDiscoveredClearance = nsnull;
+ }
+
+ rv = brc.ReflowBlock(availSpace, PR_TRUE, margin,
+ 0, isAdjacentWithTop,
+ aFloatCache->mOffsets, floatRS,
+ aReflowStatus);
+ } while (NS_SUCCEEDED(rv) && clearanceFrame);
+
// An incomplete reflow status means we should split the float
// if the height is constrained (bug 145305).
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight))
@@ -5341,14 +5571,34 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
availSpace.width = maxElementWidth;
nsCollapsingMargin marginMEW;
// construct the html reflow state for the float.
- // ReflowBlock will initialize it and set its reason.
+ // ReflowBlock will initialize it.
nsHTMLReflowState redoFloatRS(aState.mPresContext, aState.mReflowState,
floatFrame,
nsSize(availSpace.width, availSpace.height),
aState.mReflowState.reason, PR_FALSE);
- rv = brc.ReflowBlock(availSpace, PR_TRUE, marginMEW, isAdjacentWithTop,
- aFloatCache->mOffsets, redoFloatRS,
- aReflowStatus);
+
+ InitReflowStateForFloat(&redoFloatRS, aState.mPresContext);
+
+ clearanceFrame = nsnull;
+ do {
+ nsCollapsingMargin marginMEW;
+ PRBool mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(redoFloatRS, &marginMEW, clearanceFrame, &mayNeedRetry);
+
+ if (mayNeedRetry && !clearanceFrame) {
+ redoFloatRS.mDiscoveredClearance = &clearanceFrame;
+ // We don't need to push the space manager state because the
+ // the block has its own space manager that will be
+ // destroyed and recreated
+ } else {
+ redoFloatRS.mDiscoveredClearance = nsnull;
+ }
+
+ rv = brc.ReflowBlock(availSpace, PR_TRUE, marginMEW,
+ 0, isAdjacentWithTop,
+ aFloatCache->mOffsets, redoFloatRS,
+ aReflowStatus);
+ } while (NS_SUCCEEDED(rv) && clearanceFrame);
}
}
@@ -5432,8 +5682,8 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
if (mPrevInFlow) {
// get the break type of the last line in mPrevInFlow
line_iterator endLine = --((nsBlockFrame*)mPrevInFlow)->end_lines();
- if (endLine->HasFloatBreak()) {
- aState.mFloatBreakType = endLine->GetBreakType();
+ if (endLine->HasFloatBreakAfter()) {
+ aState.mFloatBreakType = endLine->GetBreakTypeAfter();
}
}
else NS_ASSERTION(PR_FALSE, "no prev in flow");
diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h
index fa83bf8cded..f84b60b4ed5 100644
--- a/mozilla/layout/generic/nsBlockFrame.h
+++ b/mozilla/layout/generic/nsBlockFrame.h
@@ -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
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.
diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp
index 021b9a25b6a..dc7f828f3c4 100644
--- a/mozilla/layout/generic/nsBlockReflowContext.cpp
+++ b/mozilla/layout/generic/nsBlockReflowContext.cpp
@@ -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.
diff --git a/mozilla/layout/generic/nsBlockReflowContext.h b/mozilla/layout/generic/nsBlockReflowContext.h
index 6868a664510..a6aaaec7536 100644
--- a/mozilla/layout/generic/nsBlockReflowContext.h
+++ b/mozilla/layout/generic/nsBlockReflowContext.h
@@ -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;
diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp
index 9554d37d99e..d8a1323dbab 100644
--- a/mozilla/layout/generic/nsBlockReflowState.cpp
+++ b/mozilla/layout/generic/nsBlockReflowState.cpp
@@ -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;
}
diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h
index 97939580a78..f5c35b5b4b9 100644
--- a/mozilla/layout/generic/nsBlockReflowState.h
+++ b/mozilla/layout/generic/nsBlockReflowState.h
@@ -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;
diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp
index cf63950398f..c655f0932f6 100644
--- a/mozilla/layout/generic/nsHTMLReflowState.cpp
+++ b/mozilla/layout/generic/nsHTMLReflowState.cpp
@@ -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;
diff --git a/mozilla/layout/generic/nsHTMLReflowState.h b/mozilla/layout/generic/nsHTMLReflowState.h
index 5cb0de47324..39d93fcda2d 100644
--- a/mozilla/layout/generic/nsHTMLReflowState.h
+++ b/mozilla/layout/generic/nsHTMLReflowState.h
@@ -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
diff --git a/mozilla/layout/generic/nsLineBox.cpp b/mozilla/layout/generic/nsLineBox.cpp
index 7959bf7e09b..b0f78795bd6 100644
--- a/mozilla/layout/generic/nsLineBox.cpp
+++ b/mozilla/layout/generic/nsLineBox.cpp
@@ -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;
diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h
index 9f79292ebd1..dc2eee87e84 100644
--- a/mozilla/layout/generic/nsLineBox.h
+++ b/mozilla/layout/generic/nsLineBox.h
@@ -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 {
diff --git a/mozilla/layout/generic/nsSpaceManager.cpp b/mozilla/layout/generic/nsSpaceManager.cpp
index 6b79a1890b6..3e50acf7289 100644
--- a/mozilla/layout/generic/nsSpaceManager.cpp
+++ b/mozilla/layout/generic/nsSpaceManager.cpp
@@ -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()
{
diff --git a/mozilla/layout/generic/nsSpaceManager.h b/mozilla/layout/generic/nsSpaceManager.h
index e825ad9cd0d..f903308497c 100644
--- a/mozilla/layout/generic/nsSpaceManager.h
+++ b/mozilla/layout/generic/nsSpaceManager.h
@@ -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.
diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp
index 49eae095f6a..b7d327a7176 100644
--- a/mozilla/layout/html/base/src/nsBlockFrame.cpp
+++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp
@@ -695,7 +695,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
- NS_BLOCK_MARGIN_ROOT & mState);
+ aReflowState.mFlags.mHasClearance || (NS_BLOCK_MARGIN_ROOT & mState),
+ (NS_BLOCK_MARGIN_ROOT & mState));
// The condition for doing Bidi resolutions includes a test for the
// dirtiness flags, because blocks sometimes send a resize reflow
@@ -1133,6 +1134,27 @@ IsPercentageAwareChild(const nsIFrame* aFrame)
return PR_FALSE;
}
+PRBool
+nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
+{
+ line_iterator begin = begin_lines();
+ line_iterator line = end_lines();
+
+ while (PR_TRUE) {
+ if (begin == line) {
+ return PR_FALSE;
+ }
+ --line;
+ if (line->mBounds.height != 0 || !line->CachedIsEmpty()) {
+ return PR_FALSE;
+ }
+ if (line->HasClearance()) {
+ return PR_TRUE;
+ }
+ }
+ // not reached
+}
+
void
nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
nsBlockReflowState& aState,
@@ -1260,7 +1282,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
nscoord oldDesiredWidth = aMetrics.width;
#endif
nsBlockReflowState state(reflowState, aState.mPresContext, this,
- aMetrics, NS_BLOCK_MARGIN_ROOT & mState);
+ aMetrics,
+ aReflowState.mFlags.mHasClearance || (NS_BLOCK_MARGIN_ROOT & mState),
+ (NS_BLOCK_MARGIN_ROOT & mState));
ReflowDirtyLines(state);
aState.mY = state.mY;
NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width");
@@ -1268,6 +1292,29 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
}
}
+ // Return bottom margin information
+ // rbs says he hit this assertion occasionally (see bug 86947), so
+ // just set the margin to zero and we'll figure out why later
+ //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
+ // "someone else set the margin");
+ nscoord nonCarriedOutVerticalMargin = 0;
+ if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
+ // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
+ // line with clearance and a non-zero top margin and all
+ // subsequent lines are empty, then we do not allow our childrens'
+ // carried out bottom margin to be carried out of us and collapse
+ // with our own bottom margin.
+ if (CheckForCollapsedBottomMarginFromClearanceLine()) {
+ // Convert the children's carried out margin to something that
+ // we will include in our height
+ nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+ }
+ aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
+ } else {
+ aMetrics.mCarriedOutBottomMargin.Zero();
+ }
+
// Compute final height
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) {
if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
@@ -1296,14 +1343,14 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
}
else {
// Use the current height; continuations will take up the rest.
- aMetrics.height = aState.mY;
+ aMetrics.height = aState.mY + nonCarriedOutVerticalMargin;
}
// Don't carry out a bottom margin when our height is fixed.
- aState.mPrevBottomMargin.Zero();
+ aMetrics.mCarriedOutBottomMargin.Zero();
}
else {
- nscoord autoHeight = aState.mY;
+ nscoord autoHeight = aState.mY + nonCarriedOutVerticalMargin;
// Shrink wrap our height around our contents.
if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
@@ -1357,16 +1404,6 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
#endif
}
- // Return bottom margin information
- // rbs says he hit this assertion occasionally (see bug 86947), so
- // just set the margin to zero and we'll figure out why later
- //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
- // "someone else set the margin");
- if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT))
- aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
- else
- aMetrics.mCarriedOutBottomMargin.Zero();
-
#ifdef DEBUG_blocks
if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
ListTag(stdout);
@@ -1537,7 +1574,7 @@ nsBlockFrame::RetargetInlineIncrementalReflow(nsReflowPath::iterator &aTarget,
// continuations will be preserved during an unconstrained reflow.
// XXXwaterson should this be `!= NS_STYLE_CLEAR_NONE'?
--aLine;
- if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE)
+ if (aLine->GetBreakTypeAfter() == NS_STYLE_CLEAR_LINE)
break;
*aTarget = aPrevInFlow;
@@ -1793,7 +1830,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
if (line->IsBlock() ||
line->HasPercentageChild() ||
line->HasFloats() ||
- (line != mLines.back() && !line->HasBreak()) ||
+ (line != mLines.back() && !line->HasBreakAfter()) ||
line->ResizeReflowOptimizationDisabled() ||
line->IsImpactedByFloat() ||
(line->mBounds.XMost() > newAvailWidth)) {
@@ -1809,14 +1846,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
#ifdef DEBUG
if (gNoisyReflow && !line->IsDirty()) {
IndentBy(stdout, gNoiseIndent + 1);
- printf("skipped: line=%p next=%p %s %s%s%s breakType=%d xmost=%d\n",
+ printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
NS_STATIC_CAST(void*, line.get()),
NS_STATIC_CAST(void*, (line.next() != end_lines() ? line.next().get() : nsnull)),
line->IsBlock() ? "block" : "inline",
- line->HasBreak() ? "has-break " : "",
+ line->HasBreakAfter() ? "has-break-after " : "",
line->HasFloats() ? "has-floats " : "",
line->IsImpactedByFloat() ? "impacted " : "",
- line->GetBreakType(),
+ line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
line->mBounds.XMost());
}
#endif
@@ -1970,10 +2007,15 @@ DirtyLineIfWrappedLinesAreDirty(const nsLineList::iterator& aLine,
}
static void PlaceFrameView(nsPresContext* aPresContext, nsIFrame* aFrame);
-static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent,
nsIFrame** aHead, nsIFrame** aTail);
+static PRBool LineHasClear(nsLineBox* aLine) {
+ return aLine->GetBreakTypeBefore() || aLine->HasFloatBreakAfter()
+ || (aLine->IsBlock() && (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN));
+}
+
+
static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent,
nsIFrame* aNewParent) {
aFrame->SetParent(aNewParent);
@@ -2026,6 +2068,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE;
PRBool repositionViews = PR_FALSE; // should we really need this?
+ PRBool foundAnyClears = PR_FALSE;
#ifdef DEBUG
if (gNoisyReflow) {
@@ -2066,8 +2109,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// reflow it or if its previous margin is dirty
PRBool needToRecoverState = PR_FALSE;
PRBool lastLineMovedUp = PR_FALSE;
- PRUint8 floatBreakType = NS_STYLE_CLEAR_NONE;
-
+ // We save up information about BR-clearance here
+ PRUint8 inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
+
// Reflow the lines that are already ours
line_iterator line = begin_lines(), line_end = end_lines();
for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
@@ -2097,15 +2141,59 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
::DirtyLineIfWrappedLinesAreDirty(line, line_end);
}
+ // This really sucks, but we have to look inside any blocks that have clear
+ // elements inside them.
+ // XXX what can we do smarter here?
+ if (line->IsBlock() &&
+ (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
+ line->MarkDirty();
+ }
+
+ // We have to reflow the line if it's a block whose clearance
+ // might have changed, so detect that.
+ if (!line->IsDirty() && line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE) {
+ nscoord curY = aState.mY;
+ // See where we would be after applying any clearance due to
+ // BRs.
+ if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+ curY = aState.ClearFloats(curY, inlineFloatBreakType);
+ }
+
+ nscoord newY = aState.ClearFloats(curY, line->GetBreakTypeBefore());
+
+ if (line->HasClearance()) {
+ // Reflow the line if it might not have clearance anymore.
+ if (newY == curY
+ // aState.mY is the clearance point which should be the
+ // top border-edge of the block frame. If sliding the
+ // block by deltaY isn't going to put it in the predicted
+ // position, then we'd better reflow the line.
+ || newY != line->mBounds.y + deltaY) {
+ line->MarkDirty();
+ }
+ } else {
+ // Reflow the line if the line might have clearance now.
+ if (curY != newY) {
+ line->MarkDirty();
+ }
+ }
+ }
+
+ // We might have to reflow a line that is after a clearing BR.
+ if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
+ aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
+ if (aState.mY != line->mBounds.y + deltaY) {
+ // SlideLine is not going to put the line where the clearance
+ // put it. Reflow the line to be sure.
+ line->MarkDirty();
+ }
+ inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
+ }
+
// Make sure |aState.mPrevBottomMargin| is at the correct position
// before calling PropagateFloatDamage.
if (needToRecoverState &&
(line->IsDirty() || line->IsPreviousMarginDirty())) {
- if (floatBreakType != NS_STYLE_CLEAR_NONE) {
- aState.ClearFloats(aState.mY, floatBreakType);
- floatBreakType = NS_STYLE_CLEAR_NONE;
- }
-
// We need to reconstruct the bottom margin only if we didn't
// reflow the previous line and we do need to reflow (or repair
// the top position of) the next line.
@@ -2218,25 +2306,29 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.RecoverStateFrom(line, deltaY);
// Keep mY up to date in case we're propagating reflow damage
- // and also because our final height may depend on it. Only
- // update mY if the line is not empty, because that's what
- // PlaceLine does.
- if (!line->CachedIsEmpty()) {
+ // and also because our final height may depend on it. If the
+ // line is inlines, then only update mY if the line is not
+ // empty, because that's what PlaceLine does. (Empty blocks may
+ // want to update mY, e.g. if they have clearance.)
+ if (line->IsBlock() || !line->CachedIsEmpty()) {
aState.mY = line->mBounds.YMost();
- // This will include any pending float clearing height, so
- // don't bother clearing previous lines' floats
- floatBreakType = NS_STYLE_CLEAR_NONE;
}
// Record if we need to clear floats before reflowing the next
- // line
- if (line->HasFloatBreak()) {
- floatBreakType = ::CombineBreakType(floatBreakType, line->GetBreakType());
+ // line. Note that inlineFloatBreakType will be handled and
+ // cleared before the next line is processed, so there is no
+ // need to combine break types here.
+ if (line->HasFloatBreakAfter()) {
+ inlineFloatBreakType = line->GetBreakTypeAfter();
}
needToRecoverState = PR_TRUE;
}
+ if (LineHasClear(line.get())) {
+ foundAnyClears = PR_TRUE;
+ }
+
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@@ -2399,6 +2491,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
break;
}
+ if (LineHasClear(line.get())) {
+ foundAnyClears = PR_TRUE;
+ }
+
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
@@ -2420,6 +2516,12 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.mY += metrics.height;
}
+ if (foundAnyClears) {
+ AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ } else {
+ RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
+ }
+
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@@ -3101,38 +3203,6 @@ nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState,
}
}
-// Combine aNewBreakType with aOrigBreakType, but limit the break types
-// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT. When there is a
right
-// after a float and the float splits, then the
's break type is combined
-// with the break type of the frame right after the floats next-in-flow.
-static PRUint8
-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;
-}
-
nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
line_iterator aLine,
@@ -3154,320 +3224,434 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH),
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
- // See if we should apply the top margin. If the block frame being
- // reflowed is a continuation (non-null prev-in-flow) then we don't
- // apply its top margin because its not significant. Otherwise, dig
- // deeper.
- PRBool applyTopMargin = PR_FALSE;
- nsIFrame* framePrevInFlow = frame->GetPrevInFlow();
- if (nsnull == framePrevInFlow) {
- applyTopMargin = ShouldApplyTopMargin(aState, aLine);
- }
-
PRUint8 breakType = display->mBreakType;
// If a float split and its prev-in-flow was followed by a
, then combine
// the
's break type with the block's break type (the block will be the very
// next frame after the split float).
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
- breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
}
+
// Clear past floats before the block if the clear style is not none
- aLine->SetBreakType(breakType);
- if (NS_STYLE_CLEAR_NONE != breakType) {
- PRBool alsoApplyTopMargin = aState.ClearPastFloats(breakType);
- if (alsoApplyTopMargin) {
- applyTopMargin = PR_TRUE;
- }
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": y=%d child ", aState.mY);
- ListTag(stdout, frame);
- printf(" has clear of %d => %s, mPrevBottomMargin=%d\n",
- breakType,
- applyTopMargin ? "applyTopMargin" : "nope",
- aState.mPrevBottomMargin);
-#endif
- }
+ aLine->SetBreakTypeBefore(breakType);
+
+ // See if we should apply the top margin. If the block frame being
+ // reflowed is a continuation (non-null prev-in-flow) then we don't
+ // apply its top margin because its not significant. Otherwise, dig
+ // deeper.
+ PRBool applyTopMargin =
+ !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
- nscoord topMargin = 0;
if (applyTopMargin) {
- // Precompute the blocks top margin value so that we can get the
- // correct available space (there might be a float that's
- // already been placed below the aState.mPrevBottomMargin
-
- // Setup a reflowState to get the style computed margin-top
- // value. We'll use a reason of `resize' so that we don't fudge
- // any incremental reflow state.
-
- // The availSpace here is irrelevant to our needs - all we want
- // out if this setup is the margin-top value which doesn't depend
- // on the childs available space.
- nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
- nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
- frame, availSpace, eReflowReason_Resize);
-
- // Now compute the collapsed margin-top value into aState.mPrevBottomMargin
- nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin;
- nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext,
- reflowState,
- aState.mPrevBottomMargin);
- topMargin = aState.mPrevBottomMargin.get();
- aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed
-
- // Temporarily advance the running Y value so that the
- // GetAvailableSpace method will return the right available
- // space. This undone as soon as the margin is computed.
- aState.mY += topMargin;
+ // The HasClearance setting is only valid if ShouldApplyTopMargin
+ // returned PR_FALSE (in which case the top-margin-root set our
+ // clearance flag). Otherwise clear it now. We'll set it later on
+ // ourselves if necessary.
+ aLine->ClearHasClearance();
+ }
+ PRBool treatWithClearance = aLine->HasClearance();
+ // If our top margin was counted as part of some parents top-margin
+ // collapse and we are being speculatively reflowed assuming this
+ // frame DID NOT need clearance, then we need to check that
+ // assumption.
+ if (!treatWithClearance && !applyTopMargin && breakType != NS_STYLE_CLEAR_NONE &&
+ aState.mReflowState.mDiscoveredClearance) {
+ nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
+ nscoord clearY = aState.ClearFloats(curY, breakType);
+ if (clearY != curY) {
+ // Looks like that assumption was invalid, we do need
+ // clearance. Tell our ancestor so it can reflow again. It is
+ // responsible for actually setting our clearance flag before
+ // the next reflow.
+ treatWithClearance = PR_TRUE;
+ // Only record the first frame that requires clearance
+ if (!*aState.mReflowState.mDiscoveredClearance) {
+ *aState.mReflowState.mDiscoveredClearance = frame;
+ }
+ // Exactly what we do now is flexible since we'll definitely be
+ // reflowed.
+ }
+ }
+ if (treatWithClearance) {
+ applyTopMargin = PR_TRUE;
}
- // Compute the available space for the block
- aState.GetAvailableSpace();
-#ifdef REALLY_NOISY_REFLOW
- printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false");
-#endif
- PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
- aLine->SetLineIsImpactedByFloat(isImpacted);
- nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
- frame->IsSplittable(splitType);
- nsRect availSpace;
- aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
+ nsIFrame* clearanceFrame = nsnull;
+ nscoord startingY = aState.mY;
+ nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
+ while (PR_TRUE) {
+ nscoord clearance = 0;
+ nscoord topMargin = 0;
+ PRBool mayNeedRetry = PR_FALSE;
+ if (applyTopMargin) {
+ // Precompute the blocks top margin value so that we can get the
+ // correct available space (there might be a float that's
+ // already been placed below the aState.mPrevBottomMargin
- // Now put the Y coordinate back and flow the block letting the
- // block reflow context compute the same top margin value we just
- // computed (sigh).
- if (topMargin) {
+ // Setup a reflowState to get the style computed margin-top
+ // value. We'll use a reason of `resize' so that we don't fudge
+ // any incremental reflow state.
+
+ // The availSpace here is irrelevant to our needs - all we want
+ // out if this setup is the margin-top value which doesn't depend
+ // on the childs available space.
+ // XXX building a complete nsHTMLReflowState just to get the margin-top
+ // seems like a waste. And we do this for almost every block!
+ nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
+ nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
+ frame, availSpace, eReflowReason_Resize);
+
+ if (treatWithClearance) {
+ aState.mY += aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+ }
+
+ // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
+ // that all child margins collapse down to clearanceFrame.
+ nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
+ &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
+
+ // XXX optimization; we could check the collapsing children to see if they are sure
+ // to require clearance, and so avoid retrying them
+
+ if (clearanceFrame) {
+ // Don't allow retries on the second pass. The clearance decisions for the
+ // blocks whose top-margins collapse with ours are now fixed.
+ mayNeedRetry = PR_FALSE;
+ }
+
+ if (!treatWithClearance && !clearanceFrame && breakType != NS_STYLE_CLEAR_NONE) {
+ // We don't know if we need clearance and this is the first,
+ // optimistic pass. So determine whether *this block* needs
+ // clearance. Note that we do not allow the decision for whether
+ // this block has clearance to change on the second pass; that
+ // decision is only allowed to be made under the optimistic
+ // first pass.
+ nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
+ nscoord clearY = aState.ClearFloats(curY, breakType);
+ if (clearY != curY) {
+ // Looks like we need clearance and we didn't know about it already. So
+ // recompute collapsed margin
+ treatWithClearance = PR_TRUE;
+ // Remember this decision, needed for incremental reflow
+ aLine->SetHasClearance();
+
+ // Apply incoming margins
+ aState.mY += aState.mPrevBottomMargin.get();
+ aState.mPrevBottomMargin.Zero();
+
+ // Compute the collapsed margin again, ignoring the incoming margin this time
+ mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
+ &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
+ }
+ }
+
+ // Temporarily advance the running Y value so that the
+ // GetAvailableSpace method will return the right available
+ // space. This undone as soon as the horizontal margins are
+ // computed.
+ topMargin = aState.mPrevBottomMargin.get();
+
+ if (treatWithClearance) {
+ nscoord currentY = aState.mY;
+ // advance mY to the clear position.
+ aState.mY = aState.ClearFloats(aState.mY, breakType);
+
+ // Compute clearance. It's the amount we need to add to the top
+ // border-edge of the frame, after applying collapsed margins
+ // from the frame and its children, to get it to line up with
+ // the bottom of the floats. The former is currentY + topMargin,
+ // the latter is the current aState.mY.
+ // Note that negative clearance is possible
+ clearance = aState.mY - (currentY + topMargin);
+
+ // Add clearance to our top margin while we compute available
+ // space for the frame
+ topMargin += clearance;
+
+ // Note that aState.mY should stay where it is: at the top
+ // border-edge of the frame
+ } else {
+ // Advance aState.mY to the top border-edge of the frame.
+ aState.mY += topMargin;
+ }
+ }
+
+ // Here aState.mY is the top border-edge of the block.
+ // Compute the available space for the block
+ aState.GetAvailableSpace();
+#ifdef REALLY_NOISY_REFLOW
+ printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false");
+#endif
+ PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
+ aLine->SetLineIsImpactedByFloat(isImpacted);
+ nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
+ frame->IsSplittable(splitType);
+ nsRect availSpace;
+ aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
+
+ // Now put the Y coordinate back to the top of the top-margin +
+ // clearance, and flow the block.
aState.mY -= topMargin;
availSpace.y -= topMargin;
if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
availSpace.height += topMargin;
}
- }
-
- // keep track of the last overflow float in case we need to undo any new additions
- nsFrameList* overflowPlace = GetOverflowPlaceholders();
- nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
-
- // Reflow the block into the available space
- nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
- nsMargin computedOffsets;
- // construct the html reflow state for the block. ReflowBlock
- // will initialize it and set its reason.
- nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
- nsSize(availSpace.width, availSpace.height),
- aState.mReflowState.reason, PR_FALSE);
- rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
- aState.IsAdjacentWithTop(), computedOffsets,
- blockHtmlRS, frameReflowStatus);
-
+
+ // keep track of the last overflow float in case we need to undo any new additions
+ nsFrameList* overflowPlace = GetOverflowPlaceholders();
+ nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
+
+ // Reflow the block into the available space
+ nsMargin computedOffsets;
+ // construct the html reflow state for the block. ReflowBlock
+ // will initialize it
+ nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
+ nsSize(availSpace.width, availSpace.height),
+ aState.mReflowState.reason, PR_TRUE);
+ blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
+
+ if (mayNeedRetry) {
+ blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
+ aState.mSpaceManager->PushState();
+ } else if (!applyTopMargin) {
+ blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
+ }
+
+ nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
+ rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
+ clearance, aState.IsAdjacentWithTop(), computedOffsets,
+ blockHtmlRS, frameReflowStatus);
+
// Remove the frame from the reflow tree.
- if (aState.mReflowState.path)
- aState.mReflowState.path->RemoveChild(frame);
-
- if (NS_FAILED(rv)) {
- return rv;
- }
- aState.mPrevChild = frame;
-
+ if (aState.mReflowState.path)
+ aState.mReflowState.path->RemoveChild(frame);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (mayNeedRetry) {
+ if (clearanceFrame) {
+ aState.mSpaceManager->PopState();
+ aState.mY = startingY;
+ aState.mPrevBottomMargin = incomingMargin;
+ continue;
+ } else {
+ // pop the saved state off the stack and discard it, because we
+ // want to keep the current state, since our speculation
+ // succeeded
+ aState.mSpaceManager->DiscardState();
+ }
+ }
+
+ aState.mPrevChild = frame;
+
#if defined(REFLOW_STATUS_COVERAGE)
- RecordReflowStatus(PR_TRUE, frameReflowStatus);
+ RecordReflowStatus(PR_TRUE, frameReflowStatus);
#endif
-
- if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
- // None of the child block fits.
- UndoSplitPlaceholders(aState, lastPlaceholder);
- PushLines(aState, aLine.prev());
- *aKeepReflowGoing = PR_FALSE;
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
- }
- else {
- // Note: line-break-after a block is a nop
-
- // Try to place the child block
- PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
- nsCollapsingMargin collapsedBottomMargin;
- nsRect combinedArea(0,0,0,0);
- *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop,
- computedOffsets, collapsedBottomMargin,
- aLine->mBounds, combinedArea);
- if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
- line_iterator nextLine = aLine;
- ++nextLine;
- if (nextLine != end_lines()) {
- nextLine->MarkPreviousMarginDirty();
- }
- }
-
- if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
- // Mark the line as dirty so once we known the final shrink wrap width
- // we can reflow the block to the correct size
- // XXX We don't always need to do this...
- aLine->MarkDirty();
- aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
- }
- if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
- // Add the right margin to the line's bounds. That way it will be
- // taken into account when we compute our shrink wrap size.
- nscoord marginRight = brc.GetMargin().right;
- if (marginRight != NS_UNCONSTRAINEDSIZE) {
- aLine->mBounds.width += marginRight;
- }
- }
- aLine->SetCombinedArea(combinedArea);
- if (*aKeepReflowGoing) {
- // Some of the child block fit
-
- // Advance to new Y position
- nscoord newY = aLine->mBounds.YMost();
- aState.mY = newY;
-
- // Continue the block frame now if it didn't completely fit in
- // the available space.
- if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
- PRBool madeContinuation;
- rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
- if (NS_FAILED(rv))
- return rv;
-
- nsIFrame* nextFrame = frame->GetNextInFlow();
-
- // Push continuation to a new line, but only if we actually made one.
- if (madeContinuation) {
- nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
- if (nsnull == line) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- mLines.after_insert(aLine, line);
- }
-
- // Advance to next line since some of the block fit. That way
- // only the following lines will be pushed.
- PushLines(aState, aLine);
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
- // If we need to reflow the continuation of the block child,
- // then we'd better reflow our continuation
- if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
- aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
- // We also need to make that continuation's line dirty so it
- // gets reflowed when we reflow our next in flow. The
- // nif's line must always be either the first line
- // of the nif's parent block or else one of our own overflow
- // lines. In the latter case the line is already marked dirty,
- // so just detect and handle the first case.
- nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent());
- NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame
- || nifBlock->GetType() == nsLayoutAtoms::areaFrame,
- "A block's child's next in flow's parent must be a block!");
- line_iterator firstLine = nifBlock->begin_lines();
- if (firstLine != nifBlock->end_lines() && firstLine->Contains(nextFrame)) {
- firstLine->MarkDirty();
- }
- }
- *aKeepReflowGoing = PR_FALSE;
-
- // The bottom margin for a block is only applied on the last
- // flow block. Since we just continued the child block frame,
- // we know that line->mFirstChild is not the last flow block
- // therefore zero out the running margin value.
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": reflow incomplete, frame=");
- nsFrame::ListTag(stdout, frame);
- printf(" prevBottomMargin=%d, setting to zero\n",
- aState.mPrevBottomMargin);
-#endif
- aState.mPrevBottomMargin.Zero();
- }
- else {
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": reflow complete for ");
- nsFrame::ListTag(stdout, frame);
- printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
- aState.mPrevBottomMargin, collapsedBottomMargin.get());
-#endif
- aState.mPrevBottomMargin = collapsedBottomMargin;
- }
-#ifdef NOISY_VERTICAL_MARGINS
- ListTag(stdout);
- printf(": frame=");
- nsFrame::ListTag(stdout, frame);
- printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
- brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
- aState.mPrevBottomMargin);
-#endif
-
- // Post-process the "line"
- nscoord maxElementWidth = 0;
- if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
- maxElementWidth = brc.GetMaxElementWidth();
- }
- // If we asked the block to update its maximum width, then record the
- // updated value in the line, and update the current maximum width
- if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
- aLine->mMaximumWidth = brc.GetMaximumWidth();
- aState.UpdateMaximumWidth(aLine->mMaximumWidth);
- }
- PostPlaceLine(aState, aLine, maxElementWidth);
-
- // If the block frame that we just reflowed happens to be our
- // first block, then its computed ascent is ours
- if (frame == GetTopBlockChild(aState.mPresContext)) {
- const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
- mAscent = metrics.ascent;
- }
-
- // Place the "marker" (bullet) frame.
- //
- // According to the CSS2 spec, section 12.6.1, the "marker" box
- // participates in the height calculation of the list-item box's
- // first line box.
- //
- // There are exactly two places a bullet can be placed: near the
- // first or second line. Its only placed on the second line in a
- // rare case: an empty first line followed by a second line that
- // contains a block (example:
... ). This is where - // the second case can happen. - if (mBullet && HaveOutsideBullet() && - ((aLine == mLines.front()) || - ((0 == mLines.front()->mBounds.height) && - (aLine == begin_lines().next())))) { - // Reflow the bullet - nsHTMLReflowMetrics metrics(nsnull); - ReflowBullet(aState, metrics); - - // Doing the alignment using |mAscent| will also cater for bullets - // that are placed next to a child block (bug 92896) - // (Note that mAscent should be set by now, otherwise why would - // we be placing the bullet yet?) - - // Tall bullets won't look particularly nice here... - nsRect bbox = mBullet->GetRect(); - nscoord bulletTopMargin = applyTopMargin - ? collapsedBottomMargin.get() - : 0; - bbox.y = aState.BorderPadding().top + mAscent - - metrics.ascent + bulletTopMargin; - mBullet->SetRect(bbox); - } + + if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { + // None of the child block fits. + UndoSplitPlaceholders(aState, lastPlaceholder); + PushLines(aState, aLine.prev()); + *aKeepReflowGoing = PR_FALSE; + aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; } else { - // None of the block fits. Determine the correct reflow status. - if (aLine == mLines.front()) { - // If it's our very first line then we need to be pushed to - // our parents next-in-flow. Therefore, return break-before - // status for our reflow status. - aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); + // Note: line-break-after a block is a nop + + // Try to place the child block + PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); + nsCollapsingMargin collapsedBottomMargin; + nsRect combinedArea(0,0,0,0); + *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop, + aLine.get(), + computedOffsets, collapsedBottomMargin, + aLine->mBounds, combinedArea); + if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) { + line_iterator nextLine = aLine; + ++nextLine; + if (nextLine != end_lines()) { + nextLine->MarkPreviousMarginDirty(); + } + } + + if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { + // Mark the line as dirty so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE); + } + if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) { + // Add the right margin to the line's bounds. That way it will be + // taken into account when we compute our shrink wrap size. + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + } + aLine->SetCombinedArea(combinedArea); + if (*aKeepReflowGoing) { + // Some of the child block fit + + // Advance to new Y position + nscoord newY = aLine->mBounds.YMost(); + aState.mY = newY; + + // Continue the block frame now if it didn't completely fit in + // the available space. + if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) { + PRBool madeContinuation; + rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation); + if (NS_FAILED(rv)) + return rv; + + nsIFrame* nextFrame = frame->GetNextInFlow(); + + // Push continuation to a new line, but only if we actually made one. + if (madeContinuation) { + nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE); + if (nsnull == line) { + return NS_ERROR_OUT_OF_MEMORY; + } + mLines.after_insert(aLine, line); + } + + // Advance to next line since some of the block fit. That way + // only the following lines will be pushed. + PushLines(aState, aLine); + aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; + // If we need to reflow the continuation of the block child, + // then we'd better reflow our continuation + if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; + // We also need to make that continuation's line dirty so it + // gets reflowed when we reflow our next in flow. The + // nif's line must always be either the first line + // of the nif's parent block or else one of our own overflow + // lines. In the latter case the line is already marked dirty, + // so just detect and handle the first case. + nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent()); + NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame + || nifBlock->GetType() == nsLayoutAtoms::areaFrame, + "A block's child's next in flow's parent must be a block!"); + line_iterator firstLine = nifBlock->begin_lines(); + if (firstLine != nifBlock->end_lines() && firstLine->Contains(nextFrame)) { + firstLine->MarkDirty(); + } + } + *aKeepReflowGoing = PR_FALSE; + + // The bottom margin for a block is only applied on the last + // flow block. Since we just continued the child block frame, + // we know that line->mFirstChild is not the last flow block + // therefore zero out the running margin value. +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": reflow incomplete, frame="); + nsFrame::ListTag(stdout, frame); + printf(" prevBottomMargin=%d, setting to zero\n", + aState.mPrevBottomMargin); +#endif + aState.mPrevBottomMargin.Zero(); + } + else { +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": reflow complete for "); + nsFrame::ListTag(stdout, frame); + printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", + aState.mPrevBottomMargin, collapsedBottomMargin.get()); +#endif + aState.mPrevBottomMargin = collapsedBottomMargin; + } +#ifdef NOISY_VERTICAL_MARGINS + ListTag(stdout); + printf(": frame="); + nsFrame::ListTag(stdout, frame); + printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n", + brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(), + aState.mPrevBottomMargin); +#endif + + // Post-process the "line" + nscoord maxElementWidth = 0; + if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) { + maxElementWidth = brc.GetMaxElementWidth(); + } + // If we asked the block to update its maximum width, then record the + // updated value in the line, and update the current maximum width + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { + aLine->mMaximumWidth = brc.GetMaximumWidth(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + } + PostPlaceLine(aState, aLine, maxElementWidth); + + // If the block frame that we just reflowed happens to be our + // first block, then its computed ascent is ours + if (frame == GetTopBlockChild(aState.mPresContext)) { + const nsHTMLReflowMetrics& metrics = brc.GetMetrics(); + mAscent = metrics.ascent; + } + + // Place the "marker" (bullet) frame. + // + // According to the CSS2 spec, section 12.6.1, the "marker" box + // participates in the height calculation of the list-item box's + // first line box. + // + // There are exactly two places a bullet can be placed: near the + // first or second line. Its only placed on the second line in a + // rare case: an empty first line followed by a second line that + // contains a block (example:
... ). This is where
+ // the second case can happen.
+ if (mBullet && HaveOutsideBullet() &&
+ ((aLine == mLines.front()) ||
+ ((0 == mLines.front()->mBounds.height) &&
+ (aLine == begin_lines().next())))) {
+ // Reflow the bullet
+ nsHTMLReflowMetrics metrics(nsnull);
+ ReflowBullet(aState, metrics);
+
+ // Doing the alignment using |mAscent| will also cater for bullets
+ // that are placed next to a child block (bug 92896)
+ // (Note that mAscent should be set by now, otherwise why would
+ // we be placing the bullet yet?)
+
+ // Tall bullets won't look particularly nice here...
+ nsRect bbox = mBullet->GetRect();
+ nscoord bulletTopMargin = applyTopMargin
+ ? collapsedBottomMargin.get()
+ : 0;
+ bbox.y = aState.BorderPadding().top + mAscent -
+ metrics.ascent + bulletTopMargin;
+ mBullet->SetRect(bbox);
+ }
}
else {
- // Push the line that didn't fit and any lines that follow it
- // to our next-in-flow.
- UndoSplitPlaceholders(aState, lastPlaceholder);
- PushLines(aState, aLine.prev());
- aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
+ // None of the block fits. Determine the correct reflow status.
+ if (aLine == mLines.front()) {
+ // If it's our very first line then we need to be pushed to
+ // our parents next-in-flow. Therefore, return break-before
+ // status for our reflow status.
+ aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
+ }
+ else {
+ // Push the line that didn't fit and any lines that follow it
+ // to our next-in-flow.
+ UndoSplitPlaceholders(aState, lastPlaceholder);
+ PushLines(aState, aLine.prev());
+ aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
+ }
}
}
+ break; // out of the reflow retry loop
}
+
#ifdef DEBUG
VerifyLines(PR_TRUE);
#endif
@@ -3795,7 +3979,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// break-after-not-complete. There are two situations: we are a
// block or we are an inline. This makes a total of 10 cases
// (fortunately, there is some overlap).
- aLine->SetBreakType(NS_STYLE_CLEAR_NONE);
+ aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
(NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
// Always abort the line reflow (because a line break is the
@@ -3838,7 +4022,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// the
's break type with the inline's break type (the inline will be the very
// next frame after the split float).
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
- breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
+ breakType = nsLayoutUtils::CombineBreakType(breakType,
+ aState.mFloatBreakType);
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
}
// Break-after cases
@@ -3847,7 +4032,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
breakType = NS_STYLE_CLEAR_NONE;
}
}
- aLine->SetBreakType(breakType);
+ aLine->SetBreakTypeAfter(breakType);
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
// Create a continuation for the incomplete frame. Note that the
// frame may already have a continuation.
@@ -4335,8 +4520,8 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// Apply break-after clearing if necessary
// This must stay in sync with |ReflowDirtyLines|.
- if (aLine->HasFloatBreak()) {
- aState.ClearFloats(aState.mY, aLine->GetBreakType());
+ if (aLine->HasFloatBreakAfter()) {
+ aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
}
return PR_FALSE;
@@ -5228,6 +5413,30 @@ nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
////////////////////////////////////////////////////////////////////////
// Float support
+static void InitReflowStateForFloat(nsHTMLReflowState* aState, nsPresContext* aPresContext)
+{
+ /* We build a different reflow context based on the width attribute of the block
+ * when 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.
+ */
+ // XXXldb We should really fix this in nsHTMLReflowState::InitConstraints instead.
+ const nsStylePosition* position = aState->frame->GetStylePosition();
+ nsStyleUnit widthUnit = position->mWidth.GetUnit();
+
+ if (eStyleUnit_Auto == widthUnit) {
+ // Initialize the reflow state and constrain the containing block's
+ // width and height to the available width and height.
+ aState->Init(aPresContext, aState->availableWidth, aState->availableHeight);
+ } else {
+ // Initialize the reflow state and use the containing block's
+ // computed width and height (or derive appropriate values for an
+ // absolutely positioned frame).
+ aState->Init(aPresContext);
+ }
+}
+
nsresult
nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
nsPlaceholderFrame* aPlaceholder,
@@ -5298,11 +5507,14 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
availWidth, availHeight);
// construct the html reflow state for the float. ReflowBlock will
- // initialize it and set its reason.
+ // initialize it.
nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
floatFrame,
nsSize(availSpace.width, availSpace.height),
aState.mReflowState.reason, PR_FALSE);
+
+ InitReflowStateForFloat(&floatRS, aState.mPresContext);
+
// Setup a block reflow state to reflow the float.
nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
computeMaxElementWidth,
@@ -5311,10 +5523,28 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
// Reflow the float
PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
- nsCollapsingMargin margin;
- nsresult rv = brc.ReflowBlock(availSpace, PR_TRUE, margin, isAdjacentWithTop,
- aFloatCache->mOffsets, floatRS,
- aReflowStatus);
+ nsIFrame* clearanceFrame = nsnull;
+ nsresult rv;
+ do {
+ nsCollapsingMargin margin;
+ PRBool mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
+ clearanceFrame, &mayNeedRetry);
+
+ if (mayNeedRetry && !clearanceFrame) {
+ floatRS.mDiscoveredClearance = &clearanceFrame;
+ // We don't need to push the space manager state because the the block has its own
+ // space manager that will be destroyed and recreated
+ } else {
+ floatRS.mDiscoveredClearance = nsnull;
+ }
+
+ rv = brc.ReflowBlock(availSpace, PR_TRUE, margin,
+ 0, isAdjacentWithTop,
+ aFloatCache->mOffsets, floatRS,
+ aReflowStatus);
+ } while (NS_SUCCEEDED(rv) && clearanceFrame);
+
// An incomplete reflow status means we should split the float
// if the height is constrained (bug 145305).
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight))
@@ -5341,14 +5571,34 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
availSpace.width = maxElementWidth;
nsCollapsingMargin marginMEW;
// construct the html reflow state for the float.
- // ReflowBlock will initialize it and set its reason.
+ // ReflowBlock will initialize it.
nsHTMLReflowState redoFloatRS(aState.mPresContext, aState.mReflowState,
floatFrame,
nsSize(availSpace.width, availSpace.height),
aState.mReflowState.reason, PR_FALSE);
- rv = brc.ReflowBlock(availSpace, PR_TRUE, marginMEW, isAdjacentWithTop,
- aFloatCache->mOffsets, redoFloatRS,
- aReflowStatus);
+
+ InitReflowStateForFloat(&redoFloatRS, aState.mPresContext);
+
+ clearanceFrame = nsnull;
+ do {
+ nsCollapsingMargin marginMEW;
+ PRBool mayNeedRetry = PR_FALSE;
+ nsBlockReflowContext::ComputeCollapsedTopMargin(redoFloatRS, &marginMEW, clearanceFrame, &mayNeedRetry);
+
+ if (mayNeedRetry && !clearanceFrame) {
+ redoFloatRS.mDiscoveredClearance = &clearanceFrame;
+ // We don't need to push the space manager state because the
+ // the block has its own space manager that will be
+ // destroyed and recreated
+ } else {
+ redoFloatRS.mDiscoveredClearance = nsnull;
+ }
+
+ rv = brc.ReflowBlock(availSpace, PR_TRUE, marginMEW,
+ 0, isAdjacentWithTop,
+ aFloatCache->mOffsets, redoFloatRS,
+ aReflowStatus);
+ } while (NS_SUCCEEDED(rv) && clearanceFrame);
}
}
@@ -5432,8 +5682,8 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
if (mPrevInFlow) {
// get the break type of the last line in mPrevInFlow
line_iterator endLine = --((nsBlockFrame*)mPrevInFlow)->end_lines();
- if (endLine->HasFloatBreak()) {
- aState.mFloatBreakType = endLine->GetBreakType();
+ if (endLine->HasFloatBreakAfter()) {
+ aState.mFloatBreakType = endLine->GetBreakTypeAfter();
}
}
else NS_ASSERTION(PR_FALSE, "no prev in flow");
diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h
index fa83bf8cded..f84b60b4ed5 100644
--- a/mozilla/layout/html/base/src/nsBlockFrame.h
+++ b/mozilla/layout/html/base/src/nsBlockFrame.h
@@ -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
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.
diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp
index 021b9a25b6a..dc7f828f3c4 100644
--- a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp
+++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp
@@ -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.
diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.h b/mozilla/layout/html/base/src/nsBlockReflowContext.h
index 6868a664510..a6aaaec7536 100644
--- a/mozilla/layout/html/base/src/nsBlockReflowContext.h
+++ b/mozilla/layout/html/base/src/nsBlockReflowContext.h
@@ -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;
diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp
index 9554d37d99e..d8a1323dbab 100644
--- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp
+++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp
@@ -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;
}
diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h
index 97939580a78..f5c35b5b4b9 100644
--- a/mozilla/layout/html/base/src/nsBlockReflowState.h
+++ b/mozilla/layout/html/base/src/nsBlockReflowState.h
@@ -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;
diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp
index cf63950398f..c655f0932f6 100644
--- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp
+++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp
@@ -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;
diff --git a/mozilla/layout/html/base/src/nsLineBox.cpp b/mozilla/layout/html/base/src/nsLineBox.cpp
index 7959bf7e09b..b0f78795bd6 100644
--- a/mozilla/layout/html/base/src/nsLineBox.cpp
+++ b/mozilla/layout/html/base/src/nsLineBox.cpp
@@ -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;
diff --git a/mozilla/layout/html/base/src/nsLineBox.h b/mozilla/layout/html/base/src/nsLineBox.h
index 9f79292ebd1..dc2eee87e84 100644
--- a/mozilla/layout/html/base/src/nsLineBox.h
+++ b/mozilla/layout/html/base/src/nsLineBox.h
@@ -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 {