From 9ede33c69074b53adc23bd0d3f989ffbc3d5a2e6 Mon Sep 17 00:00:00 2001 From: "waterson%netscape.com" Date: Thu, 20 Dec 2001 01:56:17 +0000 Subject: [PATCH] Bug 91423. When recomputing the max width during an inline incremental reflow, we may have to fix-up reflow command chain. Specifically, we may have to replace a continuing frame in the chain with its primary frame, because the continuing frame will be destroyed during the unconstrained reflow used to compute the max width. r=dbaron, sr=attinasi --- layout/generic/nsBlockFrame.cpp | 73 ++++++++++++++++++++-- layout/generic/nsHTMLReflowCommand.h | 8 +++ layout/html/base/src/nsBlockFrame.cpp | 73 ++++++++++++++++++++-- layout/html/base/src/nsHTMLReflowCommand.h | 8 +++ 4 files changed, 150 insertions(+), 12 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 1c0786af63e..f572c8deac9 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -87,7 +87,6 @@ #include "nsPrintfCString.h" #include "nsBlockDebugFlags.h" - PRBool nsBlockFrame::gLamePaintMetrics; PRBool nsBlockFrame::gLameReflowMetrics; PRBool nsBlockFrame::gNoisy; @@ -2375,6 +2374,34 @@ 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 @@ -2482,21 +2509,55 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || !aState.mCurrentLine.prev()->IsLineWrapped(); if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { + // First reflow the line with an unconstrained width. nscoord oldY = aState.mY; nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); - // First reflow the line with an unconstrained width. When doing this - // we need to set the block reflow state's "mUnconstrainedWidth" variable - // to PR_TRUE so if we encounter a placeholder and then reflow its - // associated floater we don't end up resetting the line's right edge and - // have it think the width is unconstrained... + // 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); + } + } + + // When doing this we need to set the block reflow state's + // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter + // a placeholder and then reflow its associated floater we don't + // end up resetting the line's right edge and have it think the + // width is unconstrained... aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); +#ifdef DEBUG_waterson + // XXXwaterson if oldUnconstrainedWidth was set, why do we need + // to do the second reflow, below? + if (oldUnconstrainedWidth) + printf("+++ possibly doing an unnecessary second-pass unconstrained reflow\n"); +#endif + // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); #ifdef NOISY_MAXIMUM_WIDTH diff --git a/layout/generic/nsHTMLReflowCommand.h b/layout/generic/nsHTMLReflowCommand.h index 48188e8f2b9..69d4e039249 100644 --- a/layout/generic/nsHTMLReflowCommand.h +++ b/layout/generic/nsHTMLReflowCommand.h @@ -126,6 +126,14 @@ public: */ nsresult GetAttribute(nsIAtom *& aAttribute) const; + /** + * Return the reflow command's path. The path is stored in + * reverse order in the array; i.e., the first element in + * the array is the target frame, the last element in the array is + * the current frame. + */ + nsVoidArray *GetPath() { return &mPath; } + /** * Get the child frame associated with the reflow command. */ diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp index 1c0786af63e..f572c8deac9 100644 --- a/layout/html/base/src/nsBlockFrame.cpp +++ b/layout/html/base/src/nsBlockFrame.cpp @@ -87,7 +87,6 @@ #include "nsPrintfCString.h" #include "nsBlockDebugFlags.h" - PRBool nsBlockFrame::gLamePaintMetrics; PRBool nsBlockFrame::gLameReflowMetrics; PRBool nsBlockFrame::gNoisy; @@ -2375,6 +2374,34 @@ 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 @@ -2482,21 +2509,55 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || !aState.mCurrentLine.prev()->IsLineWrapped(); if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { + // First reflow the line with an unconstrained width. nscoord oldY = aState.mY; nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); - // First reflow the line with an unconstrained width. When doing this - // we need to set the block reflow state's "mUnconstrainedWidth" variable - // to PR_TRUE so if we encounter a placeholder and then reflow its - // associated floater we don't end up resetting the line's right edge and - // have it think the width is unconstrained... + // 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); + } + } + + // When doing this we need to set the block reflow state's + // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter + // a placeholder and then reflow its associated floater we don't + // end up resetting the line's right edge and have it think the + // width is unconstrained... aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE); aState.mY = oldY; aState.mPrevBottomMargin = oldPrevBottomMargin; aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); +#ifdef DEBUG_waterson + // XXXwaterson if oldUnconstrainedWidth was set, why do we need + // to do the second reflow, below? + if (oldUnconstrainedWidth) + printf("+++ possibly doing an unnecessary second-pass unconstrained reflow\n"); +#endif + // Update the line's maximum width aLine->mMaximumWidth = aLine->mBounds.XMost(); #ifdef NOISY_MAXIMUM_WIDTH diff --git a/layout/html/base/src/nsHTMLReflowCommand.h b/layout/html/base/src/nsHTMLReflowCommand.h index 48188e8f2b9..69d4e039249 100644 --- a/layout/html/base/src/nsHTMLReflowCommand.h +++ b/layout/html/base/src/nsHTMLReflowCommand.h @@ -126,6 +126,14 @@ public: */ nsresult GetAttribute(nsIAtom *& aAttribute) const; + /** + * Return the reflow command's path. The path is stored in + * reverse order in the array; i.e., the first element in + * the array is the target frame, the last element in the array is + * the current frame. + */ + nsVoidArray *GetPath() { return &mPath; } + /** * Get the child frame associated with the reflow command. */