Bug 129115. Change the reflow path from a list structure to a tree structure to amortize state recovery and damage propagation costs. Eliminate table timeout reflows. r=kin,karnaze,rods,rbs; sr=attinasi

git-svn-id: svn://10.0.0.236/trunk@121265 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
waterson%netscape.com
2002-05-10 18:22:41 +00:00
parent 79bf7a0a29
commit 5bae3657e6
99 changed files with 3075 additions and 2868 deletions

View File

@@ -54,7 +54,7 @@
#include "nsIFrameManager.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsHTMLReflowCommand.h"
#include "nsReflowPath.h"
#include "nsIStyleContext.h"
#include "nsIView.h"
#include "nsIFontMetrics.h"
@@ -698,17 +698,18 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
reflow.Append(nsHTMLReflowState::ReasonToString(aReflowState.reason));
if (aReflowState.reason == eReflowReason_Incremental) {
reflow += " (";
nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
nsReflowType type;
aReflowState.reflowCommand->GetType(type);
reflow += kReflowCommandType[type];
if (command) {
// We're the target.
reflow += " (";
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
reflow += nsPrintfCString("@%p", target);
nsReflowType type;
command->GetType(type);
reflow += kReflowCommandType[type];
reflow += ")";
reflow += ")";
}
}
IndentBy(stdout, gNoiseIndent);
@@ -772,7 +773,7 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
#else
nsHTMLReflowState reflowState(aReflowState);
reflowState.reason = eReflowReason_Resize;
reflowState.reflowCommand = nsnull;
reflowState.path = nsnull;
nsBlockFrame::Reflow(aPresContext, aMetrics, reflowState, aStatus);
#endif
@@ -848,7 +849,6 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
nsresult rv = NS_OK;
nsIFrame* target;
switch (aReflowState.reason) {
case eReflowReason_Initial:
#ifdef NOISY_REFLOW_REASON
@@ -864,14 +864,18 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
// Do nothing; the dirty lines will already have been marked.
break;
case eReflowReason_Incremental:
aReflowState.reflowCommand->GetTarget(target);
if (this == target) {
nsReflowType type;
aReflowState.reflowCommand->GetType(type);
case eReflowReason_Incremental: {
#ifdef NOISY_REFLOW_REASON
ListTag(stdout);
printf(": reflow=incremental type=%d\n", type);
ListTag(stdout);
printf(": reflow=incremental ");
#endif
nsReflowPath *path = aReflowState.path;
nsHTMLReflowCommand *command = path->mReflowCommand;
if (command) {
nsReflowType type;
command->GetType(type);
#ifdef NOISY_REFLOW_REASON
printf("type=%s ", kReflowCommandType[type]);
#endif
switch (type) {
case eReflowType_StyleChanged:
@@ -880,28 +884,44 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext,
case eReflowType_ReflowDirty:
// Do nothing; the dirty lines will already have been marked.
break;
case eReflowType_ContentChanged:
// Perform a full reflow.
rv = PrepareResizeReflow(state);
break;
default:
// Map any other incremental operations into full reflows
// We shouldn't get here. But, if we do, perform a full reflow.
NS_ERROR("unexpected reflow type");
rv = PrepareResizeReflow(state);
break;
}
}
else {
// Get next frame in reflow command chain
aReflowState.reflowCommand->GetNext(state.mNextRCFrame);
if (path->FirstChild() != path->EndChildren()) {
// We're along the reflow path, but not necessarily the target
// of the reflow.
#ifdef NOISY_REFLOW_REASON
ListTag(stdout);
printf(": reflow=incremental");
if (state.mNextRCFrame) {
printf(" next=");
nsFrame::ListTag(stdout, state.mNextRCFrame);
printf(" next={ ");
for (nsReflowPath::iterator iter = path->FirstChild();
iter != path->EndChildren();
++iter) {
nsFrame::ListTag(stdout, *iter);
printf(" ");
}
printf("\n");
printf("}");
#endif
rv = PrepareChildIncrementalReflow(state);
}
#ifdef NOISY_REFLOW_REASON
printf("\n");
#endif
break;
}
case eReflowReason_StyleChange:
DrainOverflowLines(aPresContext);
@@ -1522,71 +1542,76 @@ nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
nsresult
nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
{
// Determine the line being impacted
line_iterator line = FindLineFor(aState.mNextRCFrame);
if (line == end_lines()) {
// This assertion actually fires on lots of pages
// (e.g., bugzilla, bugzilla query page), so limit it
// to a few people until we fix the problem causing it.
//
// I think waterson explained once why it was happening -- I think
// it has something to do with the interaction of the unconstrained
// reflow in multi-pass reflow with the reflow command's chain, but
// I don't remember the details.
#if defined(DEBUG_dbaron) || defined(DEBUG_waterson)
NS_NOTREACHED("We don't have a line for the target of the reflow. "
"Being inefficient");
#endif
// This can't happen, but just in case it does...
return PrepareResizeReflow(aState);
}
// XXXwaterson this is non-optimal. We'd rather do this in
// ReflowDirtyLines; however, I'm not quite ready to figure out how
// to deal with reflow path retargeting yet.
nsReflowPath *path = aState.mReflowState.path;
if (line->IsInline()) {
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.
nsReflowPath::iterator iter = path->FirstChild();
nsReflowPath::iterator end = path->EndChildren();
for ( ; iter != end; ++iter) {
// Determine the line being impacted
line_iterator line = FindLineFor(*iter);
if (line == end_lines()) {
// This assertion actually fires on lots of pages
// (e.g., bugzilla, bugzilla query page), so limit it
// to a few people until we fix the problem causing it.
//
// 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);
// I think waterson explained once why it was happening -- I think
// it has something to do with the interaction of the unconstrained
// reflow in multi-pass reflow with the reflow command's chain, but
// I don't remember the details.
#if defined(DEBUG_dbaron) || defined(DEBUG_waterson)
NS_NOTREACHED("We don't have a line for the target of the reflow. "
"Being inefficient");
#endif
// This can't happen, but just in case it does...
PrepareResizeReflow(aState);
continue;
}
if (line->IsInline()) {
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
// We've been asked to compute the maximum width of the block
// frame, which ReflowLine() will handle 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. This will allow the incremental
// reflow to arrive at the target frame during the first-pass
// unconstrained reflow.
nsIFrame *prevInFlow;
(*iter)->GetPrevInFlow(&prevInFlow);
if (prevInFlow)
RetargetInlineIncrementalReflow(iter, line, prevInFlow);
}
}
// Just mark this line dirty. We never need to mark the
// previous line dirty since either:
// * the line is a block, and there would never be a chance to pull
// something up
// * It's an incremental reflow to something within an inline, which
// we know must be very limited.
line->MarkDirty();
}
// Just mark this line dirty. We never need to mark the
// previous line dirty since either:
// * the line is a block, and there would never be a chance to pull
// something up
// * It's an incremental reflow to something within an inline, which
// we know must be very limited.
line->MarkDirty();
return NS_OK;
}
void
nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState,
nsBlockFrame::RetargetInlineIncrementalReflow(nsReflowPath::iterator &aTarget,
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),
NS_ASSERTION(aLine->Contains(*aTarget),
"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.
// Now fix the iterator, 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.,
@@ -1601,13 +1626,13 @@ nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState,
if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE)
break;
aState.mNextRCFrame = aPrevInFlow;
aState.mNextRCFrame->GetPrevInFlow(&aPrevInFlow);
*aTarget = aPrevInFlow;
aPrevInFlow->GetPrevInFlow(&aPrevInFlow);
#ifdef DEBUG
// Paranoia. Ensure that the prev-in-flow is really in the
// previous line.
line_iterator check = FindLineFor(aState.mNextRCFrame);
line_iterator check = FindLineFor(*aTarget);
NS_ASSERTION(check == aLine, "prev-in-flow not in previous linebox");
#endif
@@ -1616,6 +1641,15 @@ nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState,
if (lineCount > 0) {
// Fix any frames deeper in the reflow path.
#if 0
// XXXwaterson fix me! we've got to recurse through the iterator's
// kids. This really shouldn't matter unless we want to implement
// `display: inline-block' or do XBL form controls. Why, you ask?
// Because what will happen is that inline frames will get flowed
// with a resize reflow, which will be sufficient to mask any
// glitches that would otherwise occur. However, as soon as boxes
// or blocks end up in the flow here (and aren't explicit reflow
// roots), they may optimize away the resize reflow.
// 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).
@@ -1640,6 +1674,9 @@ nsBlockFrame::RetargetInlineIncrementalReflow(nsBlockReflowState &aState,
path->ReplaceElementAt(frame, i);
}
#else
NS_WARNING("blowing an incremental reflow targeted at a nested inline");
#endif
}
}
@@ -2048,12 +2085,16 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
#ifdef DEBUG
if (gNoisyReflow) {
if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsReflowType type;
aState.mReflowState.reflowCommand->GetType(type);
IndentBy(stdout, gNoiseIndent);
ListTag(stdout);
printf(": incrementally reflowing dirty lines: type=%s(%d)",
kReflowCommandType[type], type);
printf(": incrementally reflowing dirty lines");
nsHTMLReflowCommand *command = aState.mReflowState.path->mReflowCommand;
if (command) {
nsReflowType type;
command->GetType(type);
printf(": type=%s(%d)", kReflowCommandType[type], type);
}
}
else {
IndentBy(stdout, gNoiseIndent);
@@ -3031,7 +3072,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
aState.GetFlag(BRS_COMPUTEMAXELEMENTSIZE),
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
brc.SetNextRCFrame(aState.mNextRCFrame);
// See if we should apply the top margin. If the block frame being
// reflowed is a continuation (non-null prev-in-flow) then we don't
@@ -3069,14 +3109,16 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// correct available space (there might be a floater that's
// already been placed below the aState.mPrevBottomMargin
// Setup a reflowState to get the style computed margin-top value
// Setup a reflowState to get the style computed margin-top
// value. We'll use a reason of `resize' so that we don't fudge
// any incremental reflow state.
// The availSpace here is irrelevant to our needs - all we want
// out if this setup is the margin-top value which doesn't depend
// on the childs available space.
nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
frame, availSpace);
frame, availSpace, eReflowReason_Resize);
// Now compute the collapsed margin-top value into aState.mPrevBottomMargin
nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin;
@@ -3126,11 +3168,10 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
Invalidate(aState.mPresContext, mRect);
}
if (frame == aState.mNextRCFrame) {
// NULL out mNextRCFrame so if we reflow it again we don't think it's still
// an incremental reflow
aState.mNextRCFrame = nsnull;
}
// Remove the frame from the reflow tree.
if (aState.mReflowState.path)
aState.mReflowState.path->RemoveChild(frame);
if (NS_FAILED(rv)) {
return rv;
}
@@ -3613,13 +3654,18 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Reflow the inline frame
nsReflowStatus frameReflowStatus;
PRBool pushedFrame;
nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus, nsnull, pushedFrame);
if (aFrame == aState.mNextRCFrame) {
// NULL out mNextRCFrame so if we reflow it again we don't think it's still
// an incremental reflow
aState.mNextRCFrame = nsnull;
}
nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
nsnull, pushedFrame);
// If this is an incremental reflow, prune the child from the path
// so we don't incrementally reflow it again.
// XXX note that we don't currently have any incremental reflows
// that trace a path through an inline frame (thanks to reflow roots
// dealing with text inputs), but we may need to deal with this
// again, someday.
if (aState.mReflowState.path)
aState.mReflowState.path->RemoveChild(aFrame);
if (NS_FAILED(rv)) {
return rv;
}
@@ -5049,8 +5095,6 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
computeMaxElementSize,
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
brc.SetNextRCFrame(aState.mNextRCFrame);
// Reflow the floater
PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
@@ -5077,11 +5121,9 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
Invalidate(aState.mPresContext, mRect);
}
if (floater == aState.mNextRCFrame) {
// Null out mNextRCFrame so if we reflow it again, we don't think
// it's still an incremental reflow
aState.mNextRCFrame = nsnull;
}
// Remove the floater from the reflow tree.
if (aState.mReflowState.path)
aState.mReflowState.path->RemoveChild(floater);
if (NS_FAILED(rv)) {
return rv;
@@ -6233,8 +6275,32 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
nsSize availSize;
availSize.width = NS_UNCONSTRAINEDSIZE;
availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
mBullet, availSize);
// Get the reason right.
// XXXwaterson Should this look just like the logic in
// nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
const nsHTMLReflowState &rs = aState.mReflowState;
nsReflowReason reason = rs.reason;
if (reason == eReflowReason_Incremental) {
if (! rs.path->HasChild(mBullet)) {
// An incremental reflow not explicitly destined to (or through)
// the child should be treated as a resize...
reason = eReflowReason_Resize;
}
// ...unless its an incremental `style changed' reflow targeted at
// the block, in which case, we propagate that to its children.
nsHTMLReflowCommand *command = rs.path->mReflowCommand;
if (command) {
nsReflowType type;
command->GetType(type);
if (type == eReflowType_StyleChanged)
reason = eReflowReason_StyleChange;
}
}
nsHTMLReflowState reflowState(aState.mPresContext, rs,
mBullet, availSize, reason);
nsReflowStatus status;
mBullet->WillReflow(aState.mPresContext);
mBullet->Reflow(aState.mPresContext, aMetrics, reflowState, status);