зеркало из https://github.com/mozilla/pjs.git
relanding fix for bug 317278 :-(
This commit is contained in:
Родитель
b9a4cff178
Коммит
9fbb95a56f
|
@ -2744,13 +2744,14 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
|
|||
// a placeholder and then reflow its associated float we don't
|
||||
// end up resetting the line's right edge and have it think the
|
||||
// width is unconstrained...
|
||||
aState.mSpaceManager->PushState();
|
||||
nsSpaceManager::SavedState spaceManagerState;
|
||||
aState.mSpaceManager->PushState(&spaceManagerState);
|
||||
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE);
|
||||
ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE);
|
||||
aState.mY = oldY;
|
||||
aState.mPrevBottomMargin = oldPrevBottomMargin;
|
||||
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth);
|
||||
aState.mSpaceManager->PopState();
|
||||
aState.mSpaceManager->PopState(&spaceManagerState);
|
||||
|
||||
// Update the line's maximum width
|
||||
aLine->mMaximumWidth = aLine->mBounds.XMost();
|
||||
|
@ -3507,9 +3508,10 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
aState.mReflowState.reason, PR_TRUE);
|
||||
blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
|
||||
|
||||
nsSpaceManager::SavedState spaceManagerState;
|
||||
if (mayNeedRetry) {
|
||||
blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
|
||||
aState.mSpaceManager->PushState();
|
||||
aState.mSpaceManager->PushState(&spaceManagerState);
|
||||
} else if (!applyTopMargin) {
|
||||
blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
|
||||
}
|
||||
|
@ -3526,19 +3528,12 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
return rv;
|
||||
}
|
||||
|
||||
if (mayNeedRetry) {
|
||||
if (clearanceFrame) {
|
||||
UndoSplitPlaceholders(aState, lastPlaceholder);
|
||||
aState.mSpaceManager->PopState();
|
||||
aState.mY = startingY;
|
||||
aState.mPrevBottomMargin = incomingMargin;
|
||||
continue;
|
||||
} else {
|
||||
// pop the saved state off the stack and discard it, because we
|
||||
// want to keep the current state, since our speculation
|
||||
// succeeded
|
||||
aState.mSpaceManager->DiscardState();
|
||||
}
|
||||
if (mayNeedRetry && clearanceFrame) {
|
||||
UndoSplitPlaceholders(aState, lastPlaceholder);
|
||||
aState.mSpaceManager->PopState(&spaceManagerState);
|
||||
aState.mY = startingY;
|
||||
aState.mPrevBottomMargin = incomingMargin;
|
||||
continue;
|
||||
}
|
||||
|
||||
aState.mPrevChild = frame;
|
||||
|
@ -3765,12 +3760,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|||
return rv;
|
||||
}
|
||||
|
||||
#define LINE_REFLOW_OK 0
|
||||
#define LINE_REFLOW_STOP 1
|
||||
#define LINE_REFLOW_REDO 2
|
||||
// a frame was complete, but truncated and not at the top of a page
|
||||
#define LINE_REFLOW_TRUNCATED 3
|
||||
|
||||
nsresult
|
||||
nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
|
||||
line_iterator aLine,
|
||||
|
@ -3784,45 +3773,61 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
|
|||
#ifdef DEBUG
|
||||
PRInt32 spins = 0;
|
||||
#endif
|
||||
PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
|
||||
PRBool didRedo = PR_FALSE;
|
||||
LineReflowStatus lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
|
||||
PRBool movedPastFloat = PR_FALSE;
|
||||
do {
|
||||
// Once upon a time we allocated the first 30 nsLineLayout objects
|
||||
// on the stack, and then we switched to the heap. At that time
|
||||
// these objects were large (1100 bytes on a 32 bit system).
|
||||
// Then the nsLineLayout object was shrunk to 156 bytes by
|
||||
// removing some internal buffers. Given that it is so much
|
||||
// smaller, the complexity of 2 different ways of allocating
|
||||
// no longer makes sense. Now we always allocate on the stack
|
||||
|
||||
nsLineLayout lineLayout(aState.mPresContext,
|
||||
aState.mReflowState.mSpaceManager,
|
||||
&aState.mReflowState,
|
||||
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH));
|
||||
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
|
||||
rv = DoReflowInlineFrames(aState, lineLayout, aLine,
|
||||
aKeepReflowGoing, &lineReflowStatus,
|
||||
aUpdateMaximumWidth, aDamageDirtyArea);
|
||||
lineLayout.EndLineReflow();
|
||||
|
||||
if (LINE_REFLOW_REDO == lineReflowStatus) {
|
||||
didRedo = PR_TRUE;
|
||||
}
|
||||
PRBool allowPullUp = PR_TRUE;
|
||||
do {
|
||||
nsSpaceManager::SavedState spaceManagerState;
|
||||
aState.mReflowState.mSpaceManager->PushState(&spaceManagerState);
|
||||
|
||||
// Once upon a time we allocated the first 30 nsLineLayout objects
|
||||
// on the stack, and then we switched to the heap. At that time
|
||||
// these objects were large (1100 bytes on a 32 bit system).
|
||||
// Then the nsLineLayout object was shrunk to 156 bytes by
|
||||
// removing some internal buffers. Given that it is so much
|
||||
// smaller, the complexity of 2 different ways of allocating
|
||||
// no longer makes sense. Now we always allocate on the stack
|
||||
nsLineLayout lineLayout(aState.mPresContext,
|
||||
aState.mReflowState.mSpaceManager,
|
||||
&aState.mReflowState,
|
||||
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH));
|
||||
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
|
||||
rv = DoReflowInlineFrames(aState, lineLayout, aLine,
|
||||
aKeepReflowGoing, &lineReflowStatus,
|
||||
aUpdateMaximumWidth, aDamageDirtyArea,
|
||||
allowPullUp);
|
||||
lineLayout.EndLineReflow();
|
||||
|
||||
if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
|
||||
LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
|
||||
// restore the space manager state
|
||||
aState.mReflowState.mSpaceManager->PopState(&spaceManagerState);
|
||||
// Clear out below-current-line-floats
|
||||
aState.mBelowCurrentLineFloats.DeleteAll();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
spins++;
|
||||
if (1000 == spins) {
|
||||
ListTag(stdout);
|
||||
printf(": yikes! spinning on a line over 1000 times!\n");
|
||||
NS_ABORT();
|
||||
}
|
||||
spins++;
|
||||
if (1000 == spins) {
|
||||
ListTag(stdout);
|
||||
printf(": yikes! spinning on a line over 1000 times!\n");
|
||||
NS_ABORT();
|
||||
}
|
||||
#endif
|
||||
|
||||
} while (NS_SUCCEEDED(rv) && LINE_REFLOW_REDO == lineReflowStatus);
|
||||
// Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
|
||||
allowPullUp = PR_FALSE;
|
||||
} while (NS_SUCCEEDED(rv) && LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
|
||||
|
||||
// If we did at least one REDO, then the line did not fit next to some float.
|
||||
if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
|
||||
movedPastFloat = PR_TRUE;
|
||||
}
|
||||
} while (NS_SUCCEEDED(rv) && LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
|
||||
|
||||
// If we did at least one REDO_FOR_FLOAT, then the line did not fit next to some float.
|
||||
// Mark it as impacted by a float, even if it no longer is next to a float.
|
||||
if (didRedo) {
|
||||
if (movedPastFloat) {
|
||||
aLine->SetLineIsImpactedByFloat(PR_TRUE);
|
||||
}
|
||||
|
||||
|
@ -3853,9 +3858,10 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
PRBool* aKeepReflowGoing,
|
||||
PRUint8* aLineReflowStatus,
|
||||
LineReflowStatus* aLineReflowStatus,
|
||||
PRBool aUpdateMaximumWidth,
|
||||
PRBool aDamageDirtyArea)
|
||||
PRBool aDamageDirtyArea,
|
||||
PRBool aAllowPullUp)
|
||||
{
|
||||
// Forget all of the floats on the line
|
||||
aLine->FreeFloats(aState.mFloatCacheFreeList);
|
||||
|
@ -3905,7 +3911,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
|
||||
// Reflow the frames that are already on the line first
|
||||
nsresult rv = NS_OK;
|
||||
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
|
||||
LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
|
||||
PRInt32 i;
|
||||
nsIFrame* frame = aLine->mFirstChild;
|
||||
aLine->SetHasPercentageChild(PR_FALSE); // To be set by ReflowInlineFrame below
|
||||
|
@ -3952,7 +3958,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
}
|
||||
|
||||
// Don't pull up new frames into lines with continuation placeholders
|
||||
if (!isContinuingPlaceholders) {
|
||||
if (!isContinuingPlaceholders && aAllowPullUp) {
|
||||
// Pull frames and reflow them until we can't
|
||||
while (LINE_REFLOW_OK == lineReflowStatus) {
|
||||
rv = PullFrame(aState, aLine, aDamageDirtyArea, frame);
|
||||
|
@ -3984,7 +3990,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
}
|
||||
}
|
||||
|
||||
if (LINE_REFLOW_REDO == lineReflowStatus) {
|
||||
if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
|
||||
// This happens only when we have a line that is impacted by
|
||||
// floats and the first element in the line doesn't fit with
|
||||
// the floats.
|
||||
|
@ -3996,7 +4002,6 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
|
||||
"unconstrained height on totally empty line");
|
||||
|
||||
|
||||
if (aState.mAvailSpaceRect.height > 0) {
|
||||
aState.mY += aState.mAvailSpaceRect.height;
|
||||
} else {
|
||||
|
@ -4029,6 +4034,13 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
|
|||
// push the line and return now instead of later on after we are
|
||||
// past the float.
|
||||
}
|
||||
else if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus) {
|
||||
// We don't want to advance by the bottom margin anymore (we did it
|
||||
// once at the beginning of this function, which will just be called
|
||||
// again), and we certainly don't want to go back if it's negative
|
||||
// (infinite loop, bug 153429).
|
||||
aState.mPrevBottomMargin.Zero();
|
||||
}
|
||||
else if (LINE_REFLOW_TRUNCATED != lineReflowStatus) {
|
||||
// If we are propagating out a break-before status then there is
|
||||
// no point in placing the line.
|
||||
|
@ -4056,9 +4068,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
|
|||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
nsIFrame* aFrame,
|
||||
PRUint8* aLineReflowStatus)
|
||||
LineReflowStatus* aLineReflowStatus)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aFrame);
|
||||
NS_ENSURE_ARG_POINTER(aFrame);
|
||||
|
||||
*aLineReflowStatus = LINE_REFLOW_OK;
|
||||
|
||||
|
@ -4151,12 +4163,12 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
|
|||
// be trying to place content where there's no room (e.g. on a
|
||||
// line with wide floats). Inform the caller to reflow the
|
||||
// line after skipping past a float.
|
||||
*aLineReflowStatus = LINE_REFLOW_REDO;
|
||||
*aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
|
||||
}
|
||||
else {
|
||||
// It's not the first child on this line so go ahead and split
|
||||
// the line. We will see the frame again on the next-line.
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame);
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -4199,7 +4211,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
|
|||
}
|
||||
|
||||
// Split line, but after the frame just reflowed
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling());
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -4249,7 +4261,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
|
|||
if (splitLine) {
|
||||
// Split line after the current frame
|
||||
*aLineReflowStatus = LINE_REFLOW_STOP;
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling());
|
||||
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -4331,11 +4343,35 @@ nsBlockFrame::SplitPlaceholder(nsBlockReflowState& aState,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsFloatCache*
|
||||
GetLastFloat(nsLineBox* aLine)
|
||||
{
|
||||
nsFloatCache* fc = aLine->GetFirstFloat();
|
||||
while (fc && fc->Next()) {
|
||||
fc = fc->Next();
|
||||
}
|
||||
return fc;
|
||||
}
|
||||
|
||||
static PRBool
|
||||
CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
|
||||
{
|
||||
if (!aFC)
|
||||
return PR_TRUE;
|
||||
for (nsIFrame* f = aFC->mPlaceholder; f; f = f->GetParent()) {
|
||||
if (f->GetParent() == aBlock)
|
||||
return aLine->Contains(f);
|
||||
}
|
||||
NS_ASSERTION(PR_FALSE, "aBlock is not an ancestor of aFrame!");
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
|
||||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
nsIFrame* aFrame)
|
||||
nsIFrame* aFrame,
|
||||
LineReflowStatus* aLineReflowStatus)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
|
||||
|
||||
|
@ -4382,6 +4418,17 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
|
|||
// Let line layout know that some frames are no longer part of its
|
||||
// state.
|
||||
aLineLayout.SplitLineTo(aLine->GetChildCount());
|
||||
|
||||
// If floats have been placed whose placeholders have been pushed to the new
|
||||
// line, we need to reflow the old line again. We don't want to look at the
|
||||
// frames in the new line, because as a large paragraph is laid out the
|
||||
// we'd get O(N^2) performance. So instead we just check that the last
|
||||
// float and the last below-current-line float are still in aLine.
|
||||
if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
|
||||
!CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
|
||||
*aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
VerifyLines(PR_TRUE);
|
||||
#endif
|
||||
|
|
|
@ -52,6 +52,24 @@
|
|||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsStyleSet.h"
|
||||
|
||||
enum LineReflowStatus {
|
||||
// The line was completely reflowed and fit in available width, and we should
|
||||
// try to pull up content from the next line if possible.
|
||||
LINE_REFLOW_OK,
|
||||
// The line was completely reflowed and fit in available width, but we should
|
||||
// not try to pull up content from the next line.
|
||||
LINE_REFLOW_STOP,
|
||||
// We need to reflow the line again at its current vertical position. The
|
||||
// new reflow should not try to pull up any frames from the next line.
|
||||
LINE_REFLOW_REDO_NO_PULL,
|
||||
// We need to reflow the line again at a lower vertical postion where there
|
||||
// may be more horizontal space due to different float configuration.
|
||||
LINE_REFLOW_REDO_NEXT_BAND,
|
||||
// The line did not fit in the available vertical space. Try pushing it to
|
||||
// the next page or column if it's not the first line on the current page/column.
|
||||
LINE_REFLOW_TRUNCATED
|
||||
};
|
||||
|
||||
class nsBlockReflowState;
|
||||
class nsBulletFrame;
|
||||
class nsLineBox;
|
||||
|
@ -466,15 +484,16 @@ protected:
|
|||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
PRBool* aKeepReflowGoing,
|
||||
PRUint8* aLineReflowStatus,
|
||||
LineReflowStatus* aLineReflowStatus,
|
||||
PRBool aUpdateMaximumWidth,
|
||||
PRBool aDamageDirtyArea);
|
||||
PRBool aDamageDirtyArea,
|
||||
PRBool aAllowPullUp);
|
||||
|
||||
nsresult ReflowInlineFrame(nsBlockReflowState& aState,
|
||||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
nsIFrame* aFrame,
|
||||
PRUint8* aLineReflowStatus);
|
||||
LineReflowStatus* aLineReflowStatus);
|
||||
|
||||
// An incomplete aReflowStatus indicates the float should be split
|
||||
// but only if the available height is constrained.
|
||||
|
@ -501,7 +520,8 @@ protected:
|
|||
nsresult SplitLine(nsBlockReflowState& aState,
|
||||
nsLineLayout& aLineLayout,
|
||||
line_iterator aLine,
|
||||
nsIFrame* aFrame);
|
||||
nsIFrame* aFrame,
|
||||
LineReflowStatus* aLineReflowStatus);
|
||||
|
||||
nsresult PullFrame(nsBlockReflowState& aState,
|
||||
line_iterator aLine,
|
||||
|
|
|
@ -618,7 +618,8 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
|
|||
// See if this is the child's initial reflow and we are supposed to
|
||||
// compute our maximum width
|
||||
if (mComputeMaximumWidth && (eReflowReason_Initial == aFrameRS.reason)) {
|
||||
mOuterReflowState.mSpaceManager->PushState();
|
||||
nsSpaceManager::SavedState spaceManagerState;
|
||||
mOuterReflowState.mSpaceManager->PushState(&spaceManagerState);
|
||||
|
||||
nscoord oldAvailableWidth = aFrameRS.availableWidth;
|
||||
nscoord oldComputedWidth = aFrameRS.mComputedWidth;
|
||||
|
@ -642,7 +643,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
|
|||
aFrameRS.mComputedWidth = oldComputedWidth;
|
||||
aFrameRS.reason = eReflowReason_Resize;
|
||||
|
||||
mOuterReflowState.mSpaceManager->PopState();
|
||||
mOuterReflowState.mSpaceManager->PopState(&spaceManagerState);
|
||||
}
|
||||
|
||||
rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
|
||||
|
|
|
@ -1102,7 +1102,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache,
|
|||
* Place below-current-line floats.
|
||||
*/
|
||||
PRBool
|
||||
nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheList& aList, PRBool aForceFit)
|
||||
nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRBool aForceFit)
|
||||
{
|
||||
nsFloatCache* fc = aList.Head();
|
||||
while (fc) {
|
||||
|
|
|
@ -99,7 +99,7 @@ public:
|
|||
PRBool* aIsLeftFloat,
|
||||
nsReflowStatus& aReflowStatus,
|
||||
PRBool aForceFit);
|
||||
PRBool PlaceBelowCurrentLineFloats(nsFloatCacheList& aFloats, PRBool aForceFit);
|
||||
PRBool PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats, PRBool aForceFit);
|
||||
|
||||
// Returns the first coordinate >= aY that clears the
|
||||
// indicated floats.
|
||||
|
|
|
@ -875,14 +875,20 @@ nsFloatCacheList::nsFloatCacheList() :
|
|||
|
||||
nsFloatCacheList::~nsFloatCacheList()
|
||||
{
|
||||
nsFloatCache* fc = mHead;
|
||||
while (fc) {
|
||||
nsFloatCache* next = fc->mNext;
|
||||
delete fc;
|
||||
fc = next;
|
||||
DeleteAll();
|
||||
MOZ_COUNT_DTOR(nsFloatCacheList);
|
||||
}
|
||||
|
||||
void
|
||||
nsFloatCacheList::DeleteAll()
|
||||
{
|
||||
nsFloatCache* c = mHead;
|
||||
while (c) {
|
||||
nsFloatCache* next = c->Next();
|
||||
delete c;
|
||||
c = next;
|
||||
}
|
||||
mHead = nsnull;
|
||||
MOZ_COUNT_DTOR(nsFloatCacheList);
|
||||
}
|
||||
|
||||
nsFloatCache*
|
||||
|
@ -929,17 +935,24 @@ nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
|
|||
return fc;
|
||||
}
|
||||
|
||||
void
|
||||
nsFloatCacheList::Remove(nsFloatCache* aElement)
|
||||
nsFloatCache*
|
||||
nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
|
||||
{
|
||||
nsFloatCache** fcp = &mHead;
|
||||
nsFloatCache* fc;
|
||||
while (nsnull != (fc = *fcp)) {
|
||||
NS_ASSERTION(!aElement->mNext, "Can only remove a singleton element");
|
||||
|
||||
nsFloatCache* fc = mHead;
|
||||
nsFloatCache* prev = nsnull;
|
||||
while (fc) {
|
||||
if (fc == aElement) {
|
||||
*fcp = fc->mNext;
|
||||
break;
|
||||
if (prev) {
|
||||
prev->mNext = fc->mNext;
|
||||
} else {
|
||||
mHead = fc->mNext;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
fcp = &fc->mNext;
|
||||
prev = fc;
|
||||
fc = fc->mNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,6 +988,22 @@ nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
|
|||
aList.mHead = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
|
||||
{
|
||||
nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
|
||||
if (mTail == aElement) {
|
||||
mTail = prev;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFloatCacheFreeList::DeleteAll()
|
||||
{
|
||||
nsFloatCacheList::DeleteAll();
|
||||
mTail = nsnull;
|
||||
}
|
||||
|
||||
nsFloatCache*
|
||||
nsFloatCacheFreeList::Alloc()
|
||||
{
|
||||
|
|
|
@ -120,12 +120,14 @@ public:
|
|||
|
||||
nsFloatCache* Tail() const;
|
||||
|
||||
void DeleteAll();
|
||||
|
||||
nsFloatCache* Find(nsIFrame* aOutOfFlowFrame);
|
||||
|
||||
// Remove a nsFloatCache from this list. Deleting this nsFloatCache
|
||||
// becomes the caller's responsibility.
|
||||
void Remove(nsFloatCache* aElement);
|
||||
|
||||
void Remove(nsFloatCache* aElement) { RemoveAndReturnPrev(aElement); }
|
||||
|
||||
// Steal away aList's nsFloatCache objects and put them in this
|
||||
// list. aList must not be empty.
|
||||
void Append(nsFloatCacheFreeList& aList);
|
||||
|
@ -133,12 +135,18 @@ public:
|
|||
protected:
|
||||
nsFloatCache* mHead;
|
||||
|
||||
// Remove a nsFloatCache from this list. Deleting this nsFloatCache
|
||||
// becomes the caller's responsibility. Returns the nsFloatCache that was
|
||||
// before aElement, or nsnull if aElement was the first.
|
||||
nsFloatCache* RemoveAndReturnPrev(nsFloatCache* aElement);
|
||||
|
||||
friend class nsFloatCacheFreeList;
|
||||
};
|
||||
|
||||
//---------------------------------------
|
||||
// Like nsFloatCacheList, but with fast access to the tail
|
||||
|
||||
class nsFloatCacheFreeList : public nsFloatCacheList {
|
||||
class nsFloatCacheFreeList : private nsFloatCacheList {
|
||||
public:
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
nsFloatCacheFreeList();
|
||||
|
@ -148,15 +156,37 @@ public:
|
|||
~nsFloatCacheFreeList() { }
|
||||
#endif
|
||||
|
||||
// Reimplement trivial functions
|
||||
PRBool IsEmpty() const {
|
||||
return nsnull == mHead;
|
||||
}
|
||||
|
||||
nsFloatCache* Head() const {
|
||||
return mHead;
|
||||
}
|
||||
|
||||
nsFloatCache* Tail() const {
|
||||
return mTail;
|
||||
}
|
||||
|
||||
PRBool NotEmpty() const {
|
||||
return nsnull != mHead;
|
||||
}
|
||||
|
||||
void DeleteAll();
|
||||
|
||||
// Steal away aList's nsFloatCache objects and put them on this
|
||||
// free-list. aList must not be empty.
|
||||
void Append(nsFloatCacheList& aList);
|
||||
|
||||
void Append(nsFloatCache* aFloatCache);
|
||||
|
||||
// Allocate a new nsFloatCache object
|
||||
nsFloatCache* Alloc();
|
||||
void Remove(nsFloatCache* aElement);
|
||||
|
||||
// Remove an nsFloatCache object from this list and return it, or create
|
||||
// a new one if this one is empty;
|
||||
nsFloatCache* Alloc();
|
||||
|
||||
protected:
|
||||
nsFloatCache* mTail;
|
||||
|
||||
|
|
|
@ -116,7 +116,6 @@ nsSpaceManager::nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame)
|
|||
MOZ_COUNT_CTOR(nsSpaceManager);
|
||||
mX = mY = 0;
|
||||
mFrameInfoMap = nsnull;
|
||||
mSavedStates = nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -134,14 +133,6 @@ nsSpaceManager::~nsSpaceManager()
|
|||
MOZ_COUNT_DTOR(nsSpaceManager);
|
||||
mBandList.Clear();
|
||||
ClearFrameInfo();
|
||||
|
||||
NS_ASSERTION(!mSavedStates, "states remaining on state stack");
|
||||
|
||||
while (mSavedStates && mSavedStates != &mAutoState){
|
||||
SpaceManagerState *state = mSavedStates;
|
||||
mSavedStates = state->mNext;
|
||||
delete state;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -985,17 +976,16 @@ nsSpaceManager::ClearRegions()
|
|||
}
|
||||
|
||||
void
|
||||
nsSpaceManager::PushState()
|
||||
nsSpaceManager::PushState(SavedState* aState)
|
||||
{
|
||||
// This is a quick and dirty push implementation, which
|
||||
NS_PRECONDITION(aState, "Need a place to save state");
|
||||
|
||||
// This is a cheap push implementation, which
|
||||
// only saves the (x,y) and last frame in the mFrameInfoMap
|
||||
// which is enough info to get us back to where we should be
|
||||
// when pop is called.
|
||||
//
|
||||
// The alternative would be to make full copies of the contents
|
||||
// of mBandList and mFrameInfoMap and restore them when pop is
|
||||
// called, but I'm not sure it's worth the effort/bloat at this
|
||||
// point, since this push/pop mechanism is only used to undo any
|
||||
// This push/pop mechanism is used to undo any
|
||||
// floats that were added during the unconstrained reflow
|
||||
// in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
|
||||
//
|
||||
|
@ -1007,55 +997,30 @@ nsSpaceManager::PushState()
|
|||
// reflow. In the typical case A and C will be the same, but not always.
|
||||
// Allowing mFloatDamage to accumulate the damage incurred during both
|
||||
// reflows ensures that nothing gets missed.
|
||||
|
||||
SpaceManagerState *state;
|
||||
|
||||
if(mSavedStates) {
|
||||
state = new SpaceManagerState;
|
||||
} else {
|
||||
state = &mAutoState;
|
||||
}
|
||||
|
||||
NS_ASSERTION(state, "PushState() failed!");
|
||||
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->mX = mX;
|
||||
state->mY = mY;
|
||||
state->mLowestTop = mLowestTop;
|
||||
aState->mX = mX;
|
||||
aState->mY = mY;
|
||||
aState->mLowestTop = mLowestTop;
|
||||
|
||||
if (mFrameInfoMap) {
|
||||
state->mLastFrame = mFrameInfoMap->mFrame;
|
||||
aState->mLastFrame = mFrameInfoMap->mFrame;
|
||||
} else {
|
||||
state->mLastFrame = nsnull;
|
||||
aState->mLastFrame = nsnull;
|
||||
}
|
||||
|
||||
// Now that we've saved our state, add it to mSavedStates.
|
||||
|
||||
state->mNext = mSavedStates;
|
||||
mSavedStates = state;
|
||||
}
|
||||
|
||||
void
|
||||
nsSpaceManager::PopState()
|
||||
nsSpaceManager::PopState(SavedState* aState)
|
||||
{
|
||||
NS_PRECONDITION(aState, "No state to restore?");
|
||||
|
||||
// This is a quick and dirty pop implementation, to
|
||||
// match the current implementation of PushState(). The
|
||||
// idea here is to remove any frames that have been added
|
||||
// to the mFrameInfoMap since the last call to PushState().
|
||||
|
||||
NS_ASSERTION(mSavedStates, "Invalid call to PopState()!");
|
||||
|
||||
if (!mSavedStates) {
|
||||
return;
|
||||
}
|
||||
|
||||
// mFrameInfoMap is LIFO so keep removing what it points
|
||||
// to until we hit mLastFrame.
|
||||
|
||||
while (mFrameInfoMap && mFrameInfoMap->mFrame != mSavedStates->mLastFrame) {
|
||||
while (mFrameInfoMap && mFrameInfoMap->mFrame != aState->mLastFrame) {
|
||||
RemoveRegion(mFrameInfoMap->mFrame);
|
||||
}
|
||||
|
||||
|
@ -1064,39 +1029,13 @@ nsSpaceManager::PopState()
|
|||
// removed mLastFrame from mFrameInfoMap, which means our
|
||||
// state is now out of sync with what we thought it should be.
|
||||
|
||||
NS_ASSERTION(((mSavedStates->mLastFrame && mFrameInfoMap) ||
|
||||
(!mSavedStates->mLastFrame && !mFrameInfoMap)),
|
||||
NS_ASSERTION(((aState->mLastFrame && mFrameInfoMap) ||
|
||||
(!aState->mLastFrame && !mFrameInfoMap)),
|
||||
"Unexpected outcome!");
|
||||
|
||||
mX = mSavedStates->mX;
|
||||
mY = mSavedStates->mY;
|
||||
mLowestTop = mSavedStates->mLowestTop;
|
||||
|
||||
// Now that we've restored our state, pop the topmost
|
||||
// state and delete it. Make sure not to delete the mAutoState element
|
||||
// as it is embedded in this class
|
||||
|
||||
SpaceManagerState *state = mSavedStates;
|
||||
mSavedStates = mSavedStates->mNext;
|
||||
if(state != &mAutoState) {
|
||||
delete state;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSpaceManager::DiscardState()
|
||||
{
|
||||
NS_ASSERTION(mSavedStates, "Invalid call to DiscardState()!");
|
||||
|
||||
if (!mSavedStates) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpaceManagerState *state = mSavedStates;
|
||||
mSavedStates = mSavedStates->mNext;
|
||||
if(state != &mAutoState) {
|
||||
delete state;
|
||||
}
|
||||
mX = aState->mX;
|
||||
mY = aState->mY;
|
||||
mLowestTop = aState->mLowestTop;
|
||||
}
|
||||
|
||||
nscoord
|
||||
|
|
|
@ -282,6 +282,17 @@ protected:
|
|||
nsresult RemoveRegion(nsIFrame* aFrame);
|
||||
|
||||
public:
|
||||
// Structure that stores the current state of a frame manager for
|
||||
// Save/Restore purposes.
|
||||
struct SavedState {
|
||||
private:
|
||||
nsIFrame *mLastFrame;
|
||||
nscoord mX, mY;
|
||||
nscoord mLowestTop;
|
||||
|
||||
friend class nsSpaceManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the list of regions representing the unavailable space.
|
||||
*/
|
||||
|
@ -309,21 +320,19 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Pushes the current state of the space manager onto a state stack.
|
||||
* Saves the current state of the space manager into aState.
|
||||
*/
|
||||
void PushState();
|
||||
void PushState(SavedState* aState);
|
||||
|
||||
/**
|
||||
* Restores the space manager to the state at the top of the state stack,
|
||||
* then pops this state off the stack.
|
||||
* Restores the space manager to the saved state.
|
||||
*
|
||||
* These states must be managed using stack discipline. PopState can only
|
||||
* be used after PushState has been used to save the state, and it can only
|
||||
* be used once --- although it can be omitted; saved states can be ignored.
|
||||
* States must be popped in the reverse order they were pushed.
|
||||
*/
|
||||
void PopState();
|
||||
|
||||
/**
|
||||
* Pops the state off the stack without restoring it. Useful for speculative
|
||||
* reflow where we're not sure if we're going to keep the result.
|
||||
*/
|
||||
void DiscardState();
|
||||
void PopState(SavedState* aState);
|
||||
|
||||
/**
|
||||
* Get the top of the last region placed into the space manager, to
|
||||
|
@ -359,15 +368,6 @@ protected:
|
|||
#endif
|
||||
};
|
||||
|
||||
// Structure that stores the current state of a frame manager for
|
||||
// Save/Restore purposes.
|
||||
struct SpaceManagerState {
|
||||
nscoord mX, mY;
|
||||
nsIFrame *mLastFrame;
|
||||
nscoord mLowestTop;
|
||||
SpaceManagerState *mNext;
|
||||
};
|
||||
|
||||
public:
|
||||
// Doubly linked list of band rects
|
||||
struct BandRect : PRCListStr {
|
||||
|
@ -440,9 +440,6 @@ protected:
|
|||
FrameInfo* mFrameInfoMap;
|
||||
nsIntervalSet mFloatDamage;
|
||||
|
||||
SpaceManagerState *mSavedStates;
|
||||
SpaceManagerState mAutoState;
|
||||
|
||||
protected:
|
||||
FrameInfo* GetFrameInfoFor(nsIFrame* aFrame);
|
||||
FrameInfo* CreateFrameInfo(nsIFrame* aFrame, const nsRect& aRect);
|
||||
|
|
Загрузка…
Ссылка в новой задаче