Reworked vertical margin collapsing

This commit is contained in:
kipp%netscape.com 1998-10-02 21:50:53 +00:00
Родитель 50f13e69b3
Коммит 750fa1536a
13 изменённых файлов: 1974 добавлений и 711 удалений

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -80,6 +80,13 @@ public:
virtual PRIntn GetSkipSides() const;
PRBool CalculateMargins(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(nsIFrame* aKid, nscoord aDY);
nsresult InitialReflow(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow);
@ -313,7 +320,6 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
nsHTMLReflowMetrics& aMetrics,
const nsHTMLReflowState& aReflowState)
{
//XXX ListTag(stdout); printf(": enter (runningMargin=%d)\n", aMetrics.mCarriedInTopMargin);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsInlineFrame::InlineReflow maxSize=%d,%d reason=%d nif=%p",
aReflowState.maxSize.width, aReflowState.maxSize.height,
@ -349,6 +355,7 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
inlineReflow.Init(state.mBorderPadding.left, state.mBorderPadding.top,
width, height);
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
// Now translate in by our border and padding
aLineLayout.mSpaceManager->Translate(state.mBorderPadding.left,
state.mBorderPadding.top);
@ -413,6 +420,7 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
rv = ResizeReflow(state, inlineReflow);
}
ComputeFinalSize(state, inlineReflow, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
// Now translate in by our border and padding
aLineLayout.mSpaceManager->Translate(-state.mBorderPadding.left,
@ -421,13 +429,65 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("exit nsInlineFrame::InlineReflow size=%d,%d rv=%x nif=%p",
aMetrics.width, aMetrics.height, rv, mNextInFlow));
//XXX ListTag(stdout); printf(": exit (runningMargin=%d)\n", aMetrics.mCarriedOutBottomMargin);
return rv;
}
// XXX factor this (into nsFrameReflowState?) so that both block and
// inline can use most of the same logic
PRBool
nsInlineFrame::CalculateMargins(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nscoord childsCarriedOutTopMargin = aInlineReflow.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = aInlineReflow.GetCarriedOutBottomMargin();
// If the inline-reflow is wrapped up around a block or we have some
// carried out margin information then we perform margin collapsing
// (pretending we are a block...)
if (aInlineReflow.GetIsBlock() ||
(0 != childsCarriedOutTopMargin) ||
(0 != childsCarriedOutBottomMargin)) {
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineReflow.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
// If this frame is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineReflow.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
haveCarriedMargins = PR_TRUE;
}
else {
aTopMarginResult = 0;
aBottomMarginResult = 0;
haveCarriedMargins = PR_FALSE;
}
return haveCarriedMargins;
}
void
nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
@ -457,7 +517,6 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
aMetrics.ascent = 0;
aMetrics.descent = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
// XXX tip of the iceburg: when an inline is wrapping up a block frame
@ -465,17 +524,12 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
// coordinate system; apply horizontal alignment (in case the block
// has a width set), etc. Of course the block code knows how to do
// this already...
//XXXprintf("bounds={%d,%d,%d,%d}\n",
//XXX bounds.x, bounds.y, bounds.width, bounds.height);
if (aInlineReflow.GetIsBlock()) {
// Make sure the blocks top and bottom margins get applied; the
// top margin will be in bounds.y; the bottom margin we get from
// the inline reflow.
nscoord bottomMargin = aInlineReflow.GetBottomMargin();
nscoord newY = aState.mBorderPadding.top + bounds.YMost()
+ bottomMargin + aState.mBorderPadding.bottom;
//XXXprintf("newY=%d bounds={%d,%d,%d,%d} bottomMargin=%d\n", newY,
//XXX bounds.x, bounds.y, bounds.width, bounds.height, bottomMargin);
nscoord newY = aState.mBorderPadding.top + bounds.YMost() +
aState.mBorderPadding.bottom;
aMetrics.height = newY;
aMetrics.ascent = newY;
aMetrics.descent = 0;
@ -487,16 +541,16 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
aState.mBorderPadding.bottom;
aMetrics.height = aMetrics.ascent + aMetrics.descent;
}
}
// Carry out to the caller the running bottom margin value
if (0 != aState.mBorderPadding.bottom) {
// Don't carry out a child margin when we have a border
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
// Carry out our the total child bottom margin value
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
if (aState.mComputeMaxElementSize) {
@ -680,6 +734,18 @@ nsInlineFrame::PullUpChildren(nsInlineReflowState& aState,
return reflowStatus;
}
void
nsInlineFrame::SlideFrames(nsIFrame* aKid, nscoord aDY)
{
while (nsnull != aKid) {
nsRect r;
aKid->GetRect(r);
r.y += aDY;
aKid->SetRect(r);
aKid->GetNextSibling(aKid);
}
}
PRBool
nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
@ -720,7 +786,20 @@ nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
}
return PR_FALSE;
}
else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) {
// Apply collapsing block margins. However, we only do this if a
// carried out margin was present.
nscoord topMargin, bottomMargin;
if (CalculateMargins(aState, aInlineReflow, topMargin, bottomMargin)) {
if (0 != topMargin) {
SlideFrames(mFirstChild, topMargin);
}
// Record bottom margin value for returning as our carried out
// bottom margin.
aState.mPrevBottomMargin = bottomMargin;
}
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) {
// We may be breaking after a frame here (e.g. a child inline
// frame that contains a BR tag and more content after the BR
// tag). We will propagate that upward so that this frame gets
@ -748,7 +827,8 @@ nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
}
return PR_FALSE;
}
else if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
// If we are breaking after a child that's complete, we may still
// be incomplete if we have more children that need
// reflowing. Check for this after advancing to the next frame.

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -157,7 +157,6 @@ struct nsBlockReflowState : public nsFrameReflowState {
PRUint8 mTextAlign;
nsSize mStyleSize;
PRIntn mStyleSizeFlags;
nscoord mBottomEdge; // maximum Y
@ -279,6 +278,19 @@ public:
mShrinkWrap = aShrinkWrap;
}
void RecoverLineMargins(nsBlockReflowState& aState,
LineData* aPrevLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
PRBool CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(LineData* aLine, nscoord aDY);
PRBool DrainOverflowLines();
PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
@ -381,6 +393,9 @@ public:
// If true then this frame doesn't act like css says, instead it
// shrink wraps around its contents instead of filling out to its
// parents size.
// XXX This is a *good* place to use a subclass and not
// pay for the per-frame storage
PRBool mShrinkWrap;
};
@ -872,9 +887,9 @@ struct LineData {
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
mFloaters = nsnull;
mNext = nsnull;
mInnerBottomMargin = 0;
mOuterBottomMargin = 0;
mBounds.SetRect(0,0,0,0);
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
~LineData();
@ -966,8 +981,8 @@ struct LineData {
PRUint16 mChildCount;
PRUint16 mState;
nsRect mBounds;
nscoord mInnerBottomMargin;
nscoord mOuterBottomMargin;
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsVoidArray* mFloaters;
LineData* mNext;
};
@ -1018,10 +1033,16 @@ LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
if (aOutputMe) {
for (i = aIndent; --i >= 0; ) fputs(" ", out);
char cbuf[100];
fprintf(out, "line %p: count=%d state=%s {%d,%d,%d,%d} ibm=%d obm=%d <\n",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)),
mBounds.x, mBounds.y, mBounds.width, mBounds.height,
mInnerBottomMargin, mOuterBottomMargin);
fprintf(out, "line %p: count=%d state=%s",
this, mChildCount, StateToString(cbuf, sizeof(cbuf)));
if (0 != mCarriedOutTopMargin) {
fprintf(out, " tm=%d", mCarriedOutTopMargin);
}
if (0 != mCarriedOutBottomMargin) {
fprintf(out, " bm=%d", mCarriedOutBottomMargin);
}
out << mBounds;
fprintf(out, "<\n");
}
nsIFrame* frame = mFirstChild;
@ -1265,29 +1286,6 @@ nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
//----------------------------------------------------------------------
static nscoord
GetParentLeftPadding(const nsHTMLReflowState* aReflowState)
{
nscoord leftPadding = 0;
while (nsnull != aReflowState) {
nsIFrame* frame = aReflowState->frame;
const nsStyleDisplay* styleDisplay;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) styleDisplay);
if (NS_STYLE_DISPLAY_BLOCK == styleDisplay->mDisplay) {
const nsStyleSpacing* styleSpacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) styleSpacing);
nsMargin padding;
styleSpacing->CalcPaddingFor(frame, padding);
leftPadding = padding.left;
break;
}
aReflowState = (nsHTMLReflowState*)aReflowState->parentReflowState;
}
return leftPadding;
}
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
const nsHTMLReflowMetrics& aMetrics)
@ -1354,7 +1352,6 @@ nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
mContentArea.width = maxSize.width - lr;
}
}
mBorderArea.height = maxSize.height;
mContentArea.height = maxSize.height;
mBottomEdge = maxSize.height;
@ -1706,6 +1703,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Prepare inline-reflow engine
nsInlineReflow inlineReflow(state.mLineLayout, state, this);
state.mInlineReflow = &inlineReflow;
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
nsresult rv = NS_OK;
if (eReflowReason_Initial == state.reason) {
@ -1768,6 +1766,7 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
// Compute our final size
ComputeFinalSize(state, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
#ifdef NS_DEBUG
if (GetVerifyTreeEnable()) {
@ -1870,16 +1869,26 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
}
else {
// Shrink wrap our height around our contents.
if (0 != aState.mBorderPadding.bottom) {
aState.mY += aState.mBorderPadding.bottom;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
if (aState.mIsMarginRoot) {
// When we are a margin root make sure that our last childs
// bottom margin is fully applied.
aState.mY += aState.mPrevBottomMargin;
}
aState.mY += aState.mBorderPadding.bottom;
aMetrics.height = aState.mY;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
aState.mCollapsedTopMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
if ((NS_SIZE_HAS_BOTH != ss) &&
@ -1887,7 +1896,6 @@ nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
(0 == aState.mY - aState.mBorderPadding.top))) {
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
aMetrics.ascent = aMetrics.height;
@ -2108,6 +2116,40 @@ nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
LineData* aLine,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
nscoord childsTopMargin = 0;
nscoord childsBottomMargin = 0;
if (aLine->IsBlock()) {
// Recover the block frames computed bottom margin value
nsIFrame* frame = aLine->mFirstChild;
if (nsnull != frame) {
const nsStyleSpacing* spacing;
frame->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&) spacing);
if (nsnull != spacing) {
nsMargin margin;
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
spacing, margin);
childsTopMargin = margin.top;
childsBottomMargin = margin.bottom;
}
}
}
// Collapse the carried-out-margins with the childs margins
aBottomMarginResult =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aTopMarginResult =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
@ -2127,50 +2169,40 @@ nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
return rv;
}
// Recover our reflow state. First find the lastCleanLine and the
// firstDirtyLine which follows it. While we are looking, compute
// the maximum xmost of each line.
// Recover our reflow state
LineData* firstDirtyLine = mLines;
LineData* lastCleanLine = nsnull;
LineData* lastYLine = nsnull;
while (nsnull != firstDirtyLine) {
if (firstDirtyLine->IsDirty()) {
break;
}
// Recover xmost
nscoord xmost = firstDirtyLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
}
if (firstDirtyLine->mBounds.height > 0) {
lastYLine = firstDirtyLine;
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
nscoord topMargin, bottomMargin;
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
if (0 == firstDirtyLine->mBounds.height) {
// For zero height lines, collapse the lines top and bottom
// margins together to produce the effective bottomMargin value.
bottomMargin = PR_MAX(topMargin, bottomMargin);
bottomMargin = PR_MAX(aState.mPrevBottomMargin, bottomMargin);
}
aState.mPrevBottomMargin = bottomMargin;
// Advance Y to be below the line.
aState.mY = firstDirtyLine->mBounds.YMost();
// Advance to the next line
lastCleanLine = firstDirtyLine;
firstDirtyLine = firstDirtyLine->mNext;
}
// Recover the starting Y coordinate and the previous bottom margin
// value.
if (nsnull != lastCleanLine) {
// If the lastCleanLine is not a block but instead is a zero
// height inline line then we need to backup to either a non-zero
// height line.
aState.mRunningMargin = 0;
if (nsnull != lastYLine) {
// XXX I have no idea if this is right any more!
aState.mRunningMargin = lastYLine->mInnerBottomMargin +
lastYLine->mOuterBottomMargin;
}
// Start the Y coordinate after the last clean line.
aState.mY = lastCleanLine->mBounds.YMost();
// Add in the outer margin to the Y coordinate (the inner margin
// will be present in the lastCleanLine's YMost so don't add it
// in again)
aState.mY += lastCleanLine->mOuterBottomMargin;
// XXX I'm not sure about the previous statement and floaters!!!
// Place any floaters the line has
if (nsnull != lastCleanLine->mFloaters) {
aState.mCurrentLine = lastCleanLine;
@ -2747,6 +2779,88 @@ nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
aState.mInlineReflowPrepared = PR_TRUE;
}
PRBool
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
LineData* aLine,
PRBool aInlineContext,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nsInlineReflow& ir = *aState.mInlineReflow;
PRBool isFirstNonEmptyLine = (aState.mY == aState.mBorderPadding.top);
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
if (aInlineContext &&
(0 == childsCarriedOutTopMargin) &&
(0 == childsCarriedOutBottomMargin)) {
// In an inline context, when there are no carried out margins we
// do not collapse the childs margins with the carried out
// margins; the childs margins are applied in a different manner
// in inline contexts.
aTopMarginResult = 0;
aBottomMarginResult = 0;
}
else {
haveCarriedMargins = PR_TRUE;
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineContext ? 0 : ir.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
if (isFirstNonEmptyLine) {
// If this block is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
}
else {
// For secondary lines we also collpase the sibling margins. The
// previous lines bottom margin is collapsed with the current
// lines collapsed top margin.
collapsedTopMargin = PR_MAX(aState.mPrevBottomMargin, collapsedTopMargin);
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineContext ? 0 : ir.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
}
//ListTag(stdout); printf(": line=%p topMargin=%d bottomMargin=%d [%s,%s]\n", aLine, aTopMarginResult, aBottomMarginResult, isFirstNonEmptyLine ? "first" : "!first", aState.mIsMarginRoot ? "root" : "!root");
return haveCarriedMargins;
}
void
nsBlockFrame::SlideFrames(LineData* aLine, nscoord aDY)
{
// Adjust the Y coordinate of the frames in the line
nsIFrame* kid = aLine->mFirstChild;
PRIntn n = aLine->mChildCount;
while (--n >= 0) {
nsRect r;
kid->GetRect(r);
r.y += aDY;
kid->SetRect(r);
kid->GetNextSibling(kid);
}
// Slide line box too
aLine->mBounds.y += aDY;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
LineData* aLine,
@ -2791,34 +2905,58 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
ir.HorizontalAlignFrames(aLine->mBounds);
ir.RelativePositionFrames();
// Try to place the frame. It might not fit after factoring in the
// bottom margin.
if (aLine->mBounds.height == 0) {
// Leave aState.mPrevBottomMargin alone in this case so that it
// can carry forward to the next non-empty block.
aLine->mInnerBottomMargin = 0;
aLine->mOuterBottomMargin = 0;
// Calculate margins (XXX: identical copy is in PlaceLine)
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_FALSE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
nscoord innerBottomMargin = ir.GetInnerBottomMargin();
nscoord outerBottomMargin = ir.GetBottomMargin();
nscoord newY = aLine->mBounds.YMost() + outerBottomMargin;
// XXX running margin is advanced during reflow which means if the
// frame doesn't fit the wrong value will be used in
// ComputeFinalSize
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
aState.mY = newY;
aLine->mInnerBottomMargin = innerBottomMargin;
aLine->mOuterBottomMargin = outerBottomMargin;
}
// Try to place the frame
nscoord newY = aLine->mBounds.YMost() + topMargin;
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
// The frame doesn't fit inside our available space. Push the
// line to the next-in-flow and return our incomplete status to
// our parent.
PushLines(aState);
aReflowResult = NS_FRAME_NOT_COMPLETE;
return PR_FALSE;
}
aState.mY = newY;
// Apply collapsed top-margin value
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Record bottom margin value for sibling to sibling compression
// or for returning as our carried out bottom margin.
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
nscoord xmost = aLine->mBounds.XMost();
if (xmost > aState.mKidXMost) {
aState.mKidXMost = xmost;
@ -3143,6 +3281,30 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
}
}
// Apply margin collapsing when the line has some height
nscoord topMargin = 0, bottomMargin = 0;
PRBool haveCarriedMargins =
CalculateMargins(aState, aLine, PR_TRUE, topMargin, bottomMargin);
if (!haveCarriedMargins) {
// When the child being reflowed has no margin to give, we still
// need to make sure that the previous lines bottom margin is
// applied, but only if the line has a non-zero height (lines with
// no height and no carried margin need to not upset the margin
// state)
if (0 != aLine->mBounds.height) {
topMargin = aState.mPrevBottomMargin;
}
}
else {
// A special hack: if we have carried margins but the line has no
// height then collapse the carried margins down into a single
// (bottom margin) value.
if (0 == aLine->mBounds.height) {
bottomMargin = PR_MAX(topMargin, bottomMargin);
topMargin = 0;
}
}
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
@ -3152,7 +3314,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// XXX don't forget to factor in the top/bottom margin when sharing
// this with the block code
nscoord newY = aState.mY + aLine->mBounds.height + lineBottomMargin;
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
newY, aState.mBottomEdge, aLine->mBounds.height));
@ -3163,6 +3325,20 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
return PR_FALSE;
}
// Apply collapsed top-margin value
// XXX I bet the bullet placement just got broken by this code
if (0 != topMargin) {
SlideFrames(aLine, topMargin);
}
// Adjust running margin value when either we have carried margins
// from the line or we have a non-zero height line.
if (haveCarriedMargins || (0 != aLine->mBounds.height)) {
aState.mPrevBottomMargin = bottomMargin;
}
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
// Now that we know the line is staying put, put in the outside
// bullet if we have one.
if ((nsnull == mPrevInFlow) && (aLine == mLines) && (nsnull != mBullet)) {
@ -3187,8 +3363,10 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mStyleSpacing->CalcPaddingFor(this, padding);
nscoord x = aState.mBorderPadding.left - (padding.left/2) -
metrics.width;
// XXX This calculation is wrong, especially if
// vertical-alignment occurs on the line!
nscoord y = aState.mBorderPadding.top + ir.GetMaxAscent() -
metrics.ascent;
metrics.ascent + topMargin;
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
}
@ -3832,7 +4010,6 @@ nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -3851,7 +4028,6 @@ mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTa
child->GetSize(size);
if (size.width > 0) {
// We found a non-zero sized child frame that precedes aFrame
mBlock->ListTag(stdout); printf(": !left-most: size=%d,%d", size); child->ListTag(stdout); printf("\n");
return PR_FALSE;
}
child->GetNextSibling(child);
@ -4165,14 +4341,19 @@ nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
// Iterate the lines looking for lines that intersect the dirty rect
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
#if XXX
// Stop when we get to a line that's below the dirty rect
if (line->mBounds.y >= aDirtyRect.YMost()) {
break;
}
#endif
// If the line overlaps the dirty rect then iterate the child frames
// and paint those frames that intersect the dirty rect
if (line->mBounds.YMost() > aDirtyRect.y) {
#if XXX
if (line->mBounds.YMost() > aDirtyRect.y)
#endif
{
nsIFrame* kid = line->mFirstChild;
for (PRUint16 i = 0; i < line->mChildCount; i++) {
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, PR_TRUE);

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

@ -21,6 +21,7 @@
#include "nsStyleConsts.h"
#include "nsIFrame.h"
#include "nsIHTMLReflow.h"
#include "nsIContent.h"
nsFrameReflowState::nsFrameReflowState(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState,
@ -64,8 +65,37 @@ nsFrameReflowState::nsFrameReflowState(nsIPresContext& aPresContext,
// Set mDirection value
mDirection = mStyleDisplay->mDirection;
// Initialize running margin value
mRunningMargin = aMetrics.mCarriedInTopMargin;
// See if this container frame will act as a root for margin
// collapsing behavior.
mIsMarginRoot = PR_FALSE;
if ((0 != mBorderPadding.top) || (0 != mBorderPadding.bottom)) {
mIsMarginRoot = PR_TRUE;
}
else {
// A sleazy way to detect a block frame that's acting on behalf of
// another frame to reflow the other frames contents.
// XXX a better solution puhleeze!
nsIFrame* parent;
frame->GetGeometricParent(parent);
if (nsnull != parent) {
nsIContent* parentContent;
parent->GetContent(parentContent);
if (nsnull != parentContent) {
nsIContent* frameContent;
frame->GetContent(frameContent);
if (nsnull != frameContent) {
if (parentContent == frameContent) {
mIsMarginRoot = PR_TRUE;
}
NS_RELEASE(frameContent);
}
NS_RELEASE(parentContent);
}
}
}
mCollapsedTopMargin = 0;
mPrevBottomMargin = 0;
}
nsFrameReflowState::~nsFrameReflowState()

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

@ -75,11 +75,10 @@ public:
// The frames border+padding value
nsMargin mBorderPadding;
// The running margin value. This value is a copy of
// nsReflowMetrics.mCarriedInTopMargin before the first frame is
// reflowed, and after a frame is reflowed and placed it will be the
// collapsed bottom margin value from the frame.
nscoord mRunningMargin;
PRBool mIsMarginRoot;
nscoord mCollapsedTopMargin;
nscoord mPrevBottomMargin;
};
#endif /* nsFrameReflowState_h___ */

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

@ -32,21 +32,13 @@ class nsISpaceManager;
struct nsHTMLReflowMetrics : nsReflowMetrics {
// XXX Explain this better somehow!
// The caller of nsIFrame::Reflow will set these to the top margin
// value carried into the child frame. This allows the the child
// container to collapse the top margin with its first childs
// margin.
nscoord mCarriedInTopMargin; // in
// These values are set by the child frame indicating its final
// inner bottom margin value (the value of the childs last child
// bottom margin)
nscoord mCarriedOutBottomMargin; // out
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
nsHTMLReflowMetrics(nsSize* aMaxElementSize)
: nsReflowMetrics(aMaxElementSize)
{
mCarriedInTopMargin = 0;
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
}
};

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

@ -80,6 +80,13 @@ public:
virtual PRIntn GetSkipSides() const;
PRBool CalculateMargins(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult);
void SlideFrames(nsIFrame* aKid, nscoord aDY);
nsresult InitialReflow(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow);
@ -313,7 +320,6 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
nsHTMLReflowMetrics& aMetrics,
const nsHTMLReflowState& aReflowState)
{
//XXX ListTag(stdout); printf(": enter (runningMargin=%d)\n", aMetrics.mCarriedInTopMargin);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("enter nsInlineFrame::InlineReflow maxSize=%d,%d reason=%d nif=%p",
aReflowState.maxSize.width, aReflowState.maxSize.height,
@ -349,6 +355,7 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
inlineReflow.Init(state.mBorderPadding.left, state.mBorderPadding.top,
width, height);
// ListTag(stdout); printf(": enter isMarginRoot=%c\n", state.mIsMarginRoot?'T':'F');
// Now translate in by our border and padding
aLineLayout.mSpaceManager->Translate(state.mBorderPadding.left,
state.mBorderPadding.top);
@ -413,6 +420,7 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
rv = ResizeReflow(state, inlineReflow);
}
ComputeFinalSize(state, inlineReflow, aMetrics);
// ListTag(stdout); printf(": exit carriedMargins=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedOutBottomMargin);
// Now translate in by our border and padding
aLineLayout.mSpaceManager->Translate(-state.mBorderPadding.left,
@ -421,13 +429,65 @@ nsInlineFrame::InlineReflow(nsLineLayout& aLineLayout,
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("exit nsInlineFrame::InlineReflow size=%d,%d rv=%x nif=%p",
aMetrics.width, aMetrics.height, rv, mNextInFlow));
//XXX ListTag(stdout); printf(": exit (runningMargin=%d)\n", aMetrics.mCarriedOutBottomMargin);
return rv;
}
// XXX factor this (into nsFrameReflowState?) so that both block and
// inline can use most of the same logic
PRBool
nsInlineFrame::CalculateMargins(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
nscoord& aTopMarginResult,
nscoord& aBottomMarginResult)
{
PRBool haveCarriedMargins = PR_FALSE;
nscoord childsCarriedOutTopMargin = aInlineReflow.GetCarriedOutTopMargin();
nscoord childsCarriedOutBottomMargin = aInlineReflow.GetCarriedOutBottomMargin();
// If the inline-reflow is wrapped up around a block or we have some
// carried out margin information then we perform margin collapsing
// (pretending we are a block...)
if (aInlineReflow.GetIsBlock() ||
(0 != childsCarriedOutTopMargin) ||
(0 != childsCarriedOutBottomMargin)) {
// Compute the collapsed top margin value from the childs margin and
// its carried out top margin.
nscoord childsTopMargin = aInlineReflow.GetTopMargin();
nscoord collapsedTopMargin =
PR_MAX(childsCarriedOutTopMargin, childsTopMargin);
// If this frame is a root for margins then we will apply the
// collapsed top margin value ourselves. Otherwise, we pass it out
// to our parent to apply.
if (!aState.mIsMarginRoot) {
// We are not a root for margin collapsing and this is our first
// non-empty-line (with a block child presumably). Keep the
// collapsed margin value around to pass out to our parent. We
// don't apply the margin (our parent will) so zero it out.
aState.mCollapsedTopMargin = collapsedTopMargin;
collapsedTopMargin = 0;
}
aTopMarginResult = collapsedTopMargin;
// Compute the collapsed bottom margin value. This collapsed value
// will end up in aState.mPrevBottomMargin if the child frame ends
// up being placed in this block frame.
nscoord childsBottomMargin = aInlineReflow.GetBottomMargin();
nscoord collapsedBottomMargin =
PR_MAX(childsCarriedOutBottomMargin, childsBottomMargin);
aBottomMarginResult = collapsedBottomMargin;
haveCarriedMargins = PR_TRUE;
}
else {
aTopMarginResult = 0;
aBottomMarginResult = 0;
haveCarriedMargins = PR_FALSE;
}
return haveCarriedMargins;
}
void
nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
@ -457,7 +517,6 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
aMetrics.ascent = 0;
aMetrics.descent = 0;
aMetrics.height = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
// XXX tip of the iceburg: when an inline is wrapping up a block frame
@ -465,17 +524,12 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
// coordinate system; apply horizontal alignment (in case the block
// has a width set), etc. Of course the block code knows how to do
// this already...
//XXXprintf("bounds={%d,%d,%d,%d}\n",
//XXX bounds.x, bounds.y, bounds.width, bounds.height);
if (aInlineReflow.GetIsBlock()) {
// Make sure the blocks top and bottom margins get applied; the
// top margin will be in bounds.y; the bottom margin we get from
// the inline reflow.
nscoord bottomMargin = aInlineReflow.GetBottomMargin();
nscoord newY = aState.mBorderPadding.top + bounds.YMost()
+ bottomMargin + aState.mBorderPadding.bottom;
//XXXprintf("newY=%d bounds={%d,%d,%d,%d} bottomMargin=%d\n", newY,
//XXX bounds.x, bounds.y, bounds.width, bounds.height, bottomMargin);
nscoord newY = aState.mBorderPadding.top + bounds.YMost() +
aState.mBorderPadding.bottom;
aMetrics.height = newY;
aMetrics.ascent = newY;
aMetrics.descent = 0;
@ -487,16 +541,16 @@ nsInlineFrame::ComputeFinalSize(nsInlineReflowState& aState,
aState.mBorderPadding.bottom;
aMetrics.height = aMetrics.ascent + aMetrics.descent;
}
}
// Carry out to the caller the running bottom margin value
if (0 != aState.mBorderPadding.bottom) {
// Don't carry out a child margin when we have a border
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
// Carry out our the total child bottom margin value
aMetrics.mCarriedOutBottomMargin = aState.mRunningMargin;
}
// Return top and bottom margin information
if (aState.mIsMarginRoot) {
aMetrics.mCarriedOutTopMargin = 0;
aMetrics.mCarriedOutBottomMargin = 0;
}
else {
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
}
if (aState.mComputeMaxElementSize) {
@ -680,6 +734,18 @@ nsInlineFrame::PullUpChildren(nsInlineReflowState& aState,
return reflowStatus;
}
void
nsInlineFrame::SlideFrames(nsIFrame* aKid, nscoord aDY)
{
while (nsnull != aKid) {
nsRect r;
aKid->GetRect(r);
r.y += aDY;
aKid->SetRect(r);
aKid->GetNextSibling(aKid);
}
}
PRBool
nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
nsInlineReflow& aInlineReflow,
@ -720,7 +786,20 @@ nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
}
return PR_FALSE;
}
else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) {
// Apply collapsing block margins. However, we only do this if a
// carried out margin was present.
nscoord topMargin, bottomMargin;
if (CalculateMargins(aState, aInlineReflow, topMargin, bottomMargin)) {
if (0 != topMargin) {
SlideFrames(mFirstChild, topMargin);
}
// Record bottom margin value for returning as our carried out
// bottom margin.
aState.mPrevBottomMargin = bottomMargin;
}
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) {
// We may be breaking after a frame here (e.g. a child inline
// frame that contains a BR tag and more content after the BR
// tag). We will propagate that upward so that this frame gets
@ -748,7 +827,8 @@ nsInlineFrame::ReflowFrame(nsInlineReflowState& aState,
}
return PR_FALSE;
}
else if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
// If we are breaking after a child that's complete, we may still
// be incomplete if we have more children that need
// reflowing. Check for this after advancing to the next frame.

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

