relanding fix for bug 317278 :-(

This commit is contained in:
roc+%cs.cmu.edu 2006-06-29 01:19:48 +00:00
Родитель b9a4cff178
Коммит 9fbb95a56f
9 изменённых файлов: 260 добавлений и 197 удалений

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

@ -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);