diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 774a1299056..f4246bb1136 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -2735,13 +2735,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(); @@ -3498,9 +3499,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; } @@ -3517,19 +3519,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; @@ -3756,12 +3751,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, @@ -3775,45 +3764,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); } @@ -3844,9 +3849,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); @@ -3896,7 +3902,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 @@ -3943,7 +3949,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); @@ -3975,7 +3981,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. @@ -3987,7 +3993,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 { @@ -4020,6 +4025,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. @@ -4047,9 +4059,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; @@ -4142,12 +4154,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; } @@ -4190,7 +4202,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; } @@ -4240,7 +4252,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; } @@ -4322,11 +4334,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"); @@ -4373,6 +4409,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 diff --git a/mozilla/layout/generic/nsBlockFrame.h b/mozilla/layout/generic/nsBlockFrame.h index bb8b72b6478..915256ebdc1 100644 --- a/mozilla/layout/generic/nsBlockFrame.h +++ b/mozilla/layout/generic/nsBlockFrame.h @@ -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, diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp index 0a384c303a9..4af7fb826ae 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.cpp +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -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); diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index fdfbabecf78..c490a85f987 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -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) { diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index 771a9504de1..0f0e4158a65 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -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. diff --git a/mozilla/layout/generic/nsLineBox.cpp b/mozilla/layout/generic/nsLineBox.cpp index 38b3283d9fa..a4c28005c23 100644 --- a/mozilla/layout/generic/nsLineBox.cpp +++ b/mozilla/layout/generic/nsLineBox.cpp @@ -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() { diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h index a59123923d1..f495736b222 100644 --- a/mozilla/layout/generic/nsLineBox.h +++ b/mozilla/layout/generic/nsLineBox.h @@ -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; diff --git a/mozilla/layout/generic/nsSpaceManager.cpp b/mozilla/layout/generic/nsSpaceManager.cpp index b128e28abd9..1be30d02bad 100644 --- a/mozilla/layout/generic/nsSpaceManager.cpp +++ b/mozilla/layout/generic/nsSpaceManager.cpp @@ -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 diff --git a/mozilla/layout/generic/nsSpaceManager.h b/mozilla/layout/generic/nsSpaceManager.h index d77d6af2262..a84df4e99a0 100644 --- a/mozilla/layout/generic/nsSpaceManager.h +++ b/mozilla/layout/generic/nsSpaceManager.h @@ -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);