diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index ccaa656aa91f..247f65d42886 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -45,6 +45,7 @@ #include "nsCSSPseudoElements.h" #include "nsIView.h" #include "nsIScrollableView.h" +#include "nsPlaceholderFrame.h" /** * A namespace class for static layout utilities. @@ -174,6 +175,23 @@ nsLayoutUtils::GetPageFrame(nsIFrame* aFrame) return nsnull; } +nsIFrame* +nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { + if (nsLayoutAtoms::placeholderFrame != aFrame->GetType()) { + return nsnull; + } + + nsIFrame *outOfFlowFrame = + NS_STATIC_CAST(nsPlaceholderFrame*, aFrame)->GetOutOfFlowFrame(); + // This is a hack. + if (outOfFlowFrame && + !outOfFlowFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) { + return outOfFlowFrame; + } + + return nsnull; +} + // static PRBool nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 7ebd5cf17e9a..5e1cc200d7e2 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -199,7 +199,12 @@ public: } return pseudoContext != nsnull; } - + + /** + * If this frame is a placeholder for a float, then return the float, + * otherwise return nsnull. + */ + static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPossiblePlaceholder); }; #endif // nsLayoutUtils_h__ diff --git a/layout/base/public/nsLayoutUtils.h b/layout/base/public/nsLayoutUtils.h index 7ebd5cf17e9a..5e1cc200d7e2 100644 --- a/layout/base/public/nsLayoutUtils.h +++ b/layout/base/public/nsLayoutUtils.h @@ -199,7 +199,12 @@ public: } return pseudoContext != nsnull; } - + + /** + * If this frame is a placeholder for a float, then return the float, + * otherwise return nsnull. + */ + static nsIFrame* GetFloatFromPlaceholder(nsIFrame* aPossiblePlaceholder); }; #endif // nsLayoutUtils_h__ diff --git a/layout/base/src/nsLayoutUtils.cpp b/layout/base/src/nsLayoutUtils.cpp index ccaa656aa91f..247f65d42886 100644 --- a/layout/base/src/nsLayoutUtils.cpp +++ b/layout/base/src/nsLayoutUtils.cpp @@ -45,6 +45,7 @@ #include "nsCSSPseudoElements.h" #include "nsIView.h" #include "nsIScrollableView.h" +#include "nsPlaceholderFrame.h" /** * A namespace class for static layout utilities. @@ -174,6 +175,23 @@ nsLayoutUtils::GetPageFrame(nsIFrame* aFrame) return nsnull; } +nsIFrame* +nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) { + if (nsLayoutAtoms::placeholderFrame != aFrame->GetType()) { + return nsnull; + } + + nsIFrame *outOfFlowFrame = + NS_STATIC_CAST(nsPlaceholderFrame*, aFrame)->GetOutOfFlowFrame(); + // This is a hack. + if (outOfFlowFrame && + !outOfFlowFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) { + return outOfFlowFrame; + } + + return nsnull; +} + // static PRBool nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent, diff --git a/layout/base/src/nsSpaceManager.cpp b/layout/base/src/nsSpaceManager.cpp index b58fc392fb48..10b14f8b0493 100644 --- a/layout/base/src/nsSpaceManager.cpp +++ b/layout/base/src/nsSpaceManager.cpp @@ -46,6 +46,7 @@ #include "nsIPresShell.h" #include "nsMemory.h" #include "nsHTMLReflowState.h" +#include "nsHashSets.h" #ifdef DEBUG #include "nsIFrameDebug.h" #endif @@ -105,7 +106,6 @@ MOZ_DECL_CTOR_COUNTER(nsSpaceManager) nsSpaceManager::nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame) : mFrame(aFrame), - mXMost(0), mLowestTop(NSCOORD_MIN), mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell) { @@ -199,7 +199,11 @@ void nsSpaceManager::Shutdown() PRBool nsSpaceManager::XMost(nscoord& aXMost) const { - aXMost = mXMost; + nscoord xMost = 0; + for (FrameInfo* fi = mFrameInfoMap; fi; fi = fi->mNext) { + xMost = PR_MAX(xMost, fi->mRect.XMost()); + } + aXMost = xMost; return !mBandList.IsEmpty(); } @@ -813,10 +817,6 @@ nsSpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); - nscoord xmost = rect.XMost(); - if (xmost > mXMost) - mXMost = xmost; - if (rect.y > mLowestTop) mLowestTop = rect.y; @@ -843,6 +843,30 @@ nsSpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) return NS_OK; } +nsresult +nsSpaceManager::RemoveTrailingRegions(nsIFrame* aFrameList) { + nsVoidHashSet frameSet; + + frameSet.Init(1); + for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) { + frameSet.Put(f); + } + + // Pop frame regions off as long as they're in the set of frames to + // remove + while (mFrameInfoMap && frameSet.Contains(mFrameInfoMap->mFrame)) { + RemoveRegion(mFrameInfoMap->mFrame); + } + +#ifdef DEBUG + for (FrameInfo* frameInfo = mFrameInfoMap; frameInfo; + frameInfo = frameInfo->mNext) { + NS_ASSERTION(!frameSet.Contains(frameInfo->mFrame), + "Frame region deletion was requested but we couldn't delete it"); + } +#endif +} + nsresult nsSpaceManager::RemoveRegion(nsIFrame* aFrame) { @@ -995,7 +1019,6 @@ nsSpaceManager::PushState() state->mX = mX; state->mY = mY; - state->mXMost = mXMost; state->mLowestTop = mLowestTop; if (mFrameInfoMap) { @@ -1042,7 +1065,6 @@ nsSpaceManager::PopState() mX = mSavedStates->mX; mY = mSavedStates->mY; - mXMost = mSavedStates->mXMost; mLowestTop = mSavedStates->mLowestTop; // Now that we've restored our state, pop the topmost diff --git a/layout/base/src/nsSpaceManager.h b/layout/base/src/nsSpaceManager.h index fb8e45897369..e825ad9cd0d6 100644 --- a/layout/base/src/nsSpaceManager.h +++ b/layout/base/src/nsSpaceManager.h @@ -251,13 +251,28 @@ public: nsresult AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); + /** + * Remove the regions associated with this floating frame and its + * next-sibling list. Some of the frames may never have been added; + * we just skip those. This is not fully general; it only works as + * long as the N frames to be removed are the last N frames to have + * been added; if there's a frame in the middle of them that should + * not be removed, YOU LOSE. + * + * This can only be done at the end of the life of this space manager. The only + * methods it is safe to call after this are XMost() and YMost(). + */ + nsresult RemoveTrailingRegions(nsIFrame* aFrameList); + +protected: /** * Remove the region associated with aFrane. * + * doesn't work in the general case! + * * Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region * tagged with aFrame */ -protected: /* doesn't work in the general case */ nsresult RemoveRegion(nsIFrame* aFrame); public: @@ -335,7 +350,6 @@ protected: struct SpaceManagerState { nscoord mX, mY; nsIFrame *mLastFrame; - nscoord mXMost; nscoord mLowestTop; SpaceManagerState *mNext; }; @@ -408,7 +422,6 @@ protected: nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space BandList mBandList; // header/sentinel for circular linked list of band rects - nscoord mXMost; nscoord mLowestTop; // the lowest *top* FrameInfo* mFrameInfoMap; nsIntervalSet mFloatDamage; diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index de4ab6f7672f..eb78a3b97045 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -78,6 +78,7 @@ #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" #endif +#include "nsLayoutUtils.h" #ifdef IBMBIDI #include "nsBidiPresUtils.h" @@ -658,6 +659,12 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, FinishAndStoreOverflow(&aMetrics); +#ifdef DEBUG + if (gNoisy) { + gNoiseIndent--; + } +#endif + return NS_OK; } } @@ -921,8 +928,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // XXX_perf get rid of this! This is one of the things that makes // incremental reflow O(N^2). - BuildFloatList(); - + BuildFloatList(state); + // Compute our final size ComputeFinalSize(aReflowState, state, aMetrics); FinishAndStoreOverflow(&aMetrics); @@ -2066,6 +2073,20 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) return rv; } if (!keepGoing) { +#ifdef DEBUG + if (gNoisyReflow) { + gNoiseIndent--; + nsRect lca(line->GetCombinedArea()); + IndentBy(stdout, gNoiseIndent); + printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n", + NS_STATIC_CAST(void*, line.get()), aState.mY, + line->mBounds.x, line->mBounds.y, + line->mBounds.width, line->mBounds.height, + lca.x, lca.y, lca.width, lca.height, + deltaY, aState.mPrevBottomMargin.get(), + line->GetChildCount()); + } +#endif if (0 == line->GetChildCount()) { DeleteLine(aState, line, line_end); } @@ -6479,38 +6500,30 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // This is used to scan overflow frames for any float placeholders, // and add their floats to the list represented by aHead and aTail. We // only search the inline descendants. -static void CollectOverflowFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, - nsIFrame** aHead, nsIFrame** aTail) { +static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, + nsIFrame** aHead, nsIFrame** aTail) { while (aFrame) { // Don't descend into block children if (!aFrame->GetStyleDisplay()->IsBlockLevel()) { - if (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) { - nsIFrame *outOfFlowFrame = - NS_STATIC_CAST(nsPlaceholderFrame*, aFrame)->GetOutOfFlowFrame(); + nsIFrame *outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); + if (outOfFlowFrame) { // Make sure that its parent is the block we care // about. Otherwise we don't want to mess around with it because // it belongs to someone else. I think this could happen if the // overflow lines contain a block descendant which owns its own // floats. - if (outOfFlowFrame && - !outOfFlowFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) { - NS_ASSERTION(outOfFlowFrame->GetParent() == aBlockParent, - "Out of flow frame doesn't have the expected parent"); - // It's not an absolute or fixed positioned frame, so it must - // be a float! - // XXX This is a lame-o way of detecting a float, but it's the - // only way apparently - if (!*aHead) { - *aHead = *aTail = outOfFlowFrame; - } else { - (*aTail)->SetNextSibling(outOfFlowFrame); - *aTail = outOfFlowFrame; - } + NS_ASSERTION(outOfFlowFrame->GetParent() == aBlockParent, + "Out of flow frame doesn't have the expected parent"); + if (!*aHead) { + *aHead = *aTail = outOfFlowFrame; + } else { + (*aTail)->SetNextSibling(outOfFlowFrame); + *aTail = outOfFlowFrame; } } - CollectOverflowFloats(aFrame->GetFirstChild(nsnull), aBlockParent, - aHead, aTail); + CollectFloats(aFrame->GetFirstChild(nsnull), aBlockParent, + aHead, aTail); } aFrame = aFrame->GetNextSibling(); @@ -6519,7 +6532,7 @@ static void CollectOverflowFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, //XXX get rid of this -- its slow void -nsBlockFrame::BuildFloatList() +nsBlockFrame::BuildFloatList(nsBlockReflowState& aState) { // Accumulate float list into mFloats. // Use the float cache to speed up searching the lines for floats. @@ -6558,11 +6571,24 @@ nsBlockFrame::BuildFloatList() head = nsnull; current = nsnull; - CollectOverflowFloats(overflowLines->front()->mFirstChild, - this, &head, ¤t); + CollectFloats(overflowLines->front()->mFirstChild, + this, &head, ¤t); if (current) { current->SetNextSibling(nsnull); + + // Floats that were pushed should be removed from our space + // manager. Otherwise the space manager's YMost or XMost might + // be larger than necessary, causing this block to get an + // incorrect desired height (or width). Some of these floats + // may not actually have been added to the space manager because + // they weren't reflowed before being pushed; that's OK, + // RemoveRegions will ignore them. It is safe to do this here + // because we know from here on the space manager will only be + // used for its XMost and YMost, not to place new floats and + // lines. + aState.mSpaceManager->RemoveTrailingRegions(head); + nsFrameList* frameList = new nsFrameList(head); if (frameList) { SetOverflowOutOfFlows(frameList); diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 73177fde2dc0..d42dcfc78114 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -518,7 +518,7 @@ protected: nsLineBox* aLine, nscoord aDeltaY); - void BuildFloatList(); + void BuildFloatList(nsBlockReflowState& aState); //---------------------------------------- // List handling kludge diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index e90efc276c5c..c644b5d9a863 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -818,7 +818,7 @@ nsBlockReflowState::CanPlaceFloat(const nsRect& aFloatRect, } void -nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, +nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus) { @@ -858,6 +858,9 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, GetAvailableSpace(); } + NS_ASSERTION(floatFrame->GetParent() == mBlock, + "Float frame has wrong parent"); + // Reflow the float mBlock->ReflowFloat(*this, placeholder, aFloatCache, aReflowStatus); diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index c7d204173c3d..03e94b4c984f 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -65,6 +65,7 @@ #include "nsHTMLAtoms.h" #include "nsTextFragment.h" #include "nsBidiUtils.h" +#include "nsLayoutUtils.h" #ifdef DEBUG #undef NOISY_HORIZONTAL_ALIGN @@ -1003,25 +1004,22 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, if (frameType) { if (nsLayoutAtoms::placeholderFrame == frameType) { pfd->SetFlag(PFD_ISPLACEHOLDERFRAME, PR_TRUE); - nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)aFrame)->GetOutOfFlowFrame(); + nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); if (outOfFlowFrame) { - // Make sure it's floated and not absolutely positioned - const nsStyleDisplay* display = outOfFlowFrame->GetStyleDisplay(); - if (!display->IsAbsolutelyPositioned()) { - if (eReflowReason_Incremental == reason) { - InitFloat((nsPlaceholderFrame*)aFrame, aReflowStatus); - } - else { - AddFloat((nsPlaceholderFrame*)aFrame, aReflowStatus); - } - if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) { - SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); - // An incomplete reflow status means we should split the - // float if the height is constrained (bug 145305). We - // never split floating first letters. - if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) - aReflowStatus = NS_FRAME_COMPLETE; - } + nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, aFrame); + if (eReflowReason_Incremental == reason) { + InitFloat(placeholder, aReflowStatus); + } + else { + AddFloat(placeholder, aReflowStatus); + } + if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) { + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); + // An incomplete reflow status means we should split the + // float if the height is constrained (bug 145305). We + // never split floating first letters. + if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) + aReflowStatus = NS_FRAME_COMPLETE; } } } diff --git a/layout/generic/nsSpaceManager.cpp b/layout/generic/nsSpaceManager.cpp index b58fc392fb48..10b14f8b0493 100644 --- a/layout/generic/nsSpaceManager.cpp +++ b/layout/generic/nsSpaceManager.cpp @@ -46,6 +46,7 @@ #include "nsIPresShell.h" #include "nsMemory.h" #include "nsHTMLReflowState.h" +#include "nsHashSets.h" #ifdef DEBUG #include "nsIFrameDebug.h" #endif @@ -105,7 +106,6 @@ MOZ_DECL_CTOR_COUNTER(nsSpaceManager) nsSpaceManager::nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame) : mFrame(aFrame), - mXMost(0), mLowestTop(NSCOORD_MIN), mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell) { @@ -199,7 +199,11 @@ void nsSpaceManager::Shutdown() PRBool nsSpaceManager::XMost(nscoord& aXMost) const { - aXMost = mXMost; + nscoord xMost = 0; + for (FrameInfo* fi = mFrameInfoMap; fi; fi = fi->mNext) { + xMost = PR_MAX(xMost, fi->mRect.XMost()); + } + aXMost = xMost; return !mBandList.IsEmpty(); } @@ -813,10 +817,6 @@ nsSpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) nsRect rect(aUnavailableSpace.x + mX, aUnavailableSpace.y + mY, aUnavailableSpace.width, aUnavailableSpace.height); - nscoord xmost = rect.XMost(); - if (xmost > mXMost) - mXMost = xmost; - if (rect.y > mLowestTop) mLowestTop = rect.y; @@ -843,6 +843,30 @@ nsSpaceManager::AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace) return NS_OK; } +nsresult +nsSpaceManager::RemoveTrailingRegions(nsIFrame* aFrameList) { + nsVoidHashSet frameSet; + + frameSet.Init(1); + for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) { + frameSet.Put(f); + } + + // Pop frame regions off as long as they're in the set of frames to + // remove + while (mFrameInfoMap && frameSet.Contains(mFrameInfoMap->mFrame)) { + RemoveRegion(mFrameInfoMap->mFrame); + } + +#ifdef DEBUG + for (FrameInfo* frameInfo = mFrameInfoMap; frameInfo; + frameInfo = frameInfo->mNext) { + NS_ASSERTION(!frameSet.Contains(frameInfo->mFrame), + "Frame region deletion was requested but we couldn't delete it"); + } +#endif +} + nsresult nsSpaceManager::RemoveRegion(nsIFrame* aFrame) { @@ -995,7 +1019,6 @@ nsSpaceManager::PushState() state->mX = mX; state->mY = mY; - state->mXMost = mXMost; state->mLowestTop = mLowestTop; if (mFrameInfoMap) { @@ -1042,7 +1065,6 @@ nsSpaceManager::PopState() mX = mSavedStates->mX; mY = mSavedStates->mY; - mXMost = mSavedStates->mXMost; mLowestTop = mSavedStates->mLowestTop; // Now that we've restored our state, pop the topmost diff --git a/layout/generic/nsSpaceManager.h b/layout/generic/nsSpaceManager.h index fb8e45897369..e825ad9cd0d6 100644 --- a/layout/generic/nsSpaceManager.h +++ b/layout/generic/nsSpaceManager.h @@ -251,13 +251,28 @@ public: nsresult AddRectRegion(nsIFrame* aFrame, const nsRect& aUnavailableSpace); + /** + * Remove the regions associated with this floating frame and its + * next-sibling list. Some of the frames may never have been added; + * we just skip those. This is not fully general; it only works as + * long as the N frames to be removed are the last N frames to have + * been added; if there's a frame in the middle of them that should + * not be removed, YOU LOSE. + * + * This can only be done at the end of the life of this space manager. The only + * methods it is safe to call after this are XMost() and YMost(). + */ + nsresult RemoveTrailingRegions(nsIFrame* aFrameList); + +protected: /** * Remove the region associated with aFrane. * + * doesn't work in the general case! + * * Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region * tagged with aFrame */ -protected: /* doesn't work in the general case */ nsresult RemoveRegion(nsIFrame* aFrame); public: @@ -335,7 +350,6 @@ protected: struct SpaceManagerState { nscoord mX, mY; nsIFrame *mLastFrame; - nscoord mXMost; nscoord mLowestTop; SpaceManagerState *mNext; }; @@ -408,7 +422,6 @@ protected: nsIFrame* const mFrame; // frame associated with the space manager nscoord mX, mY; // translation from local to global coordinate space BandList mBandList; // header/sentinel for circular linked list of band rects - nscoord mXMost; nscoord mLowestTop; // the lowest *top* FrameInfo* mFrameInfoMap; nsIntervalSet mFloatDamage; diff --git a/layout/html/base/src/nsBlockFrame.cpp b/layout/html/base/src/nsBlockFrame.cpp index de4ab6f7672f..eb78a3b97045 100644 --- a/layout/html/base/src/nsBlockFrame.cpp +++ b/layout/html/base/src/nsBlockFrame.cpp @@ -78,6 +78,7 @@ #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" #endif +#include "nsLayoutUtils.h" #ifdef IBMBIDI #include "nsBidiPresUtils.h" @@ -658,6 +659,12 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, FinishAndStoreOverflow(&aMetrics); +#ifdef DEBUG + if (gNoisy) { + gNoiseIndent--; + } +#endif + return NS_OK; } } @@ -921,8 +928,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // XXX_perf get rid of this! This is one of the things that makes // incremental reflow O(N^2). - BuildFloatList(); - + BuildFloatList(state); + // Compute our final size ComputeFinalSize(aReflowState, state, aMetrics); FinishAndStoreOverflow(&aMetrics); @@ -2066,6 +2073,20 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) return rv; } if (!keepGoing) { +#ifdef DEBUG + if (gNoisyReflow) { + gNoiseIndent--; + nsRect lca(line->GetCombinedArea()); + IndentBy(stdout, gNoiseIndent); + printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n", + NS_STATIC_CAST(void*, line.get()), aState.mY, + line->mBounds.x, line->mBounds.y, + line->mBounds.width, line->mBounds.height, + lca.x, lca.y, lca.width, lca.height, + deltaY, aState.mPrevBottomMargin.get(), + line->GetChildCount()); + } +#endif if (0 == line->GetChildCount()) { DeleteLine(aState, line, line_end); } @@ -6479,38 +6500,30 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, // This is used to scan overflow frames for any float placeholders, // and add their floats to the list represented by aHead and aTail. We // only search the inline descendants. -static void CollectOverflowFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, - nsIFrame** aHead, nsIFrame** aTail) { +static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, + nsIFrame** aHead, nsIFrame** aTail) { while (aFrame) { // Don't descend into block children if (!aFrame->GetStyleDisplay()->IsBlockLevel()) { - if (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) { - nsIFrame *outOfFlowFrame = - NS_STATIC_CAST(nsPlaceholderFrame*, aFrame)->GetOutOfFlowFrame(); + nsIFrame *outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); + if (outOfFlowFrame) { // Make sure that its parent is the block we care // about. Otherwise we don't want to mess around with it because // it belongs to someone else. I think this could happen if the // overflow lines contain a block descendant which owns its own // floats. - if (outOfFlowFrame && - !outOfFlowFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) { - NS_ASSERTION(outOfFlowFrame->GetParent() == aBlockParent, - "Out of flow frame doesn't have the expected parent"); - // It's not an absolute or fixed positioned frame, so it must - // be a float! - // XXX This is a lame-o way of detecting a float, but it's the - // only way apparently - if (!*aHead) { - *aHead = *aTail = outOfFlowFrame; - } else { - (*aTail)->SetNextSibling(outOfFlowFrame); - *aTail = outOfFlowFrame; - } + NS_ASSERTION(outOfFlowFrame->GetParent() == aBlockParent, + "Out of flow frame doesn't have the expected parent"); + if (!*aHead) { + *aHead = *aTail = outOfFlowFrame; + } else { + (*aTail)->SetNextSibling(outOfFlowFrame); + *aTail = outOfFlowFrame; } } - CollectOverflowFloats(aFrame->GetFirstChild(nsnull), aBlockParent, - aHead, aTail); + CollectFloats(aFrame->GetFirstChild(nsnull), aBlockParent, + aHead, aTail); } aFrame = aFrame->GetNextSibling(); @@ -6519,7 +6532,7 @@ static void CollectOverflowFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, //XXX get rid of this -- its slow void -nsBlockFrame::BuildFloatList() +nsBlockFrame::BuildFloatList(nsBlockReflowState& aState) { // Accumulate float list into mFloats. // Use the float cache to speed up searching the lines for floats. @@ -6558,11 +6571,24 @@ nsBlockFrame::BuildFloatList() head = nsnull; current = nsnull; - CollectOverflowFloats(overflowLines->front()->mFirstChild, - this, &head, ¤t); + CollectFloats(overflowLines->front()->mFirstChild, + this, &head, ¤t); if (current) { current->SetNextSibling(nsnull); + + // Floats that were pushed should be removed from our space + // manager. Otherwise the space manager's YMost or XMost might + // be larger than necessary, causing this block to get an + // incorrect desired height (or width). Some of these floats + // may not actually have been added to the space manager because + // they weren't reflowed before being pushed; that's OK, + // RemoveRegions will ignore them. It is safe to do this here + // because we know from here on the space manager will only be + // used for its XMost and YMost, not to place new floats and + // lines. + aState.mSpaceManager->RemoveTrailingRegions(head); + nsFrameList* frameList = new nsFrameList(head); if (frameList) { SetOverflowOutOfFlows(frameList); diff --git a/layout/html/base/src/nsBlockFrame.h b/layout/html/base/src/nsBlockFrame.h index 73177fde2dc0..d42dcfc78114 100644 --- a/layout/html/base/src/nsBlockFrame.h +++ b/layout/html/base/src/nsBlockFrame.h @@ -518,7 +518,7 @@ protected: nsLineBox* aLine, nscoord aDeltaY); - void BuildFloatList(); + void BuildFloatList(nsBlockReflowState& aState); //---------------------------------------- // List handling kludge diff --git a/layout/html/base/src/nsBlockReflowState.cpp b/layout/html/base/src/nsBlockReflowState.cpp index e90efc276c5c..c644b5d9a863 100644 --- a/layout/html/base/src/nsBlockReflowState.cpp +++ b/layout/html/base/src/nsBlockReflowState.cpp @@ -818,7 +818,7 @@ nsBlockReflowState::CanPlaceFloat(const nsRect& aFloatRect, } void -nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, +nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus) { @@ -858,6 +858,9 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, GetAvailableSpace(); } + NS_ASSERTION(floatFrame->GetParent() == mBlock, + "Float frame has wrong parent"); + // Reflow the float mBlock->ReflowFloat(*this, placeholder, aFloatCache, aReflowStatus); diff --git a/layout/html/base/src/nsLineLayout.cpp b/layout/html/base/src/nsLineLayout.cpp index c7d204173c3d..03e94b4c984f 100644 --- a/layout/html/base/src/nsLineLayout.cpp +++ b/layout/html/base/src/nsLineLayout.cpp @@ -65,6 +65,7 @@ #include "nsHTMLAtoms.h" #include "nsTextFragment.h" #include "nsBidiUtils.h" +#include "nsLayoutUtils.h" #ifdef DEBUG #undef NOISY_HORIZONTAL_ALIGN @@ -1003,25 +1004,22 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, if (frameType) { if (nsLayoutAtoms::placeholderFrame == frameType) { pfd->SetFlag(PFD_ISPLACEHOLDERFRAME, PR_TRUE); - nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)aFrame)->GetOutOfFlowFrame(); + nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); if (outOfFlowFrame) { - // Make sure it's floated and not absolutely positioned - const nsStyleDisplay* display = outOfFlowFrame->GetStyleDisplay(); - if (!display->IsAbsolutelyPositioned()) { - if (eReflowReason_Incremental == reason) { - InitFloat((nsPlaceholderFrame*)aFrame, aReflowStatus); - } - else { - AddFloat((nsPlaceholderFrame*)aFrame, aReflowStatus); - } - if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) { - SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); - // An incomplete reflow status means we should split the - // float if the height is constrained (bug 145305). We - // never split floating first letters. - if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) - aReflowStatus = NS_FRAME_COMPLETE; - } + nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, aFrame); + if (eReflowReason_Incremental == reason) { + InitFloat(placeholder, aReflowStatus); + } + else { + AddFloat(placeholder, aReflowStatus); + } + if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) { + SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); + // An incomplete reflow status means we should split the + // float if the height is constrained (bug 145305). We + // never split floating first letters. + if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) + aReflowStatus = NS_FRAME_COMPLETE; } } }