From 7ecb959a881328ae1576e896625300f581b5d1dd Mon Sep 17 00:00:00 2001 From: "troy%netscape.com" Date: Sat, 8 Jan 2000 03:58:27 +0000 Subject: [PATCH] More work on getting block frames to incrementally update their maximum width git-svn-id: svn://10.0.0.236/trunk@57170 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/generic/nsBlockFrame.cpp | 103 +++++++++++++++--- mozilla/layout/generic/nsBlockFrame.h | 15 ++- .../layout/generic/nsBlockReflowContext.cpp | 21 ++++ mozilla/layout/generic/nsBlockReflowState.cpp | 103 +++++++++++++++--- mozilla/layout/generic/nsBlockReflowState.h | 103 +++++++++++++++--- mozilla/layout/generic/nsLineBox.cpp | 17 +++ mozilla/layout/generic/nsLineBox.h | 1 + mozilla/layout/html/base/src/nsBlockFrame.cpp | 103 +++++++++++++++--- mozilla/layout/html/base/src/nsBlockFrame.h | 15 ++- .../html/base/src/nsBlockReflowContext.cpp | 21 ++++ .../html/base/src/nsBlockReflowState.cpp | 103 +++++++++++++++--- .../layout/html/base/src/nsBlockReflowState.h | 103 +++++++++++++++--- mozilla/layout/html/base/src/nsLineBox.cpp | 17 +++ mozilla/layout/html/base/src/nsLineBox.h | 1 + 14 files changed, 632 insertions(+), 94 deletions(-) diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index 8c8e941a339..6a054ff44ba 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -205,7 +205,8 @@ protected: nsresult PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing); + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth); // XXX blech void PostPlaceLine(nsBlockReflowState& aState, @@ -233,23 +234,27 @@ protected: nsresult ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepLineGoing); + PRBool* aKeepLineGoing, + PRBool aUpdateMaximumWidth = PR_FALSE); nsresult DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp index 5fbfe4f2f6c..62957768abc 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.cpp +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -262,6 +262,27 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, nscoord tx = x - mOuterReflowState.mComputedBorderPadding.left; nscoord ty = y - mOuterReflowState.mComputedBorderPadding.top; mOuterReflowState.mSpaceManager->Translate(tx, ty); + + // See if this is the child's initial reflow and we are supposed to + // compute our maximum width + if (mComputeMaximumWidth && (eReflowReason_Initial == reason)) { + nscoord oldAvailableWidth = reflowState.availableWidth; + nscoord oldComputedWidth = reflowState.mComputedWidth; + + reflowState.availableWidth = NS_UNCONSTRAINEDSIZE; + reflowState.mComputedWidth = NS_UNCONSTRAINEDSIZE; + rv = aFrame->Reflow(mPresContext, mMetrics, reflowState, + aFrameReflowStatus); + + // Update the reflow metrics with the maximum width + mMetrics.mMaximumWidth = mMetrics.width; + + // The second reflow is just as a resize reflow with the constrained + // width + reflowState.availableWidth = oldAvailableWidth; + reflowState.mComputedWidth = oldComputedWidth; + reason = eReflowReason_Resize; + } rv = aFrame->Reflow(mPresContext, mMetrics, reflowState, aFrameReflowStatus); mOuterReflowState.mSpaceManager->Translate(-tx, -ty); diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/generic/nsLineBox.cpp b/mozilla/layout/generic/nsLineBox.cpp index e1b886df682..b5870f7c9a6 100644 --- a/mozilla/layout/generic/nsLineBox.cpp +++ b/mozilla/layout/generic/nsLineBox.cpp @@ -333,6 +333,23 @@ nsLineBox::FreeFloaters(nsFloaterCacheFreeList& aFreeList) } } +void +nsLineBox::RemoveFloatersFromSpaceManager(nsISpaceManager* aSpaceManager) +{ + if (IsInline()) { + if (mInlineData) { + nsFloaterCache* floaterCache = mInlineData->mFloaters.Head(); + + while (floaterCache) { + nsIFrame* floater = floaterCache->mPlaceholder->GetOutOfFlowFrame(); + + aSpaceManager->RemoveRegion(floater); + floaterCache = floaterCache->Next(); + } + } + } +} + void nsLineBox::AppendFloaters(nsFloaterCacheFreeList& aFreeList) { diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h index 8cfcfc15190..6d963f56308 100644 --- a/mozilla/layout/generic/nsLineBox.h +++ b/mozilla/layout/generic/nsLineBox.h @@ -243,6 +243,7 @@ public: void FreeFloaters(nsFloaterCacheFreeList& aFreeList); void AppendFloaters(nsFloaterCacheFreeList& aFreeList); PRBool RemoveFloater(nsIFrame* aFrame); + void RemoveFloatersFromSpaceManager(nsISpaceManager* aSpaceManager); // Combined area void SetCombinedArea(const nsRect& aCombinedArea); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.h b/mozilla/layout/html/base/src/nsBlockFrame.h index 8c8e941a339..6a054ff44ba 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.h +++ b/mozilla/layout/html/base/src/nsBlockFrame.h @@ -205,7 +205,8 @@ protected: nsresult PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing); + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth); // XXX blech void PostPlaceLine(nsBlockReflowState& aState, @@ -233,23 +234,27 @@ protected: nsresult ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepLineGoing); + PRBool* aKeepLineGoing, + PRBool aUpdateMaximumWidth = PR_FALSE); nsresult DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus); + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth); nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp index 5fbfe4f2f6c..62957768abc 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp @@ -262,6 +262,27 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, nscoord tx = x - mOuterReflowState.mComputedBorderPadding.left; nscoord ty = y - mOuterReflowState.mComputedBorderPadding.top; mOuterReflowState.mSpaceManager->Translate(tx, ty); + + // See if this is the child's initial reflow and we are supposed to + // compute our maximum width + if (mComputeMaximumWidth && (eReflowReason_Initial == reason)) { + nscoord oldAvailableWidth = reflowState.availableWidth; + nscoord oldComputedWidth = reflowState.mComputedWidth; + + reflowState.availableWidth = NS_UNCONSTRAINEDSIZE; + reflowState.mComputedWidth = NS_UNCONSTRAINEDSIZE; + rv = aFrame->Reflow(mPresContext, mMetrics, reflowState, + aFrameReflowStatus); + + // Update the reflow metrics with the maximum width + mMetrics.mMaximumWidth = mMetrics.width; + + // The second reflow is just as a resize reflow with the constrained + // width + reflowState.availableWidth = oldAvailableWidth; + reflowState.mComputedWidth = oldComputedWidth; + reason = eReflowReason_Resize; + } rv = aFrame->Reflow(mPresContext, mMetrics, reflowState, aFrameReflowStatus); mOuterReflowState.mSpaceManager->Translate(-tx, -ty); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index e52bae25b90..1e3e90ac521 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -2442,6 +2442,27 @@ nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState, } } +static PRBool +WrappedLinesAreDirty(nsLineBox* aLine) +{ + if (aLine->IsInline()) { + while (aLine->IsLineWrapped()) { + aLine = aLine->mNext; + if (!aLine) { + break; + } + + NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line"); + if (aLine->IsDirty()) { + // we found a continuing line that is dirty + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + /** * Reflow the dirty lines */ @@ -2495,7 +2516,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } #endif - if (line->IsDirty()) { + // If we're supposed to update our maximum width, then we'll also need to + // reflow this line if it's line wrapped and any of the continuing lines + // are dirty + if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) { // Compute the dirty lines "before" YMost, after factoring in // the running deltaY value - the running value is implicit in // aState.mY. @@ -2746,7 +2770,40 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } else { aLine->SetLineWrapped(PR_FALSE); - rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + // If we're supposed to update the maximum width, then we'll need to reflow + // the line with an unconstrained width (which will give us the new maximum + // width), then we'll reflow it again with the constrained width. + // We only do this if this is a beginning line, i.e., don't do this for + // lines associated with content that line wrapped (see ReflowDirtyLines() + // for details). + // XXX This approach doesn't work when floaters are involved in which case + // we'll either need to recover the floater state that applies to the + // unconstrained reflow or keep it around in a separate space manager... + if (aState.mComputeMaximumWidth && aState.mPrevLine && !aState.mPrevLine->IsLineWrapped()) { + nscoord oldY = aState.mY; + nscoord oldPrevBottomMargin = aState.mPrevBottomMargin; + + // First reflow the line with an unconstrained width + ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE); + + // Update the line's maximum width + aLine->mMaximumWidth = aLine->mBounds.XMost(); + aState.UpdateMaximumWidth(aLine->mMaximumWidth); + + // Remove any floaters associated with the line from the space + // manager + aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager); + + // Now reflow the line again this time without having it compute + // the maximum width + aState.mY = oldY; + aState.mPrevBottomMargin = oldPrevBottomMargin; + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + + } else { + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); + } // We don't really know what changed in the line, so use the union // of the old and new combined areas @@ -3545,7 +3602,8 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsresult nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; *aKeepReflowGoing = PR_TRUE; @@ -3560,11 +3618,13 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, // large. if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h? rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } else { rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing, - &lineReflowStatus); + &lineReflowStatus, + aUpdateMaximumWidth); } if (NS_FAILED(rv)) { break; @@ -3585,7 +3645,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout* ll = new nsLineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3597,7 +3658,7 @@ nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState, ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber); ll->SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing, - aLineReflowStatus); + aLineReflowStatus, aUpdateMaximumWidth); ll->EndLineReflow(); delete ll; return rv; @@ -3607,7 +3668,8 @@ nsresult nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { nsLineLayout lineLayout(aState.mPresContext, aState.mReflowState.mSpaceManager, @@ -3616,17 +3678,19 @@ nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState, lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); lineLayout.SetReflowTextRuns(mTextRuns); nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine, - aKeepReflowGoing, aLineReflowStatus); + aKeepReflowGoing, aLineReflowStatus, + aUpdateMaximumWidth); lineLayout.EndLineReflow(); return rv; } - + nsresult nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, PRBool* aKeepReflowGoing, - PRUint8* aLineReflowStatus) + PRUint8* aLineReflowStatus, + PRBool aUpdateMaximumWidth) { // Forget all of the floaters on the line aLine->FreeFloaters(aState.mFloaterCacheFreeList); @@ -3651,6 +3715,9 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, /* XXX get the height right! */ availHeight = aState.mAvailSpaceRect.height; } + if (aUpdateMaximumWidth) { + availWidth = NS_UNCONSTRAINEDSIZE; + } aLineLayout.BeginLineReflow(x, aState.mY, availWidth, availHeight, impactedByFloaters, @@ -3742,7 +3809,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, // If we are propogating out a break-before status then there is // no point in placing the line. if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { - rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing); + rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth); } } *aLineReflowStatus = lineReflowStatus; @@ -4044,7 +4111,8 @@ nsresult nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, nsLineBox* aLine, - PRBool* aKeepReflowGoing) + PRBool* aKeepReflowGoing, + PRBool aUpdateMaximumWidth) { nsresult rv = NS_OK; @@ -4190,7 +4258,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, ComputeLineMaxElementSize(aState, aLine, &maxElementSize); } } - PostPlaceLine(aState, aLine, maxElementSize); + + // If we're reflowing the line just to get incrementally update the + // maximum width, then don't post-place the line. It's doing work we + // don't need, and it will update things like aState.mKidXMost that + // we don't want updated... + if (!aUpdateMaximumWidth) { + PostPlaceLine(aState, aLine, maxElementSize); + } // Add the already placed current-line floaters to the line aLine->AppendFloaters(aState.mCurrentLineFloaters); diff --git a/mozilla/layout/html/base/src/nsLineBox.cpp b/mozilla/layout/html/base/src/nsLineBox.cpp index e1b886df682..b5870f7c9a6 100644 --- a/mozilla/layout/html/base/src/nsLineBox.cpp +++ b/mozilla/layout/html/base/src/nsLineBox.cpp @@ -333,6 +333,23 @@ nsLineBox::FreeFloaters(nsFloaterCacheFreeList& aFreeList) } } +void +nsLineBox::RemoveFloatersFromSpaceManager(nsISpaceManager* aSpaceManager) +{ + if (IsInline()) { + if (mInlineData) { + nsFloaterCache* floaterCache = mInlineData->mFloaters.Head(); + + while (floaterCache) { + nsIFrame* floater = floaterCache->mPlaceholder->GetOutOfFlowFrame(); + + aSpaceManager->RemoveRegion(floater); + floaterCache = floaterCache->Next(); + } + } + } +} + void nsLineBox::AppendFloaters(nsFloaterCacheFreeList& aFreeList) { diff --git a/mozilla/layout/html/base/src/nsLineBox.h b/mozilla/layout/html/base/src/nsLineBox.h index 8cfcfc15190..6d963f56308 100644 --- a/mozilla/layout/html/base/src/nsLineBox.h +++ b/mozilla/layout/html/base/src/nsLineBox.h @@ -243,6 +243,7 @@ public: void FreeFloaters(nsFloaterCacheFreeList& aFreeList); void AppendFloaters(nsFloaterCacheFreeList& aFreeList); PRBool RemoveFloater(nsIFrame* aFrame); + void RemoveFloatersFromSpaceManager(nsISpaceManager* aSpaceManager); // Combined area void SetCombinedArea(const nsRect& aCombinedArea);