Bug 256311. Improve block reflow performance in the presence of many empty lines, by being more aggressive about caching block and line emptiness state, and by stashing a reference to a line that we know has nothing but empty lines above it. Relanding because I accidentally backed it out. r+sr=dbaron

This commit is contained in:
roc+%cs.cmu.edu 2006-02-27 04:15:05 +00:00
Родитель 1fac9c6d76
Коммит f132edee94
8 изменённых файлов: 93 добавлений и 10 удалений

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

@ -3124,6 +3124,24 @@ nsBlockFrame::IsSelfEmpty()
return PR_TRUE; return PR_TRUE;
} }
PRBool
nsBlockFrame::CachedIsEmpty()
{
if (!IsSelfEmpty()) {
return PR_FALSE;
}
for (line_iterator line = begin_lines(), line_end = end_lines();
line != line_end;
++line)
{
if (!line->CachedIsEmpty())
return PR_FALSE;
}
return PR_TRUE;
}
PRBool PRBool
nsBlockFrame::IsEmpty() nsBlockFrame::IsEmpty()
{ {
@ -3160,7 +3178,11 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
} }
// Determine if this line is "essentially" the first line // Determine if this line is "essentially" the first line
for (line_iterator line = begin_lines(); line != aLine; ++line) { line_iterator line = begin_lines();
if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
line = aState.mLineAdjacentToTop;
}
while (line != aLine) {
if (!line->CachedIsEmpty() || line->HasClearance()) { if (!line->CachedIsEmpty() || line->HasClearance()) {
// A line which precedes aLine is non-empty, or has clearance, // A line which precedes aLine is non-empty, or has clearance,
// so therefore the top margin applies. // so therefore the top margin applies.
@ -3169,6 +3191,9 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
} }
// No need to apply the top margin if the line has floats. We // No need to apply the top margin if the line has floats. We
// should collapse anyway (bug 44419) // should collapse anyway (bug 44419)
++line;
aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, PR_TRUE);
aState.mLineAdjacentToTop = line;
} }
// The line being reflowed is "essentially" the first line in the // The line being reflowed is "essentially" the first line in the

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

@ -202,6 +202,7 @@ public:
virtual PRBool IsVisibleInSelection(nsISelection* aSelection); virtual PRBool IsVisibleInSelection(nsISelection* aSelection);
virtual PRBool IsEmpty(); virtual PRBool IsEmpty();
virtual PRBool CachedIsEmpty();
virtual PRBool IsSelfEmpty(); virtual PRBool IsSelfEmpty();
// nsIHTMLReflow // nsIHTMLReflow

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

@ -90,7 +90,8 @@ static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
PRBool PRBool
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry) nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
PRBool* aMayNeedRetry, PRBool* aBlockIsEmpty)
{ {
// Include frame's top margin // Include frame's top margin
aMargin->Include(aRS.mComputedMargin.top); aMargin->Include(aRS.mComputedMargin.top);
@ -105,6 +106,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
#endif #endif
PRBool dirtiedLine = PR_FALSE; PRBool dirtiedLine = PR_FALSE;
PRBool setBlockIsEmpty = PR_FALSE;
// Calculate the frame's generational top-margin from its child // Calculate the frame's generational top-margin from its child
// blocks. Note that if the frame has a non-zero top-border or // blocks. Note that if the frame has a non-zero top-border or
@ -151,8 +153,10 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
dirtiedLine = PR_TRUE; dirtiedLine = PR_TRUE;
} }
PRBool isEmpty = line->IsEmpty(); PRBool isEmpty;
if (line->IsBlock()) { if (line->IsInline()) {
isEmpty = line->IsEmpty();
} else {
nsIFrame* kid = line->mFirstChild; nsIFrame* kid = line->mFirstChild;
if (kid == aClearanceFrame) { if (kid == aClearanceFrame) {
line->SetHasClearance(); line->SetHasClearance();
@ -194,7 +198,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
*aMayNeedRetry = PR_TRUE; *aMayNeedRetry = PR_TRUE;
} }
if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry)) { if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) {
line->MarkDirty(); line->MarkDirty();
dirtiedLine = PR_TRUE; dirtiedLine = PR_TRUE;
} }
@ -205,8 +209,20 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
delete NS_CONST_CAST(nsHTMLReflowState*, outerReflowState); delete NS_CONST_CAST(nsHTMLReflowState*, outerReflowState);
} }
} }
if (!isEmpty) if (!isEmpty) {
if (!setBlockIsEmpty && aBlockIsEmpty) {
setBlockIsEmpty = PR_TRUE;
*aBlockIsEmpty = PR_FALSE;
}
goto done; goto done;
}
}
if (!setBlockIsEmpty && aBlockIsEmpty) {
// The first time we reach here is when this is the first block
// and we have processed all its normal lines.
setBlockIsEmpty = PR_TRUE;
// All lines are empty, or we wouldn't be here!
*aBlockIsEmpty = aRS.frame->IsSelfEmpty();
} }
} }
} }
@ -214,6 +230,10 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
; ;
} }
if (!setBlockIsEmpty && aBlockIsEmpty) {
*aBlockIsEmpty = aRS.frame->IsEmpty();
}
#ifdef NOISY_VERTICAL_MARGINS #ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame); nsFrame::ListTag(stdout, aRS.frame);
printf(": => %d\n", aMargin->get()); printf(": => %d\n", aMargin->get());

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