@ -74,6 +74,8 @@ nsInlineReflow::Init(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight)
else {
mBottomEdge = aY + aHeight;
}
mCarriedOutTopMargin = 0;
mCarriedOutBottomMargin = 0;
mIsBlock = PR_FALSE;
mIsFirstChild = PR_FALSE;
@ -192,8 +194,8 @@ nsInlineReflow::ReflowFrame(nsIFrame* aFrame)
nsReflowStatus result;
nsSize innerMaxElementSize;
nsHTMLReflowMetrics metrics(mComputeMaxElementSize
? &innerMaxElementSize
: nsnull);
? &innerMaxElementSize
: nsnull);
// Prepare for reflowing the frame
SetFrame(aFrame);
@ -239,31 +241,49 @@ void
nsInlineReflow::CalculateMargins()
{
const nsStyleSpacing* spacing = GetSpacing();
// Get the margins from the style system
spacing->CalcMarginFor(mFrame, mMargin);
if (mTreatFrameAsBlock) {
// Get font height if we will be doing an auto margin. We use the
// default font height for the auto margin value.
nsStyleUnit topUnit = spacing->mMargin.GetTopUnit();
nsStyleUnit bottomUnit = spacing->mMargin.GetBottomUnit();
nscoord fontHeight = 0;
if ((eStyleUnit_Auto == topUnit) || (eStyleUnit_Auto == bottomUnit)) {
const nsFont& defaultFont = mPresContext.GetDefaultFont();
nsIFontMetrics* fm = mPresContext.GetMetricsFor(defaultFont);
fm->GetHeight(fontHeight);
NS_RELEASE(fm);
}
// For auto margins use the font height computed above
if (eStyleUnit_Auto == topUnit) {
mMargin.top = fontHeight;
}
if (eStyleUnit_Auto == bottomUnit) {
mMargin.bottom = fontHeight;
}
CalculateBlockMarginsFor(mPresContext, mFrame, spacing, mMargin);
}
else {
// Get the margins from the style system
spacing->CalcMarginFor(mFrame, mMargin);
}
}
void
nsInlineReflow::CalculateBlockMarginsFor(nsIPresContext& aPresContext,
nsIFrame* aFrame,
const nsStyleSpacing* aSpacing,
nsMargin& aMargin)
{
aSpacing->CalcMarginFor(aFrame, aMargin);
// Get font height if we will be doing an auto margin. We use the
// default font height for the auto margin value.
nsStyleUnit topUnit = aSpacing->mMargin.GetTopUnit();
nsStyleUnit bottomUnit = aSpacing->mMargin.GetBottomUnit();
nscoord fontHeight = 0;
if ((eStyleUnit_Auto == topUnit) || (eStyleUnit_Auto == bottomUnit)) {
// XXX Use the font for the frame, not the default font???
const nsFont& defaultFont = aPresContext.GetDefaultFont();
nsIFontMetrics* fm = aPresContext.GetMetricsFor(defaultFont);
fm->GetHeight(fontHeight);
NS_RELEASE(fm);
}
// For auto margins use the font height computed above
if (eStyleUnit_Auto == topUnit) {
aMargin.top = fontHeight;
}
if (eStyleUnit_Auto == bottomUnit) {
aMargin.bottom = fontHeight;
}
// XXX Add in code to provide a zero top margin when the frame is
// the "first" block frame in a margin-root situation.?
// XXX Add in code to provide a zero bottom margin when the frame is
// the "last" block frame in a margin-root situation.?
}
void
@ -291,26 +311,6 @@ nsInlineReflow::ApplyTopLeftMargins()
break;
}
mFrameX += leftMargin;
// Apply top margin only if the child frame is to be treated as a
// block frame. Compute collapsed value to apply.
if (mTreatFrameAsBlock) {
// Collapse childs margin with previous running margin value
nscoord inTopMargin = mOuterReflowState.mRunningMargin;
nscoord deltaMargin = mMargin.top - inTopMargin;
if (deltaMargin < 0) {
deltaMargin = 0;
}
mDeltaTopMargin = deltaMargin;
mTotalTopMargin = mMargin.top + deltaMargin;
// Apply change in margin to this frame's Y coordinate
mFrameY += deltaMargin;
}
else {
mTotalTopMargin = 0;
mDeltaTopMargin = 0;
}
}
PRBool
@ -536,6 +536,15 @@ nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
mMaxDescent = aMetrics.descent;
}
// Compute collapsed margin information
mCarriedOutTopMargin = aMetrics.mCarriedOutTopMargin;
mCarriedOutBottomMargin = aMetrics.mCarriedOutBottomMargin;
#if 0
//XXX
mCarriedOutTopMargin = PR_MAX(mCarriedOutTopMargin, mMargin.top);
mCarriedOutBottomMargin = PR_MAX(mCarriedOutBottomMargin, mMargin.bottom);
#endif
// Advance to next X coordinate
mX = aBounds.XMost() + mRightMargin;
@ -548,20 +557,7 @@ nsInlineReflow::PlaceFrame(nsHTMLReflowMetrics& aMetrics, nsRect& aBounds)
// Compute the bottom margin to apply. Note that the margin only
// applies if the frame ends up with a non-zero height.
mBottomMargin = 0;
mInnerBottomMargin = aMetrics.mCarriedOutBottomMargin;
if (!emptyFrame) {
// Compute the effective amount of bottom margin by collapsing
// it with the inner margin returned from reflowing the frame.
nscoord inBottomMargin = aMetrics.mCarriedOutBottomMargin;
nscoord deltaMargin = mMargin.bottom - inBottomMargin;
if (deltaMargin < 0) {
deltaMargin = 0;
}
mBottomMargin = deltaMargin;/* XXX rename mBottomMargin? */
mInnerBottomMargin = inBottomMargin;/* XXX rename? */
mOuterReflowState.mRunningMargin = inBottomMargin + deltaMargin;
// Inform line layout that we have placed a non-empty frame
#ifdef NS_DEBUG
mLineLayout.AddPlacedFrame(mFrame);
@ -614,7 +610,7 @@ void
nsInlineReflow::VerticalAlignFrames(nsRect& aLineBox)
{
nscoord x = mLeftEdge;
nscoord y0 = mTopEdge + mDeltaTopMargin;
nscoord y0 = mTopEdge;
nscoord width = mX - mLeftEdge;
nscoord height = mMaxAscent + mMaxDescent;

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

@ -64,14 +64,23 @@ public:
PRInt32 GetCurrentFrameNum() const { return mFrameNum; }
nscoord GetInnerBottomMargin() const { return mInnerBottomMargin; }
nscoord GetBottomMargin() const { return mBottomMargin; }
PRBool GetIsBlock() const { return mIsBlock; }
const nsSize& GetMaxElementSize() const { return mMaxElementSize; }
nscoord GetCarriedOutTopMargin() const { return mCarriedOutTopMargin; }
nscoord GetCarriedOutBottomMargin() const { return mCarriedOutBottomMargin; }
nscoord GetTopMargin() const { return mMargin.top; }
nscoord GetBottomMargin() const { return mMargin.bottom; }
static void CalculateBlockMarginsFor(nsIPresContext& aPresContext,
nsIFrame* aFrame,
const nsStyleSpacing* aSpacing,
nsMargin& aMargin);
protected:
void SetFrame(nsIFrame* aFrame);
@ -147,18 +156,9 @@ protected:
// The frame's computed margin values (includes auto value
// computation)
nsMargin mMargin;
// The computed delta top margin that has been applied for the
// frame. This is the collapsed margin value.
nscoord mDeltaTopMargin;
// The sum ofthe previous top margin plus additional margin
// contributed by the frame being reflowed.
nscoord mTotalTopMargin;
nscoord mRightMargin;/* XXX why? */
nscoord mInnerBottomMargin;
nscoord mBottomMargin;
nscoord mRightMargin;/* XXX */
nscoord mCarriedOutTopMargin;
nscoord mCarriedOutBottomMargin;
// The computed available size and location for the frame
nscoord mFrameX, mFrameY;