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.

This commit is contained in:
waterson%netscape.com 2002-01-05 01:42:36 +00:00
Родитель de7da7a8e5
Коммит e2c4e6e97d
4 изменённых файлов: 234 добавлений и 106 удалений

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

@ -1566,6 +1566,26 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
if (line->IsInline()) { if (line->IsInline()) {
aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); 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 // Just mark this line dirty. We never need to mark the
@ -1579,6 +1599,77 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
return NS_OK; 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 nsresult
nsBlockFrame::MarkLineDirty(line_iterator aLine) 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 * 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
@ -2514,31 +2577,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin);
PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH);
// If this incremental reflow is targeted at a continuing frame, #ifdef DEBUG_waterson
// we've got to retarget it to the primary frame. The if (oldUnconstrainedWidth)
// unconstrained reflow will destroy all of the continuations. printf("*** oldUnconstrainedWidth was already set.\n");
// #endif
// 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 // When doing this we need to set the block reflow state's
// "mUnconstrainedWidth" variable to PR_TRUE so if we encounter // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter

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

@ -295,6 +295,28 @@ protected:
*/ */
nsresult PrepareChildIncrementalReflow(nsBlockReflowState& aState); 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 /** set up the conditions necessary for an resize reflow
* the primary task is to mark the minimumly sufficient lines dirty. * the primary task is to mark the minimumly sufficient lines dirty.
*/ */

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

@ -1566,6 +1566,26 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
if (line->IsInline()) { if (line->IsInline()) {
aState.SetFlag(BRS_ISINLINEINCRREFLOW, PR_TRUE); 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 // Just mark this line dirty. We never need to mark the
@ -1579,6 +1599,77 @@ nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
return NS_OK; 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 nsresult
nsBlockFrame::MarkLineDirty(line_iterator aLine) 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 * 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
@ -2514,31 +2577,10 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin); nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin);
PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH); PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH);
// If this incremental reflow is targeted at a continuing frame, #ifdef DEBUG_waterson
// we've got to retarget it to the primary frame. The if (oldUnconstrainedWidth)
// unconstrained reflow will destroy all of the continuations. printf("*** oldUnconstrainedWidth was already set.\n");
// #endif
// 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 // When doing this we need to set the block reflow state's
// "mUnconstrainedWidth" variable to PR_TRUE so if we encounter // "mUnconstrainedWidth" variable to PR_TRUE so if we encounter

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

@ -295,6 +295,28 @@ protected:
*/ */
nsresult PrepareChildIncrementalReflow(nsBlockReflowState& aState); 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 /** set up the conditions necessary for an resize reflow
* the primary task is to mark the minimumly sufficient lines dirty. * the primary task is to mark the minimumly sufficient lines dirty.
*/ */