@ -122,7 +122,7 @@ public:
*/ */
static PRBool ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, static PRBool ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
PRBool* aMayNeedRetry); PRBool* aMayNeedRetry, PRBool* aIsEmpty = nsnull);
protected: protected:
nsPresContext* mPresContext; nsPresContext* mPresContext;

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

@ -58,7 +58,9 @@ class nsBlockFrame;
#define BRS_COMPUTEMAXELEMENTWIDTH 0x00000100 #define BRS_COMPUTEMAXELEMENTWIDTH 0x00000100
#define BRS_COMPUTEMAXWIDTH 0x00000200 #define BRS_COMPUTEMAXWIDTH 0x00000200
#define BRS_ISFIRSTINFLOW 0x00000400 #define BRS_ISFIRSTINFLOW 0x00000400
#define BRS_LASTFLAG BRS_ISFIRSTINFLOW // Set when mLineAdjacentToTop is valid
#define BRS_HAVELINEADJACENTTOTOP 0x00000800
#define BRS_LASTFLAG BRS_HAVELINEADJACENTTOTOP
class nsBlockReflowState { class nsBlockReflowState {
public: public:
@ -198,6 +200,12 @@ public:
// If it is mBlock->end_lines(), then it is invalid. // If it is mBlock->end_lines(), then it is invalid.
nsLineList::iterator mCurrentLine; nsLineList::iterator mCurrentLine;
// When BRS_HAVELINEADJACENTTOTOP is set, this refers to a line
// which we know is adjacent to the top of the block (in other words,
// all lines before it are empty and do not have clearance. This line is
// always before the current line.
nsLineList::iterator mLineAdjacentToTop;
// The current Y coordinate in the block // The current Y coordinate in the block
nscoord mY; nscoord mY;

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

@ -3545,6 +3545,14 @@ nsFrame::IsEmpty()
return PR_FALSE; return PR_FALSE;
} }
PRBool
nsIFrame::CachedIsEmpty()
{
NS_PRECONDITION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
"Must only be called on reflowed lines");
return IsEmpty();
}
/* virtual */ PRBool /* virtual */ PRBool
nsFrame::IsSelfEmpty() nsFrame::IsSelfEmpty()
{ {

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

@ -1402,7 +1402,11 @@ public:
* should return true. * should return true.
*/ */
virtual PRBool IsEmpty() = 0; virtual PRBool IsEmpty() = 0;
/**
* Return the same as IsEmpty(). This may only be called after the frame
* has been reflowed and before any further style or content changes.
*/
virtual PRBool CachedIsEmpty();
/** /**
* Determine whether the frame is logically empty, assuming that all * Determine whether the frame is logically empty, assuming that all
* its children are empty. * its children are empty.

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

@ -307,7 +307,24 @@ nsLineBox::CachedIsEmpty()
return mFlags.mEmptyCacheState; return mFlags.mEmptyCacheState;
} }
PRBool result = IsEmpty(); PRBool result;
if (IsBlock()) {
result = mFirstChild->CachedIsEmpty();
} else {
PRInt32 n;
nsIFrame *kid;
result = PR_TRUE;
for (n = GetChildCount(), kid = mFirstChild;
n > 0;
--n, kid = kid->GetNextSibling())
{
if (!kid->CachedIsEmpty()) {
result = PR_FALSE;
break;
}
}
}
mFlags.mEmptyCacheValid = PR_TRUE; mFlags.mEmptyCacheValid = PR_TRUE;
mFlags.mEmptyCacheState = result; mFlags.mEmptyCacheState = result;
return result; return result;