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;
}
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
nsBlockFrame::IsEmpty()
{
@ -3160,7 +3178,11 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
}
// 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()) {
// A line which precedes aLine is non-empty, or has clearance,
// 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
// 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

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

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

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

@ -90,7 +90,8 @@ static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
PRBool
nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry)
nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
PRBool* aMayNeedRetry, PRBool* aBlockIsEmpty)
{
// Include frame's top margin
aMargin->Include(aRS.mComputedMargin.top);
@ -105,6 +106,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
#endif
PRBool dirtiedLine = PR_FALSE;
PRBool setBlockIsEmpty = PR_FALSE;
// Calculate the frame's generational top-margin from its child
// 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;
}
PRBool isEmpty = line->IsEmpty();
if (line->IsBlock()) {
PRBool isEmpty;
if (line->IsInline()) {
isEmpty = line->IsEmpty();
} else {
nsIFrame* kid = line->mFirstChild;
if (kid == aClearanceFrame) {
line->SetHasClearance();
@ -194,7 +198,7 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
*aMayNeedRetry = PR_TRUE;
}
if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry)) {
if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) {
line->MarkDirty();
dirtiedLine = PR_TRUE;
}
@ -205,14 +209,30 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
delete NS_CONST_CAST(nsHTMLReflowState*, outerReflowState);
}
}
if (!isEmpty)
if (!isEmpty) {
if (!setBlockIsEmpty && aBlockIsEmpty) {
setBlockIsEmpty = PR_TRUE;
*aBlockIsEmpty = PR_FALSE;
}
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();
}
}
}
done:
;
}
if (!setBlockIsEmpty && aBlockIsEmpty) {
*aBlockIsEmpty = aRS.frame->IsEmpty();
}
#ifdef NOISY_VERTICAL_MARGINS
nsFrame::ListTag(stdout, aRS.frame);

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

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

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

@ -58,7 +58,9 @@ class nsBlockFrame;
#define BRS_COMPUTEMAXELEMENTWIDTH 0x00000100
#define BRS_COMPUTEMAXWIDTH 0x00000200
#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 {
public:
@ -198,6 +200,12 @@ public:
// If it is mBlock->end_lines(), then it is invalid.
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
nscoord mY;

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

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

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

@ -1402,7 +1402,11 @@ public:
* should return true.
*/
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
* its children are empty.

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

@ -307,7 +307,24 @@ nsLineBox::CachedIsEmpty()
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.mEmptyCacheState = result;
return result;