зеркало из https://github.com/mozilla/pjs.git
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. r+sr=dbaron
This commit is contained in:
Родитель
e2b83ee77f
Коммит
e8f95314d6
|
@ -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
|
||||
|
|
|
@ -203,6 +203,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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче