From e2c4e6e97d982a90f0f51cc362ee2c0146cd1ee5 Mon Sep 17 00:00:00 2001 From: "waterson%netscape.com" Date: Sat, 5 Jan 2002 01:42:36 +0000 Subject: [PATCH] Bug 116230 et al; fix-on-a-fix for bug 91423. Don't always retarget to the primary frame: stop at the first continuation after a hard-break if there is one. Also, move retargeting to reflow state setup. r=rbs, sr=nobody. --- layout/generic/nsBlockFrame.cpp | 148 +++++++++++++++++--------- layout/generic/nsBlockFrame.h | 22 ++++ layout/html/base/src/nsBlockFrame.cpp | 148 +++++++++++++++++--------- layout/html/base/src/nsBlockFrame.h | 22 ++++ 4 files changed, 234 insertions(+), 106 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 2173114ad3d..0c11c81f6ac 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -1566,6 +1566,26 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) if (line->IsInline()) { aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); + + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { + // We've been asked to compute the maximum width of the block + // frame, which ReflowLine() will handle this by performing an + // unconstrained reflow on the line. If this incremental reflow + // is targeted at a continuing frame, we may have to retarget + // it, as the unconstrained reflow can destroy some of the + // continuations. + // + // XXXwaterson if we implement some sort of ``reflow root'' + // frame, rather than requiring incremental reflows to always + // walk from the real root frame, this code may not be needed + // anymore. Or will it? + NS_ASSERTION(aState.mNextRCFrame, "aState has no mNextRCFrame"); + + nsIFrame *prevInFlow; + aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); + if (prevInFlow) + RetargetInlineIncrementalReflow(aState, line, prevInFlow); + } } // Just mark this line dirty. We never need to mark the @@ -1579,6 +1599,77 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) return NS_OK; } +void +nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState, + line_iterator &aLine, + nsIFrame *aPrevInFlow) +{ + // To retarget the reflow, we'll walk back through the continuations + // until we reach the primary frame, or we reach a continuation that + // is preceded by a ``hard'' line break. + NS_ASSERTION(aLine->Contains(aState.mNextRCFrame), + "line doesn't contain the target of the incremental reflow"); + + // Now fix aState's mNextRCFrame, keeping track of how many lines we + // walk back through. + PRInt32 lineCount = 0; + do { + // XXX this might happen if the block is split; e.g., + // printing or print preview. For now, panic. + NS_ASSERTION(aLine != begin_lines(), + "ran out of lines before we ran out of prev-in-flows"); + + // Is the previous line a ``hard'' break? If so, stop: these + // continuations will be preserved during an unconstrained reflow. + // XXXwaterson should this be `!= NS_STYLE_CLEAR_NONE'? + --aLine; + if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE) + break; + + aState.mNextRCFrame = aPrevInFlow; + aState.mNextRCFrame->GetPrevInFlow(&aPrevInFlow); + +#ifdef DEBUG + // Paranoia. Ensure that the prev-in-flow is really in the + // previous line. + PRBool dummy; + line_iterator check; + FindLineFor(aState.mNextRCFrame, &dummy, &check); + NS_ASSERTION(check == aLine, "prev-in-flow not in previous linebox"); +#endif + + ++lineCount; + } while (aPrevInFlow); + + if (lineCount > 0) { + // Fix any frames deeper in the reflow path. + + // Get the reflow path, which is stored as a stack (i.e., the next + // frame in the reflow is at the _end_ of the array). + nsVoidArray *path = aState.mReflowState.reflowCommand->GetPath(); + + for (PRInt32 i = path->Count() - 1; i >= 0; --i) { + nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i)); + + // Stop if we encounter a non-inline frame in the reflow path. + const nsStyleDisplay *display; + ::GetStyleData(frame, &display); + + if (NS_STYLE_DISPLAY_INLINE != display->mDisplay) + break; + + // Walk back to the primary frame. + PRInt32 count = lineCount; + nsIFrame *prevInFlow; + do { + frame->GetPrevInFlow(&prevInFlow); + } while (--count >= 0 && prevInFlow && (frame = prevInFlow)); + + path->ReplaceElementAt(frame, i); + } + } +} + nsresult nsBlockFrame::MarkLineDirty(line_iterator aLine) { @@ -2374,34 +2465,6 @@ nsBlockFrame::DeleteLine(nsBlockReflowState& aState, } } -static void -RetargetReflowToPrimaryFrames(nsHTMLReflowCommand *aReflowCommand) -{ - // Get the reflow path, which is stored as a stack (i.e., the next - // frame in the reflow is at the _end_ of the array). - nsVoidArray *path = aReflowCommand->GetPath(); - - for (PRInt32 i = path->Count() - 1; i >= 0; --i) { - nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i)); - - // Stop if we encounter a non-inline frame in the reflow path. - const nsStyleDisplay *display; - GetStyleData(frame, &display); - - if (NS_STYLE_DISPLAY_INLINE != display->mDisplay) - break; - - // Walk back to the primary frame. - nsIFrame *prevFrame; - do { - frame->GetPrevInFlow(&prevFrame); - } while (prevFrame && (frame = prevFrame)); - - path->ReplaceElementAt(frame, i); - } -} - - /** * Reflow a line. The line will either contain a single block frame * or contain 1 or more inline frames. aKeepReflowGoing indicates @@ -2514,31 +2577,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); - // If this incremental reflow is targeted at a continuing frame, - // we've got to retarget it to the primary frame. The - // unconstrained reflow will destroy all of the continuations. - // - // XXXwaterson if we implement some sort of ``reflow root'' - // frame, rather than requiring incremental reflows to always - // walk from the real root frame, this code may not be needed - // anymore. Or will it? - if (aState.mNextRCFrame) { - NS_ASSERTION(aState.GetFlag(BRS_ISINLINEINCRREFLOW), - "expected to be inline incremental reflow"); - - nsIFrame *prevInFlow; - aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); - if (prevInFlow) { - // Fix aState's mNextRCFrame - while (prevInFlow) { - aState.mNextRCFrame = prevInFlow; - aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); - } - - // Fix any frames deeper in the reflow path. - RetargetReflowToPrimaryFrames(aState.mReflowState.reflowCommand); - } - } +#ifdef DEBUG_waterson + if (oldUnconstrainedWidth) + printf("*** oldUnconstrainedWidth was already set.\n"); +#endif // When doing this we need to set the block reflow state's // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 9b07e0ff03a..d4d98fddd16 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -295,6 +295,28 @@ protected: */ nsresult PrepareChildIncrementalReflow(nsBlockReflowState& aState); + /** + * Retarget an inline incremental reflow from continuing frames that + * will be destroyed. + * + * @param aState |aState.mNextRCFrame| contains the next frame in + * the reflow path; this will be ``rewound'' to either the target + * frame's primary frame, or to the first continuation frame after a + * ``hard break''. In other words, it will be set to the closest + * continuation which will not be destroyed by the unconstrained + * reflow. The remaining frames in the reflow path for + * |aState.mReflowState.reflowCommand| will be altered similarly. + * + * @param aLine is initially the line box that contains the target + * frame. It will be ``rewound'' in lockstep with + * |aState.mNextRCFrame|. + * + * @param aPrevInFlow points to the target frame's prev-in-flow. + */ + void RetargetInlineIncrementalReflow(nsBlockReflowState &aState, + line_iterator &aLine, + nsIFrame *aPrevInFlow); + /** set up the conditions necessary for an resize reflow * the primary task is to mark the minimumly sufficient lines dirty. */ diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp index 2173114ad3d..0c11c81f6ac 100644 --- a/layout/html/base/src/nsBlockFrame.cpp +++ b/layout/html/base/src/nsBlockFrame.cpp @@ -1566,6 +1566,26 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) if (line->IsInline()) { aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); + + if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) { + // We've been asked to compute the maximum width of the block + // frame, which ReflowLine() will handle this by performing an + // unconstrained reflow on the line. If this incremental reflow + // is targeted at a continuing frame, we may have to retarget + // it, as the unconstrained reflow can destroy some of the + // continuations. + // + // XXXwaterson if we implement some sort of ``reflow root'' + // frame, rather than requiring incremental reflows to always + // walk from the real root frame, this code may not be needed + // anymore. Or will it? + NS_ASSERTION(aState.mNextRCFrame, "aState has no mNextRCFrame"); + + nsIFrame *prevInFlow; + aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); + if (prevInFlow) + RetargetInlineIncrementalReflow(aState, line, prevInFlow); + } } // Just mark this line dirty. We never need to mark the @@ -1579,6 +1599,77 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState) return NS_OK; } +void +nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState, + line_iterator &aLine, + nsIFrame *aPrevInFlow) +{ + // To retarget the reflow, we'll walk back through the continuations + // until we reach the primary frame, or we reach a continuation that + // is preceded by a ``hard'' line break. + NS_ASSERTION(aLine->Contains(aState.mNextRCFrame), + "line doesn't contain the target of the incremental reflow"); + + // Now fix aState's mNextRCFrame, keeping track of how many lines we + // walk back through. + PRInt32 lineCount = 0; + do { + // XXX this might happen if the block is split; e.g., + // printing or print preview. For now, panic. + NS_ASSERTION(aLine != begin_lines(), + "ran out of lines before we ran out of prev-in-flows"); + + // Is the previous line a ``hard'' break? If so, stop: these + // continuations will be preserved during an unconstrained reflow. + // XXXwaterson should this be `!= NS_STYLE_CLEAR_NONE'? + --aLine; + if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE) + break; + + aState.mNextRCFrame = aPrevInFlow; + aState.mNextRCFrame->GetPrevInFlow(&aPrevInFlow); + +#ifdef DEBUG + // Paranoia. Ensure that the prev-in-flow is really in the + // previous line. + PRBool dummy; + line_iterator check; + FindLineFor(aState.mNextRCFrame, &dummy, &check); + NS_ASSERTION(check == aLine, "prev-in-flow not in previous linebox"); +#endif + + ++lineCount; + } while (aPrevInFlow); + + if (lineCount > 0) { + // Fix any frames deeper in the reflow path. + + // Get the reflow path, which is stored as a stack (i.e., the next + // frame in the reflow is at the _end_ of the array). + nsVoidArray *path = aState.mReflowState.reflowCommand->GetPath(); + + for (PRInt32 i = path->Count() - 1; i >= 0; --i) { + nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i)); + + // Stop if we encounter a non-inline frame in the reflow path. + const nsStyleDisplay *display; + ::GetStyleData(frame, &display); + + if (NS_STYLE_DISPLAY_INLINE != display->mDisplay) + break; + + // Walk back to the primary frame. + PRInt32 count = lineCount; + nsIFrame *prevInFlow; + do { + frame->GetPrevInFlow(&prevInFlow); + } while (--count >= 0 && prevInFlow && (frame = prevInFlow)); + + path->ReplaceElementAt(frame, i); + } + } +} + nsresult nsBlockFrame::MarkLineDirty(line_iterator aLine) { @@ -2374,34 +2465,6 @@ nsBlockFrame::DeleteLine(nsBlockReflowState& aState, } } -static void -RetargetReflowToPrimaryFrames(nsHTMLReflowCommand *aReflowCommand) -{ - // Get the reflow path, which is stored as a stack (i.e., the next - // frame in the reflow is at the _end_ of the array). - nsVoidArray *path = aReflowCommand->GetPath(); - - for (PRInt32 i = path->Count() - 1; i >= 0; --i) { - nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i)); - - // Stop if we encounter a non-inline frame in the reflow path. - const nsStyleDisplay *display; - GetStyleData(frame, &display); - - if (NS_STYLE_DISPLAY_INLINE != display->mDisplay) - break; - - // Walk back to the primary frame. - nsIFrame *prevFrame; - do { - frame->GetPrevInFlow(&prevFrame); - } while (prevFrame && (frame = prevFrame)); - - path->ReplaceElementAt(frame, i); - } -} - - /** * Reflow a line. The line will either contain a single block frame * or contain 1 or more inline frames. aKeepReflowGoing indicates @@ -2514,31 +2577,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); - // If this incremental reflow is targeted at a continuing frame, - // we've got to retarget it to the primary frame. The - // unconstrained reflow will destroy all of the continuations. - // - // XXXwaterson if we implement some sort of ``reflow root'' - // frame, rather than requiring incremental reflows to always - // walk from the real root frame, this code may not be needed - // anymore. Or will it? - if (aState.mNextRCFrame) { - NS_ASSERTION(aState.GetFlag(BRS_ISINLINEINCRREFLOW), - "expected to be inline incremental reflow"); - - nsIFrame *prevInFlow; - aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); - if (prevInFlow) { - // Fix aState's mNextRCFrame - while (prevInFlow) { - aState.mNextRCFrame = prevInFlow; - aState.mNextRCFrame->GetPrevInFlow(&prevInFlow); - } - - // Fix any frames deeper in the reflow path. - RetargetReflowToPrimaryFrames(aState.mReflowState.reflowCommand); - } - } +#ifdef DEBUG_waterson + if (oldUnconstrainedWidth) + printf("*** oldUnconstrainedWidth was already set.\n"); +#endif // When doing this we need to set the block reflow state's // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter diff --git a/layout/html/base/src/nsBlockFrame.h b/layout/html/base/src/nsBlockFrame.h index 9b07e0ff03a..d4d98fddd16 100644 --- a/layout/html/base/src/nsBlockFrame.h +++ b/layout/html/base/src/nsBlockFrame.h @@ -295,6 +295,28 @@ protected: */ nsresult PrepareChildIncrementalReflow(nsBlockReflowState& aState); + /** + * Retarget an inline incremental reflow from continuing frames that + * will be destroyed. + * + * @param aState |aState.mNextRCFrame| contains the next frame in + * the reflow path; this will be ``rewound'' to either the target + * frame's primary frame, or to the first continuation frame after a + * ``hard break''. In other words, it will be set to the closest + * continuation which will not be destroyed by the unconstrained + * reflow. The remaining frames in the reflow path for + * |aState.mReflowState.reflowCommand| will be altered similarly. + * + * @param aLine is initially the line box that contains the target + * frame. It will be ``rewound'' in lockstep with + * |aState.mNextRCFrame|. + * + * @param aPrevInFlow points to the target frame's prev-in-flow. + */ + void RetargetInlineIncrementalReflow(nsBlockReflowState &aState, + line_iterator &aLine, + nsIFrame *aPrevInFlow); + /** set up the conditions necessary for an resize reflow * the primary task is to mark the minimumly sufficient lines dirty. */