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

This commit is contained in:
waterson%netscape.com 2001-12-20 01:56:17 +00:00
Родитель 1c3c5dd35a
Коммит 9ede33c690
4 изменённых файлов: 150 добавлений и 12 удалений

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

@ -87,7 +87,6 @@
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "nsBlockDebugFlags.h" #include "nsBlockDebugFlags.h"
PRBool nsBlockFrame::gLamePaintMetrics; PRBool nsBlockFrame::gLamePaintMetrics;
PRBool nsBlockFrame::gLameReflowMetrics; PRBool nsBlockFrame::gLameReflowMetrics;
PRBool nsBlockFrame::gNoisy; 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 * Reflow a line. The line will either contain a single block frame
* or contain 1 or more inline frames. aKeepReflowGoing indicates * or contain 1 or more inline frames. aKeepReflowGoing indicates
@ -2482,21 +2509,55 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || PRBool isBeginningLine = aState.mCurrentLine == begin_lines() ||
!aState.mCurrentLine.prev()->IsLineWrapped(); !aState.mCurrentLine.prev()->IsLineWrapped();
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) {
// First reflow the line with an unconstrained width.
nscoord oldY = aState.mY; nscoord oldY = aState.mY;
nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin);
PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH);
// First reflow the line with an unconstrained width. When doing this // If this incremental reflow is targeted at a continuing frame,
// we need to set the block reflow state's "mUnconstrainedWidth" variable // we've got to retarget it to the primary frame. The
// to PR_TRUE so if we encounter a placeholder and then reflow its // unconstrained reflow will destroy all of the continuations.
// associated floater we don't end up resetting the line's right edge and //
// have it think the width is unconstrained... // 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); aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE);
ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE);
aState.mY = oldY; aState.mY = oldY;
aState.mPrevBottomMargin = oldPrevBottomMargin; aState.mPrevBottomMargin = oldPrevBottomMargin;
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); 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 // Update the line's maximum width
aLine->mMaximumWidth = aLine->mBounds.XMost(); aLine->mMaximumWidth = aLine->mBounds.XMost();
#ifdef NOISY_MAXIMUM_WIDTH #ifdef NOISY_MAXIMUM_WIDTH

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

@ -126,6 +126,14 @@ public:
*/ */
nsresult GetAttribute(nsIAtom *& aAttribute) const; nsresult GetAttribute(nsIAtom *& aAttribute) const;
/**
* Return the reflow command's path. The path is stored in
* <em>reverse</em> 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. * Get the child frame associated with the reflow command.
*/ */

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

@ -87,7 +87,6 @@
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "nsBlockDebugFlags.h" #include "nsBlockDebugFlags.h"
PRBool nsBlockFrame::gLamePaintMetrics; PRBool nsBlockFrame::gLamePaintMetrics;
PRBool nsBlockFrame::gLameReflowMetrics; PRBool nsBlockFrame::gLameReflowMetrics;
PRBool nsBlockFrame::gNoisy; 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 * Reflow a line. The line will either contain a single block frame
* or contain 1 or more inline frames. aKeepReflowGoing indicates * or contain 1 or more inline frames. aKeepReflowGoing indicates
@ -2482,21 +2509,55 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
PRBool isBeginningLine = aState.mCurrentLine == begin_lines() || PRBool isBeginningLine = aState.mCurrentLine == begin_lines() ||
!aState.mCurrentLine.prev()->IsLineWrapped(); !aState.mCurrentLine.prev()->IsLineWrapped();
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) { if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) {
// First reflow the line with an unconstrained width.
nscoord oldY = aState.mY; nscoord oldY = aState.mY;
nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin);
PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH);
// First reflow the line with an unconstrained width. When doing this // If this incremental reflow is targeted at a continuing frame,
// we need to set the block reflow state's "mUnconstrainedWidth" variable // we've got to retarget it to the primary frame. The
// to PR_TRUE so if we encounter a placeholder and then reflow its // unconstrained reflow will destroy all of the continuations.
// associated floater we don't end up resetting the line's right edge and //
// have it think the width is unconstrained... // 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); aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE);
ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE); ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE);
aState.mY = oldY; aState.mY = oldY;
aState.mPrevBottomMargin = oldPrevBottomMargin; aState.mPrevBottomMargin = oldPrevBottomMargin;
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth); 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 // Update the line's maximum width
aLine->mMaximumWidth = aLine->mBounds.XMost(); aLine->mMaximumWidth = aLine->mBounds.XMost();
#ifdef NOISY_MAXIMUM_WIDTH #ifdef NOISY_MAXIMUM_WIDTH

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

@ -126,6 +126,14 @@ public:
*/ */
nsresult GetAttribute(nsIAtom *& aAttribute) const; nsresult GetAttribute(nsIAtom *& aAttribute) const;
/**
* Return the reflow command's path. The path is stored in
* <em>reverse</em> 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. * Get the child frame associated with the reflow command.
*/ */