зеркало из https://github.com/mozilla/gecko-dev.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. Relanding because I accidentally backed it out. r+sr=dbaron
This commit is contained in:
Родитель
1fac9c6d76
Коммит
f132edee94
|
@ -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;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче