From 30604288b9ffa6fb0cc6118a896988a9061c9b1a Mon Sep 17 00:00:00 2001 From: "roc+%cs.cmu.edu" Date: Wed, 23 Mar 2005 03:35:08 +0000 Subject: [PATCH] Bug 263825. Make paginated floats work, and other fixes. r+sr=dbaron --- layout/base/nsFrameManager.cpp | 1 + layout/base/nsLayoutAtomList.h | 2 +- layout/generic/nsAbsoluteContainingBlock.cpp | 9 +- layout/generic/nsBlockFrame.cpp | 1349 +++++++++++------- layout/generic/nsBlockFrame.h | 124 +- layout/generic/nsBlockReflowContext.cpp | 311 ++-- layout/generic/nsBlockReflowContext.h | 3 +- layout/generic/nsBlockReflowState.cpp | 111 +- layout/generic/nsBlockReflowState.h | 33 +- layout/generic/nsColumnSetFrame.cpp | 17 + layout/generic/nsFrame.cpp | 22 + layout/generic/nsFrameList.cpp | 60 +- layout/generic/nsFrameList.h | 8 + layout/generic/nsHTMLReflowState.h | 24 +- layout/generic/nsInlineFrame.cpp | 2 +- layout/generic/nsLineBox.cpp | 8 +- layout/generic/nsLineLayout.cpp | 9 +- layout/generic/nsLineLayout.h | 8 +- layout/generic/nsPlaceholderFrame.cpp | 21 + layout/generic/nsPlaceholderFrame.h | 2 + layout/generic/nsSpaceManager.cpp | 10 +- 21 files changed, 1350 insertions(+), 784 deletions(-) diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index cfd9b780688..64a68722fd4 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -538,6 +538,7 @@ nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame) NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!"); entry->placeholderFrame = aPlaceholderFrame; + return NS_OK; } diff --git a/layout/base/nsLayoutAtomList.h b/layout/base/nsLayoutAtomList.h index 21be3db79f3..471b9ac7a5d 100644 --- a/layout/base/nsLayoutAtomList.h +++ b/layout/base/nsLayoutAtomList.h @@ -144,7 +144,7 @@ LAYOUT_ATOM(overflowAreaProperty, "OverflowArea") // nsRect* LAYOUT_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame* LAYOUT_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox* LAYOUT_ATOM(overflowOutOfFlowsProperty, "OverflowOutOfFlowsProperty") // nsFrameList* -LAYOUT_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty") // nsPlaceholder* +LAYOUT_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty") // nsFrameList* LAYOUT_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty") // nscoord* LAYOUT_ATOM(spaceManagerProperty, "SpaceManagerProperty") // the space manager for a block LAYOUT_ATOM(tableBCProperty, "TableBCProperty") // table border collapsing info (e.g. damage area, table border widths) diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 682d731c347..d6bb5219ea6 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -490,9 +490,7 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat PrettyUC(aReflowState.mComputedHeight, height); printf("c=%s,%s \n", width, height); } - if (nsBlockFrame::gNoisy) { - nsBlockFrame::gNoiseIndent++; - } + AutoNoisyIndenter indent(nsBlockFrame::gNoisy); #endif // DEBUG nsresult rv; @@ -665,11 +663,8 @@ nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegat } } #ifdef DEBUG - if (nsBlockFrame::gNoisy) { - nsBlockFrame::gNoiseIndent--; - } if (nsBlockFrame::gNoisyReflow) { - nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent); + nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1); printf("abs pos "); if (nsnull != aKidFrame) { nsIFrameDebug* frameDebug; diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 6f05d666803..2acfe21f83a 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -304,10 +304,11 @@ nsBlockFrame::Destroy(nsPresContext* aPresContext) if (overflowLines) { nsLineBox::DeleteLineList(aPresContext, *overflowLines); } - nsFrameList* overflowOutOfFlows = RemoveOverflowOutOfFlows(); - if (overflowOutOfFlows) { - overflowOutOfFlows->DestroyFrames(aPresContext); - delete overflowOutOfFlows; + + { + nsAutoOOFFrameList oofs(this); + oofs.mList.DestroyFrames(aPresContext); + // oofs is now empty and will remove the frame list property } return nsBlockFrameSuper::Destroy(aPresContext); @@ -498,8 +499,7 @@ nsBlockFrame::GetFirstChild(nsIAtom* aListName) const return overflowLines ? overflowLines->front()->mFirstChild : nsnull; } else if (aListName == nsLayoutAtoms::overflowOutOfFlowList) { - nsFrameList* oof = GetOverflowOutOfFlows(); - return oof ? oof->FirstChild() : nsnull; + return GetOverflowOutOfFlows().FirstChild(); } else if (aListName == nsLayoutAtoms::floatList) { return mFloats.FirstChild(); @@ -543,6 +543,25 @@ nsBlockFrame::IsFloatContainingBlock() const return PR_TRUE; } +static PRBool IsContinuationPlaceholder(nsIFrame* aFrame) +{ + return aFrame->GetPrevInFlow() && + nsLayoutAtoms::placeholderFrame == aFrame->GetType(); +} + +static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, + nsIFrame* aNewParent) { + NS_ASSERTION(aOldParent == aFrame->GetParent(), + "Parent not consistent with exepectations"); + + aFrame->SetParent(aNewParent); + + // When pushing and pulling frames we need to check for whether any + // views need to be reparented + nsHTMLContainerFrame::ReparentFrameView(aFrame->GetPresContext(), aFrame, + aOldParent, aNewParent); +} + ////////////////////////////////////////////////////////////////////// // Frame structure methods @@ -636,9 +655,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, aReflowState.availableWidth, aReflowState.availableHeight, aReflowState.mComputedWidth, aReflowState.mComputedHeight); } - if (gNoisy) { - gNoiseIndent++; - } + AutoNoisyIndenter indent(gNoisy); PRTime start = LL_ZERO; // Initialize these variablies to silence the compiler. PRInt32 ctc = 0; // We only use these if they are set (gLameReflowMetrics). if (gLameReflowMetrics) { @@ -697,12 +714,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, FinishAndStoreOverflow(&aMetrics); -#ifdef DEBUG - if (gNoisy) { - gNoiseIndent--; - } -#endif - return NS_OK; } @@ -768,7 +779,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // ALWAYS drain overflow. We never want to leave the previnflow's // overflow lines hanging around; block reflow depends on the // overflow line lists being cleared out between reflow passes. - DrainOverflowLines(); + DrainOverflowLines(state); switch (aReflowState.reason) { case eReflowReason_Initial: @@ -859,7 +870,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, if (NS_FAILED(rv)) return rv; // Now reflow... - rv = ReflowDirtyLines(state); + rv = ReflowDirtyLines(state, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed"); if (NS_FAILED(rv)) return rv; @@ -868,9 +879,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, // allows subsequent lines on the page to be impacted by floats. If the // block is incomplete or there is no ancestor using the same space manager, // put continued floats at the beginning of the first overflow line. - nsFrameList* overflowPlace = nsnull; if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) && - (overflowPlace = RemoveOverflowPlaceholders())) { + state.mOverflowPlaceholders.NotEmpty()) { PRBool gaveToAncestor = PR_FALSE; if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) { // find the nearest block ancestor that uses the same space manager @@ -879,29 +889,28 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, ancestorRS = ancestorRS->parentReflowState) { nsIFrame* ancestor = ancestorRS->frame; nsIAtom* fType = ancestor->GetType(); - if ((nsLayoutAtoms::blockFrame == fType) || (nsLayoutAtoms::areaFrame == fType)) { - if (aReflowState.mSpaceManager == ancestorRS->mSpaceManager) { - // Put the continued floats in ancestor since it uses the same space manager - nsFrameList* ancestorPlace = - ((nsBlockFrame*)ancestor)->GetOverflowPlaceholders(); - if (ancestorPlace) { - ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild()); - } - else { - // ancestor doesn't have an overflow place holder list, so - // create one. Note that we use AppendFrames() to add the - // frames, instead of passing them into the constructor, so - // we can levarage the code in AppendFrames() which updates - // the parent for each frame in the list. - - ancestorPlace = new nsFrameList(); - if (ancestorPlace) { - ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild()); - ((nsBlockFrame*)ancestor)->SetOverflowPlaceholders(ancestorPlace); - } - else - return NS_ERROR_OUT_OF_MEMORY; + if ((nsLayoutAtoms::blockFrame == fType || nsLayoutAtoms::areaFrame == fType) && + aReflowState.mSpaceManager == ancestorRS->mSpaceManager) { + // Put the continued floats in ancestor since it uses the same space manager + nsFrameList* ancestorPlace = + ((nsBlockFrame*)ancestor)->GetOverflowPlaceholders(); + // The ancestor should have this list, since it's being reflowed. But maybe + // it isn't because of reflow roots or something. + if (ancestorPlace) { + for (nsIFrame* f = state.mOverflowPlaceholders.FirstChild(); + f; f = f->GetNextSibling()) { + NS_ASSERTION(IsContinuationPlaceholder(f), + "Overflow placeholders must be continuation placeholders"); + ReparentFrame(f, this, ancestorRS->frame); + nsIFrame* oof = NS_STATIC_CAST(nsPlaceholderFrame*, f)->GetOutOfFlowFrame(); + mFloats.RemoveFrame(oof); + ReparentFrame(oof, this, ancestorRS->frame); + // Clear the next-sibling in case the frame wasn't in mFloats + oof->SetNextSibling(nsnull); + NS_STATIC_CAST(nsBlockFrame*, ancestorRS->frame) + ->mFloats.AppendFrame(nsnull, oof); } + ancestorPlace->AppendFrames(nsnull, state.mOverflowPlaceholders.FirstChild()); gaveToAncestor = PR_TRUE; break; } @@ -909,40 +918,30 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, } } if (!gaveToAncestor) { - PRInt32 numOverflowPlace = overflowPlace->GetLength(); - nsLineList* overflowLines = GetOverflowLines(); - if (overflowLines) { - nsPlaceholderFrame* lastPlaceholder = - (nsPlaceholderFrame*)overflowPlace->LastChild(); - line_iterator firstLine = overflowLines->begin(); - nsIFrame* firstFrame = firstLine->mFirstChild; - - // hook up the last placeholder with the existing overflow frames - NS_ASSERTION(firstFrame != lastPlaceholder, "trying to set next sibling to self"); - lastPlaceholder->SetNextSibling(firstFrame); - if (firstLine->IsBlock()) { - // Create a new line as the first line and put the floats there; - nsLineBox* newLine = state.NewLineBox(overflowPlace->FirstChild(), numOverflowPlace, PR_FALSE); - overflowLines->push_front(newLine); + state.mOverflowPlaceholders.SortByContentOrder(); + PRInt32 numOverflowPlace = state.mOverflowPlaceholders.GetLength(); + nsLineBox* newLine = + state.NewLineBox(state.mOverflowPlaceholders.FirstChild(), + numOverflowPlace, PR_FALSE); + if (newLine) { + nsLineList* overflowLines = GetOverflowLines(); + if (overflowLines) { + // Put the new placeholders *last* in the overflow lines + // because they might have previnflows in the overflow lines. + nsIFrame* lastChild = overflowLines->back()->LastChild(); + lastChild->SetNextSibling(state.mOverflowPlaceholders.FirstChild()); + // Create a new line as the last line and put the + // placeholders there + overflowLines->push_back(newLine); } - else { // floats go on 1st overflow line - firstLine->mFirstChild = overflowPlace->FirstChild(); - firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowPlace); + else { + mLines.push_back(newLine); + nsLineList::iterator nextToLastLine = ----end_lines(); + PushLines(state, nextToLastLine); } } - else { - // Create a line, put the floats in it, and then push. - nsLineBox* newLine = state.NewLineBox(overflowPlace->FirstChild(), numOverflowPlace, PR_FALSE); - if (!newLine) - return NS_ERROR_OUT_OF_MEMORY; - mLines.push_back(newLine); - nsLineList::iterator nextToLastLine = ----end_lines(); - PushLines(state, nextToLastLine); - } state.mReflowStatus |= NS_FRAME_NOT_COMPLETE | NS_FRAME_REFLOW_NEXTINFLOW; } - - delete overflowPlace; } if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) { @@ -1061,9 +1060,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext, aStatus = state.mReflowStatus; #ifdef DEBUG - if (gNoisy) { - gNoiseIndent--; - } if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); ListTag(stdout); @@ -1318,7 +1314,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, aMetrics, aReflowState.mFlags.mHasClearance || (NS_BLOCK_MARGIN_ROOT & mState), (NS_BLOCK_MARGIN_ROOT & mState)); - ReflowDirtyLines(state); + // Don't try to pull up any new content from our next in flow. Shrinkwrapping + // shouldn't make us shorter anyway. + ReflowDirtyLines(state, PR_FALSE); aState.mY = state.mY; NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); } @@ -1878,7 +1876,7 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) #ifdef REALLY_NOISY_REFLOW if (!line->IsBlock()) { printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n", - line, line->IsImpactedByFloat() ? "" : "not "); + line.get(), line->IsImpactedByFloat() ? "" : "not "); } #endif #ifdef DEBUG @@ -2036,8 +2034,6 @@ DirtyLinesWithDirtyContinuations(const nsLineList::iterator& aLineStart, } static void PlaceFrameView(nsIFrame* aFrame); -static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, - nsIFrame** aHead, nsIFrame** aTail); static PRBool LineHasClear(nsLineBox* aLine) { return aLine->GetBreakTypeBefore() || aLine->HasFloatBreakAfter() @@ -2045,54 +2041,47 @@ static PRBool LineHasClear(nsLineBox* aLine) { } -static void ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, - nsIFrame* aNewParent) { - aFrame->SetParent(aNewParent); - - // When pushing and pulling frames we need to check for whether any - // views need to be reparented - nsHTMLContainerFrame::ReparentFrameView(aFrame->GetPresContext(), aFrame, - aOldParent, aNewParent); -} - /** * Reparent a whole list of floats from aOldParent to this block. The - * floats might be taken from aOldParent's overflow list. Whichever - * list they're on, they must be the first floats in the list. They - * will be removed from the list. + * floats might be taken from aOldParent's overflow list. They will be + * removed from the list. They end up appended to our mFloats list. */ void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, PRBool aFromOverflow) { - nsIFrame* head = nsnull; + nsFrameList list; nsIFrame* tail = nsnull; - CollectFloats(aFirstFrame, aOldParent, &head, &tail); - if (head) { - if (aFromOverflow) { - nsFrameList* oofs = aOldParent->GetOverflowOutOfFlows(); - NS_ASSERTION(oofs && head == oofs->FirstChild(), "Floats out of order"); - if (tail->GetNextSibling()) { - oofs->SetFrames(tail->GetNextSibling()); - } else { - delete aOldParent->RemoveOverflowOutOfFlows(); - } - } else { - NS_ASSERTION(head == aOldParent->mFloats.FirstChild(), - "Floats out of order"); - aOldParent->mFloats.SetFrames(tail->GetNextSibling()); - } - for (nsIFrame* f = head; f != tail->GetNextSibling(); - f = f->GetNextSibling()) { + aOldParent->CollectFloats(aFirstFrame, list, &tail, aFromOverflow); + if (list.NotEmpty()) { + for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) { ReparentFrame(f, aOldParent, this); } + mFloats.AppendFrames(nsnull, list.FirstChild()); } } +static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine, + nscoord aDeltaY, PRInt32 aDeltaIndent) { +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsRect lca(aLine->GetCombinedArea()); + nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent); + printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n", + NS_STATIC_CAST(void*, aLine), aState.mY, + aLine->IsDirty() ? "yes" : "no", + aLine->mBounds.x, aLine->mBounds.y, + aLine->mBounds.width, aLine->mBounds.height, + lca.x, lca.y, lca.width, lca.height, + aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount()); + } +#endif +} + /** * Reflow the dirty lines */ nsresult -nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) +nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState, PRBool aTryPull) { nsresult rv = NS_OK; PRBool keepGoing = PR_TRUE; @@ -2118,8 +2107,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) printf(": reflowing dirty lines"); } printf(" computedWidth=%d\n", aState.mReflowState.mComputedWidth); - gNoiseIndent++; } + AutoNoisyIndenter indent(gNoisyReflow); #endif // Check whether we need to do invalidation for the child block @@ -2136,6 +2125,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // recompute the carried out margin before the line if we want to // reflow it or if its previous margin is dirty PRBool needToRecoverState = PR_FALSE; + PRBool reflowedFloat = PR_FALSE; PRBool lastLineMovedUp = PR_FALSE; // We save up information about BR-clearance here PRUint8 inlineFloatBreakType = NS_STYLE_CLEAR_NONE; @@ -2150,19 +2140,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) // Reflow the lines that are already ours for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) { + DumpLine(aState, line, deltaY, 0); #ifdef DEBUG - if (gNoisyReflow) { - nsRect lca(line->GetCombinedArea()); - IndentBy(stdout, gNoiseIndent); - printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n", - NS_STATIC_CAST(void*, line.get()), aState.mY, - line->IsDirty() ? "yes" : "no", - line->mBounds.x, line->mBounds.y, - line->mBounds.width, line->mBounds.height, - lca.x, lca.y, lca.width, lca.height, - deltaY, aState.mPrevBottomMargin.get()); - gNoiseIndent++; - } + AutoNoisyIndenter indent2(gNoisyReflow); #endif // This really sucks, but we have to look inside any blocks that have clear @@ -2271,21 +2251,13 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) if (NS_FAILED(rv)) { return rv; } + + if (line->HasFloats()) { + reflowedFloat = PR_TRUE; + } + 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 + DumpLine(aState, line, deltaY, -1); if (0 == line->GetChildCount()) { DeleteLine(aState, line, line_end); } @@ -2366,19 +2338,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) foundAnyClears = PR_TRUE; } -#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\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()); - } -#endif + DumpLine(aState, line, deltaY, -1); } if (needToRecoverState) { @@ -2396,10 +2356,19 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) ::PlaceFrameView(this); // We can skip trying to pull up the next line if there is no next - // in flow or if the next in flow is not changing and we cannot have - // added more space for its first line to be pulled up into. - if (!aState.mNextInFlow || - (aState.mReflowState.mFlags.mNextInFlowUntouched && !lastLineMovedUp)) { + // in flow or we were told not to or we know it will be futile, i.e., + // -- the next in flow is not changing + // -- and we cannot have added more space for its first line to be + // pulled up into, + // -- and it's a not a resize reflow (so our width didn't change), + // -- and we didn't reflow any floats (so the available space + // didn't change) + if (!aState.mNextInFlow || !aTryPull || + (aState.mReflowState.mFlags.mNextInFlowUntouched && + !lastLineMovedUp && + (aState.mReflowState.reason == eReflowReason_Incremental || + aState.mReflowState.reason == eReflowReason_Dirty) && + !reflowedFloat)) { if (aState.mNextInFlow) { aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE; } @@ -2413,12 +2382,21 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) nsLineBox *toMove; PRBool collectOverflowFloats; if (nifLine != nextInFlow->end_lines()) { + if (HandleOverflowPlaceholdersOnPulledLine(aState, nifLine)) { + // go around again in case the line was deleted + continue; + } toMove = nifLine; nextInFlow->mLines.erase(nifLine); collectOverflowFloats = PR_FALSE; } else { // Grab an overflow line if there are any - nsLineList* overflowLines = nextInFlow->RemoveOverflowLines(); + nsLineList* overflowLines = nextInFlow->GetOverflowLines(); + if (overflowLines && + HandleOverflowPlaceholdersOnPulledLine(aState, overflowLines->front())) { + // go around again in case the line was deleted + continue; + } if (!overflowLines) { aState.mNextInFlow = NS_STATIC_CAST(nsBlockFrame*, nextInFlow->mNextInFlow); @@ -2428,6 +2406,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) NS_ASSERTION(nifLine != overflowLines->end(), "Stored overflow line list should not be empty"); toMove = nifLine; + nextInFlow->RemoveOverflowLines(); nifLine = overflowLines->erase(nifLine); if (nifLine != overflowLines->end()) { // We need to this remove-and-put-back dance because we want @@ -2458,6 +2437,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) } lastFrame->SetNextSibling(nsnull); + // Reparent floats whose placeholders are in the line. + ReparentFloats(toMove->mFirstChild, nextInFlow, collectOverflowFloats); + // Add line to our line list if (aState.mPrevChild) { aState.mPrevChild->SetNextSibling(toMove->mFirstChild); @@ -2465,37 +2447,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) line = mLines.before_insert(end_lines(), toMove); - // Reparent floats whose placeholders are in the line. We need - // to do this differently depending on whether the line is an - // overflow line or not. - if (collectOverflowFloats) { - ReparentFloats(line->mFirstChild, nextInFlow, PR_TRUE); - } else { - // If line contains floats, remove them from aState.mNextInFlow's - // float list. They will be pushed onto this blockframe's float - // list, via BuildFloatList(), when we are done reflowing dirty lines. - // - // XXX: If the call to BuildFloatList() is removed from - // nsBlockFrame::Reflow(), we'll probably need to manually - // append the floats to |this|'s float list. - // - // We don't need to fix the line's float cache because we're - // next going to reflow the line, which will wipe and - // rebuild the float cache. - if (line->HasFloats()) { - nsFloatCache* fc = line->GetFirstFloat(); - while (fc) { - if (fc->mPlaceholder) { - nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame(); - if (floatFrame) { - nextInFlow->mFloats.RemoveFrame(floatFrame); - ReparentFrame(floatFrame, nextInFlow, this); - } - } - fc = fc->Next(); - } - } - } + DumpLine(aState, toMove, deltaY, 0); +#ifdef DEBUG + AutoNoisyIndenter indent2(gNoisyReflow); +#endif // Now reflow it and any lines that it makes during it's reflow // (we have to loop here because reflowing the line may case a new @@ -2507,21 +2462,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) NS_WARNING("Line reflow failed"); return rv; } + DumpLine(aState, line, deltaY, -1); 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); } @@ -2561,8 +2503,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) #ifdef DEBUG if (gNoisyReflow) { - gNoiseIndent--; - IndentBy(stdout, gNoiseIndent); + IndentBy(stdout, gNoiseIndent - 1); ListTag(stdout); printf(": done reflowing dirty lines (status=%x)\n", aState.mReflowStatus); @@ -2815,8 +2756,13 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, // First check our remaining lines if (end_lines() != aLine.next()) { - return PullFrameFrom(aState, aLine, this, PR_FALSE, aLine.next(), - aDamageDeletedLines, aFrameResult); +#ifdef DEBUG + PRBool retry = +#endif + PullFrameFrom(aState, aLine, this, PR_FALSE, aLine.next(), + aDamageDeletedLines, aFrameResult); + NS_ASSERTION(!retry, "Shouldn't have to retry in the current block"); + return NS_OK; } NS_ASSERTION(!GetOverflowLines(), @@ -2827,16 +2773,24 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, while (nextInFlow) { // first normal lines, then overflow lines if (!nextInFlow->mLines.empty()) { - return PullFrameFrom(aState, aLine, nextInFlow, PR_FALSE, - nextInFlow->mLines.begin(), - aDamageDeletedLines, aFrameResult); + if (PullFrameFrom(aState, aLine, nextInFlow, PR_FALSE, + nextInFlow->mLines.begin(), + aDamageDeletedLines, aFrameResult)) { + // try again with the same value of nextInFlow + continue; + } + break; } nsLineList* overflowLines = nextInFlow->GetOverflowLines(); if (overflowLines) { - return PullFrameFrom(aState, aLine, nextInFlow, PR_TRUE, - overflowLines->begin(), - aDamageDeletedLines, aFrameResult); + if (PullFrameFrom(aState, aLine, nextInFlow, PR_TRUE, + overflowLines->begin(), + aDamageDeletedLines, aFrameResult)) { + // try again with the same value of nextInFlow + continue; + } + break; } nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow; @@ -2847,8 +2801,7 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, } /** - * Try to pull a frame out of a line pointed at by aFromLine. If a frame - * is pulled then aPulled will be set to PR_TRUE. In addition, if + * Try to pull a frame out of a line pointed at by aFromLine. If * aUpdateGeometricParent is set then the pulled frames geometric parent * will be updated (e.g. when pulling from a next-in-flows line list). * @@ -2858,8 +2811,10 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState, * emptied (and destroyed) or the line gets reflowed (because we mark * it dirty) and the code at the top of ReflowLine empties the * array. So eventually, it will be removed, just not right away. + * + * @return PR_TRUE to force retrying of the pull. */ -nsresult +PRBool nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aLine, nsBlockFrame* aFromContainer, @@ -2873,6 +2828,9 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line"); NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line"); + NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->GetStyleDisplay()->IsBlockLevel(), + "Disagreement about whether it's a block or not"); + if (fromLine->IsBlock()) { // If our line is not empty and the child in aFromLine is a block // then we cannot pull up the frame into this line. In this case @@ -2882,8 +2840,19 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, else { // Take frame from fromLine nsIFrame* frame = fromLine->mFirstChild; - aLine->SetChildCount(aLine->GetChildCount() + 1); + if (aFromContainer != this) { + if (HandleOverflowPlaceholdersForPulledFrame(aState, frame)) { + // we lost this one, retry + return PR_TRUE; + } + + aLine->LastChild()->SetNextSibling(frame); + } + // when aFromContainer is 'this', then aLine->LastChild()'s next sibling + // is already set correctly. + aLine->SetChildCount(aLine->GetChildCount() + 1); + PRInt32 fromLineChildCount = fromLine->GetChildCount(); if (0 != --fromLineChildCount) { // Mark line dirty now that we pulled a child @@ -2942,7 +2911,7 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState, VerifyLines(PR_TRUE); #endif } - return NS_OK; + return PR_FALSE; } static void @@ -3211,21 +3180,29 @@ void nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState, nsIFrame* aLastPlaceholder) { - nsIFrame* undoPlaceholder = nsnull; + nsIFrame* undoPlaceholder; if (aLastPlaceholder) { undoPlaceholder = aLastPlaceholder->GetNextSibling(); aLastPlaceholder->SetNextSibling(nsnull); } else { - // just remove the property - nsFrameList* overflowPlace = RemoveOverflowPlaceholders(); - delete overflowPlace; + undoPlaceholder = aState.mOverflowPlaceholders.FirstChild(); + aState.mOverflowPlaceholders.SetFrames(nsnull); } // remove the next in flows of the placeholders that need to be removed - for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) { + for (nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, undoPlaceholder); + placeholder; ) { + NS_ASSERTION(!placeholder->GetNextInFlow(), "Must be the last placeholder"); + + nsFrameManager* fm = aState.mPresContext->GetPresShell()->FrameManager(); + fm->UnregisterPlaceholderFrame(placeholder); + placeholder->SetOutOfFlowFrame(nsnull); + + // XXX we probably should be doing something with oof here. But what? + nsSplittableFrame::RemoveFromFlow(placeholder); nsIFrame* savePlaceholder = placeholder; - placeholder = placeholder->GetNextSibling(); + placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, placeholder->GetNextSibling()); savePlaceholder->Destroy(aState.mPresContext); } } @@ -3308,8 +3285,9 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsIFrame* clearanceFrame = nsnull; nscoord startingY = aState.mY; nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin; + nscoord clearance; while (PR_TRUE) { - nscoord clearance = 0; + clearance = 0; nscoord topMargin = 0; PRBool mayNeedRetry = PR_FALSE; if (applyTopMargin) { @@ -3411,7 +3389,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // Compute the available space for the block aState.GetAvailableSpace(); #ifdef REALLY_NOISY_REFLOW - printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false"); + printf("setting line %p isImpacted to %s\n", aLine.get(), aState.IsImpactedByFloat()?"true":"false"); #endif PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE; aLine->SetLineIsImpactedByFloat(isImpacted); @@ -3429,8 +3407,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, } // keep track of the last overflow float in case we need to undo any new additions - nsFrameList* overflowPlace = GetOverflowPlaceholders(); - nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull; + nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild(); // Reflow the block into the available space nsMargin computedOffsets; @@ -3462,6 +3439,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, if (mayNeedRetry) { if (clearanceFrame) { + UndoSplitPlaceholders(aState, lastPlaceholder); aState.mSpaceManager->PopState(); aState.mY = startingY; aState.mPrevBottomMargin = incomingMargin; @@ -3490,14 +3468,19 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, else { // Note: line-break-after a block is a nop - // Try to place the child block - PRBool isAdjacentWithTop = aState.IsAdjacentWithTop(); + // Try to place the child block. + // Don't force the block to fit if we have positive clearance, because + // pushing it to the next page would give it more room. + // Don't force the block to fit if it's impacted by a float. If it is, + // then pushing it to the next page would give it more room. Note that + // isImpacted doesn't include impact from the block's own floats. + PRBool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 && + !isImpacted; nsCollapsingMargin collapsedBottomMargin; nsRect combinedArea(0,0,0,0); - *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop, - aLine.get(), + *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(), computedOffsets, collapsedBottomMargin, - aLine->mBounds, combinedArea); + aLine->mBounds, combinedArea, frameReflowStatus); if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) { line_iterator nextLine = aLine; ++nextLine; @@ -3556,19 +3539,25 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, // then we'd better reflow our continuation if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; - // We also need to make that continuation's line dirty so it - // gets reflowed when we reflow our next in flow. The - // nif's line must always be either the first line - // of the nif's parent block or else one of our own overflow - // lines. In the latter case the line is already marked dirty, - // so just detect and handle the first case. - nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent()); - NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame - || nifBlock->GetType() == nsLayoutAtoms::areaFrame, - "A block's child's next in flow's parent must be a block!"); - line_iterator firstLine = nifBlock->begin_lines(); - if (firstLine != nifBlock->end_lines() && firstLine->Contains(nextFrame)) { - firstLine->MarkDirty(); + // We also need to make that continuation's line dirty so + // it gets reflowed when we reflow our next in flow. The + // nif's line must always be either a line of the nif's + // parent block (only if we didn't make a continuation) or + // else one of our own overflow lines. In the latter case + // the line is already marked dirty, so just handle the + // first case. + if (!madeContinuation) { + nsBlockFrame* nifBlock = NS_STATIC_CAST(nsBlockFrame*, nextFrame->GetParent()); + NS_ASSERTION(nifBlock->GetType() == nsLayoutAtoms::blockFrame + || nifBlock->GetType() == nsLayoutAtoms::areaFrame, + "A block's child's next in flow's parent must be a block!"); + for (line_iterator line = nifBlock->begin_lines(), + line_end = nifBlock->end_lines(); line != line_end; ++line) { + if (line->Contains(nextFrame)) { + line->MarkDirty(); + break; + } + } } } *aKeepReflowGoing = PR_FALSE; @@ -3819,8 +3808,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, } // keep track of the last overflow float in case we need to undo any new additions - nsFrameList* overflowPlace = GetOverflowPlaceholders(); - nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull; + nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild(); // Reflow the frames that are already on the line first nsresult rv = NS_OK; @@ -3828,9 +3816,16 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, PRInt32 i; nsIFrame* frame = aLine->mFirstChild; aLine->SetHasPercentageChild(PR_FALSE); // To be set by ReflowInlineFrame below + // Determine whether this is a line of placeholders for out-of-flow + // continuations + PRBool isContinuingPlaceholders = PR_FALSE; + // need to repeatedly call GetChildCount here, because the child // count can change during the loop! - for (i = 0; i < aLine->GetChildCount(); i++) { + for (i = 0; i < aLine->GetChildCount(); i++) { + if (IsContinuationPlaceholder(frame)) { + isContinuingPlaceholders = PR_TRUE; + } rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame, &lineReflowStatus); if (NS_FAILED(rv)) { @@ -3863,34 +3858,39 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, frame = frame->GetNextSibling(); } - // Pull frames and reflow them until we can't - while (LINE_REFLOW_OK == lineReflowStatus) { - rv = PullFrame(aState, aLine, aDamageDirtyArea, frame); - if (NS_FAILED(rv)) { - return rv; - } - if (nsnull == frame) { - break; - } + // Don't pull up new frames into lines with continuation placeholders + if (!isContinuingPlaceholders) { + // Pull frames and reflow them until we can't while (LINE_REFLOW_OK == lineReflowStatus) { - PRInt32 oldCount = aLine->GetChildCount(); - rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame, - &lineReflowStatus); + rv = PullFrame(aState, aLine, aDamageDirtyArea, frame); if (NS_FAILED(rv)) { return rv; } - if (aLine->GetChildCount() != oldCount) { - // We just created a continuation for aFrame AND its going - // to end up on this line (e.g. :first-letter - // situation). Therefore we have to loop here before trying - // to pull another frame. - frame = frame->GetNextSibling(); - } - else { + if (nsnull == frame) { break; } + + while (LINE_REFLOW_OK == lineReflowStatus) { + PRInt32 oldCount = aLine->GetChildCount(); + rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame, + &lineReflowStatus); + if (NS_FAILED(rv)) { + return rv; + } + if (aLine->GetChildCount() != oldCount) { + // We just created a continuation for aFrame AND its going + // to end up on this line (e.g. :first-letter + // situation). Therefore we have to loop here before trying + // to pull another frame. + frame = frame->GetNextSibling(); + } + else { + break; + } + } } } + if (LINE_REFLOW_REDO == 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 @@ -3903,8 +3903,28 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height, "unconstrained height on totally empty line"); - aState.mY += aState.mAvailSpaceRect.height; + if (aState.mAvailSpaceRect.height > 0) { + aState.mY += aState.mAvailSpaceRect.height; + } else { + NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight, + "We shouldn't be running out of height here"); + if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableHeight) { + // just move it down a bit to try to get out of this mess + aState.mY += 1; + } else { + // There's nowhere to retry placing the line. Just treat it as if + // we placed the float but it was truncated so we need this line + // to go to the next page/column. + lineReflowStatus = LINE_REFLOW_TRUNCATED; + // Don't push any lines if we just want to calculate the maximum width + if (!aUpdateMaximumWidth) { + // Push the line that didn't fit + PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing); + } + } + } + // 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 @@ -3970,6 +3990,18 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus, nsnull, pushedFrame); + if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { + // we need to ensure that the frame's nextinflow gets reflowed. + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; + nsBlockFrame* ourNext = NS_STATIC_CAST(nsBlockFrame*, GetNextInFlow()); + if (ourNext && aFrame->GetNextInFlow()) { + line_iterator f = ourNext->FindLineFor(aFrame->GetNextInFlow()); + if (f != ourNext->end_lines()) { + f->MarkDirty(); + } + } + } + // If this is an incremental reflow, prune the child from the path // so we don't incrementally reflow it again. // XXX note that we don't currently have any incremental reflows @@ -4098,7 +4130,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // frame may already have a continuation. PRBool madeContinuation; rv = (nsLayoutAtoms::placeholderFrame == frameType) - ? SplitPlaceholder(*aState.mPresContext, *aFrame) + ? SplitPlaceholder(aState, aFrame) : CreateContinuationFor(aState, aLine, aFrame, madeContinuation); if (NS_FAILED(rv)) return rv; @@ -4111,7 +4143,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // If we are reflowing the first letter frame or a placeholder then // don't split the line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter && - (nsLayoutAtoms::placeholderFrame != frameType); + nsLayoutAtoms::placeholderFrame != frameType; if (reflowingFirstLetter) { if ((nsLayoutAtoms::inlineFrame == frameType) || (nsLayoutAtoms::lineFrame == frameType)) { @@ -4176,30 +4208,28 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState, } nsresult -nsBlockFrame::SplitPlaceholder(nsPresContext& aPresContext, - nsIFrame& aPlaceholder) +nsBlockFrame::SplitPlaceholder(nsBlockReflowState& aState, + nsIFrame* aPlaceholder) { nsIFrame* nextInFlow; - nsresult rv = CreateNextInFlow(&aPresContext, this, &aPlaceholder, nextInFlow); + nsresult rv = CreateNextInFlow(aState.mPresContext, this, aPlaceholder, nextInFlow); if (NS_FAILED(rv)) return rv; + + if (!nextInFlow) { + // Next in flow was not created because it already exists. + return NS_OK; + } + // put the sibling list back to what it was before the continuation was created - nsIFrame *contFrame = aPlaceholder.GetNextSibling(); + nsIFrame *contFrame = aPlaceholder->GetNextSibling(); nsIFrame *next = contFrame->GetNextSibling(); - aPlaceholder.SetNextSibling(next); + aPlaceholder->SetNextSibling(next); contFrame->SetNextSibling(nsnull); - // add the placehoder to the overflow floats - nsFrameList* overflowPlace = GetOverflowPlaceholders(); - if (overflowPlace) { - overflowPlace->AppendFrames(this, contFrame); - } - else { - overflowPlace = new nsFrameList(contFrame); - if (overflowPlace) { - SetOverflowPlaceholders(overflowPlace); - } - else return NS_ERROR_NULL_POINTER; - } + + NS_ASSERTION(IsContinuationPlaceholder(contFrame), + "Didn't create the right kind of frame"); + aState.mOverflowPlaceholders.AppendFrame(this, contFrame); return NS_OK; } @@ -4235,6 +4265,8 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState, if (0 != pushCount) { NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push"); NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops"); + NS_ASSERTION(nsFrameList(aFrame).GetLength() >= pushCount, + "Not enough frames to push"); // Put frames being split out into their own line nsLineBox* newLine = aState.NewLineBox(aFrame, pushCount, PR_FALSE); @@ -4460,9 +4492,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // Stop reflow and whack the reflow status if reflow hasn't // already been stopped. if (*aKeepReflowGoing) { - NS_ASSERTION(NS_FRAME_COMPLETE == aState.mReflowStatus, - "lost reflow status"); - aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; + aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE; *aKeepReflowGoing = PR_FALSE; } return PR_TRUE; @@ -4513,8 +4543,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, // If we just want to calculate the maximum width, then don't push the line. // We'll reflow it again and then it will get pushed if necessary. if (!aUpdateMaximumWidth) { - nsFrameList* overflowPlace = GetOverflowPlaceholders(); - nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull; + nsIFrame* lastPlaceholder = aState.mOverflowPlaceholders.LastChild(); PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing); } } @@ -4614,30 +4643,43 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState, PRBool firstLine = overBegin == begin_lines(); if (overBegin != end_lines()) { - // XXXldb use presshell arena! - nsLineList* overflowLines = new nsLineList(); - overflowLines->splice(overflowLines->end(), mLines, overBegin, - end_lines()); - NS_ASSERTION(!overflowLines->empty(), "should not be empty"); + // overflow lines can already exist in some cases, in particular, + // when shrinkwrapping and we discover that the shrinkwap causes + // the height of some child block to grow which creates additional + // overflowing content. In such cases we must prepend the new + // overflow to the existing overflow. + nsLineList* overflowLines = RemoveOverflowLines(); + if (!overflowLines) { + // XXXldb use presshell arena! + overflowLines = new nsLineList(); + } + if (overflowLines) { + if (!overflowLines->empty()) { + mLines.back()->LastChild()->SetNextSibling(overflowLines->front()->mFirstChild); + } + overflowLines->splice(overflowLines->begin(), mLines, overBegin, + end_lines()); + NS_ASSERTION(!overflowLines->empty(), "should not be empty"); // this takes ownership but it won't delete it immediately so we // can keep using it. - SetOverflowLines(overflowLines); + SetOverflowLines(overflowLines); - // Mark all the overflow lines dirty so that they get reflowed when - // they are pulled up by our next-in-flow. + // Mark all the overflow lines dirty so that they get reflowed when + // they are pulled up by our next-in-flow. - // XXXldb Can this get called O(N) times making the whole thing O(N^2)? - for (line_iterator line = overflowLines->begin(), - line_end = overflowLines->end(); - line != line_end; - ++line) - { - line->MarkDirty(); - line->MarkPreviousMarginDirty(); - line->mBounds.SetRect(0, 0, 0, 0); - if (line->HasFloats()) { - line->FreeFloats(aState.mFloatCacheFreeList); - } + // XXXldb Can this get called O(N) times making the whole thing O(N^2)? + for (line_iterator line = overflowLines->begin(), + line_end = overflowLines->end(); + line != line_end; + ++line) + { + line->MarkDirty(); + line->MarkPreviousMarginDirty(); + line->mBounds.SetRect(0, 0, 0, 0); + if (line->HasFloats()) { + line->FreeFloats(aState.mFloatCacheFreeList); + } + } } } @@ -4650,95 +4692,350 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState, #endif } +/** + * Call this when a frame will be pulled from the block's + * next-in-flow into this frame. If it's a continuation placeholder, + * it should not be here so we push it into our overflow placeholders + * list. To avoid creating holes (e.g., the following block doesn't + * have a placeholder but the block after it does) we also need to + * pull all the following placeholders and put them in our overflow + * placeholders list too. + * + * If it's a first-in-flow placeholder, or it contains one, then we + * need to do this to the continuation placeholders. + * + * We return PR_TRUE if we removed the frame and it cannot be used. + * If we return PR_FALSE then the frame *must* be pulled immediately. + */ +PRBool +nsBlockFrame::HandleOverflowPlaceholdersForPulledFrame( + nsBlockReflowState& aState, nsIFrame* aFrame) +{ + if (nsLayoutAtoms::placeholderFrame != aFrame->GetType()) { + // Check the in-flow inline children. We should encounter only + // first-in-flow placeholders, so the frame subtree rooted at + // aFrame should not change. + if (!aFrame->GetStyleDisplay()->IsBlockLevel()) { + for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) { +#ifdef DEBUG + PRBool changed = +#endif + HandleOverflowPlaceholdersForPulledFrame(aState, f); + NS_ASSERTION(!changed, "Shouldn't find any continuation placeholders inside inlines"); + } + } + return PR_FALSE; + } + + PRBool taken = PR_TRUE; + nsIFrame* frame = aFrame; + if (!aFrame->GetPrevInFlow()) { + // First in flow frame. We only want to deal with its + // next in flows, if there are any. + taken = PR_FALSE; + frame = frame->GetNextInFlow(); + if (!frame) + return PR_FALSE; + } + + nsBlockFrame* parent = NS_STATIC_CAST(nsBlockFrame*, frame->GetParent()); + // Remove aFrame and all its next in flows from their parents, but + // don't destroy the frames. +#ifdef DEBUG + nsresult rv = +#endif + parent->DoRemoveFrame(frame, PR_FALSE); + NS_ASSERTION(NS_SUCCEEDED(rv), "frame should be in parent's lists"); + + nsIFrame* lastOverflowPlace = aState.mOverflowPlaceholders.LastChild(); + while (frame) { + NS_ASSERTION(IsContinuationPlaceholder(frame), + "Should only be dealing with continuation placeholders here"); + + parent = NS_STATIC_CAST(nsBlockFrame*, frame->GetParent()); + ReparentFrame(frame, parent, this); + + // continuation placeholders are always direct children of a block + nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, frame); + nsIFrame* outOfFlow = placeholder->GetOutOfFlowFrame(); + + if (!parent->mFloats.RemoveFrame(outOfFlow)) { + nsAutoOOFFrameList oofs(parent); +#ifdef DEBUG + PRBool found = +#endif + oofs.mList.RemoveFrame(outOfFlow); + NS_ASSERTION(found, "Must have the out of flow in some child list"); + } + ReparentFrame(outOfFlow, parent, this); + + aState.mOverflowPlaceholders.AppendFrame(nsnull, frame); + // outOfFlow isn't inserted anywhere yet. Eventually the overflow + // placeholders get put into the overflow lines, then + // BuildFloatList collects their out-of-flows and puts them on the + // overflow out-of-flows list. + lastOverflowPlace = frame; + + frame = frame->GetNextInFlow(); + } + + return taken; +} + +/** + * Call this when a line will be pulled from the block's + * next-in-flow's line. + * + * @return PR_TRUE we consumed the entire line, delete it and try again + */ +PRBool +nsBlockFrame::HandleOverflowPlaceholdersOnPulledLine( + nsBlockReflowState& aState, nsLineBox* aLine) +{ + // First, see if it's a line of continuation placeholders. If it + // is, remove one and retry. + if (aLine->mFirstChild && IsContinuationPlaceholder(aLine->mFirstChild)) { + PRBool taken = + HandleOverflowPlaceholdersForPulledFrame(aState, aLine->mFirstChild); + NS_ASSERTION(taken, "We must have removed that frame"); + return PR_TRUE; + } + + // OK, it's a normal line. Scan it for floats with continuations that + // need to be taken care of. We won't need to change the line. + PRInt32 n = aLine->GetChildCount(); + for (nsIFrame* f = aLine->mFirstChild; n > 0; f = f->GetNextSibling(), --n) { +#ifdef DEBUG + PRBool taken = +#endif + HandleOverflowPlaceholdersForPulledFrame(aState, f); + NS_ASSERTION(!taken, "Shouldn't be any continuation placeholders on this line"); + } + + return PR_FALSE; +} + // The overflowLines property is stored as a pointer to a line list, // which must be deleted. However, the following functions all maintain // the invariant that the property is never set if the list is empty. PRBool -nsBlockFrame::DrainOverflowLines() +nsBlockFrame::DrainOverflowLines(nsBlockReflowState& aState) { #ifdef DEBUG VerifyOverflowSituation(); #endif - PRBool drained = PR_FALSE; - nsLineList* overflowLines; + nsLineList* overflowLines = nsnull; + nsLineList* ourOverflowLines = nsnull; // First grab the prev-in-flows overflow lines nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow; - if (nsnull != prevBlock) { + if (prevBlock) { overflowLines = prevBlock->RemoveOverflowLines(); - if (nsnull != overflowLines) { + if (overflowLines) { NS_ASSERTION(! overflowLines->empty(), "overflow lines should never be set and empty"); - drained = PR_TRUE; - // Make all the frames on the overflow line list mine - nsIFrame* lastFrame = nsnull; nsIFrame* frame = overflowLines->front()->mFirstChild; while (nsnull != frame) { ReparentFrame(frame, prevBlock, this); // Get the next frame - lastFrame = frame; frame = frame->GetNextSibling(); } - // The lines on the overflow list have already been marked dirty and their - // previous margins marked dirty also. - - // Join the line lists - NS_ASSERTION(lastFrame, "overflow list was created with no frames"); - if (! mLines.empty()) - { - // Remember to recompute the margins on the first line. This will - // also recompute the correct deltaY if necessary. - mLines.front()->MarkPreviousMarginDirty(); - // Join the sibling lists together - lastFrame->SetNextSibling(mLines.front()->mFirstChild); + // make the overflow out-of-flow frames mine too + nsAutoOOFFrameList oofs(prevBlock); + if (oofs.mList.NotEmpty()) { + for (nsIFrame* f = oofs.mList.FirstChild(); f; f = f->GetNextSibling()) { + ReparentFrame(f, prevBlock, this); + } + mFloats.InsertFrames(nsnull, nsnull, oofs.mList.FirstChild()); + oofs.mList.SetFrames(nsnull); } + } + + // The lines on the overflow list have already been marked dirty and their + // previous margins marked dirty also. + } + + // Don't need to reparent frames in our own overflow lines/oofs, because they're + // already ours. But we should put overflow floats back in mFloats. + ourOverflowLines = RemoveOverflowLines(); + if (ourOverflowLines) { + nsAutoOOFFrameList oofs(this); + if (oofs.mList.NotEmpty()) { + mFloats.InsertFrames(nsnull, nsnull, oofs.mList.FirstChild()); + oofs.mList.SetFrames(nsnull); + } + } + + if (!overflowLines && !ourOverflowLines) { + // nothing to do; always the case for non-constrained-height reflows + return PR_FALSE; + } + + NS_ASSERTION(aState.mOverflowPlaceholders.IsEmpty(), + "Should have no overflow placeholders yet"); + + // All continuation placeholders need to be moved to the front of + // our line list. We also need to maintain the invariant that at + // most one frame for a given piece of content is in our normal + // child list, by pushing all but the first placeholder to our + // overflow placeholders list + // + // One problem we have to deal with is that some of these + // continuation placeholders may have been donated to us by a + // descendant block that was complete. We need to push them down to + // a lower block if possible. + // + // We keep the lists ordered so that prev in flows come before their + // next in flows. We do not worry about properly ordering the + // placeholders for different content relative to each other until + // the end. Then we sort them. + nsIFrame* lastOP = nsnull; + nsFrameList keepPlaceholders; + nsIFrame* lastKP = nsnull; + nsLineList* lineLists[3] = { overflowLines, &mLines, ourOverflowLines }; + static const PRPackedBool searchFirstLinesOnly[3] = { PR_FALSE, PR_TRUE, PR_FALSE }; + for (PRInt32 i = 0; i < 3; ++i) { + nsLineList* ll = lineLists[i]; + if (ll && !ll->empty()) { + line_iterator iter = ll->begin(); + line_iterator iter_end = ll->end(); + nsIFrame* lastFrame = nsnull; + while (iter != iter_end) { + PRUint32 n = iter->GetChildCount(); + if (n == 0 || !IsContinuationPlaceholder(iter->mFirstChild)) { + if (lastFrame) { + lastFrame->SetNextSibling(iter->mFirstChild); + } + if (searchFirstLinesOnly[i]) { + break; + } + lastFrame = iter->LastChild(); + ++iter; + } else { + nsLineBox* line = iter; + iter = ll->erase(iter); + nsIFrame* next; + for (nsPlaceholderFrame* f = NS_STATIC_CAST(nsPlaceholderFrame*, line->mFirstChild); + n > 0; --n, f = NS_STATIC_CAST(nsPlaceholderFrame*, next)) { + NS_ASSERTION(IsContinuationPlaceholder(f), + "Line frames should all be continuation placeholders"); + next = f->GetNextSibling(); + nsIFrame* fpif = f->GetPrevInFlow(); + PRBool isAncestor = nsLayoutUtils::IsProperAncestorFrame(this, fpif); + if (isAncestor) { + // oops. we already have a prev-in-flow for this + // placeholder. We have to move this frame out of here. We + // can put it in our overflow placeholders. + aState.mOverflowPlaceholders.InsertFrame(nsnull, lastOP, f); + lastOP = f; + } else { + if (fpif->GetParent() == prevBlock) { + keepPlaceholders.InsertFrame(nsnull, lastKP, f); + lastKP = f; + } else { + // Ok, now we're in the tough situation where some child + // of prevBlock was complete and pushed its overflow + // placeholders up to prevBlock's overflow. We might be + // able to find a more appropriate parent for f somewhere + // down in our descendants. + NS_ASSERTION(nsLayoutUtils::IsProperAncestorFrame(prevBlock, fpif), + "bad prev-in-flow ancestor chain"); + // Find the first ancestor of f's prev-in-flow that has a next in flow. + // That next in flow should become f's parent. + nsIFrame* fpAncestor; + for (fpAncestor = fpif->GetParent(); !fpAncestor->GetNextInFlow(); + fpAncestor = fpAncestor->GetParent()) + ; + if (fpAncestor == prevBlock) { + // We're still the best parent. + keepPlaceholders.InsertFrame(nsnull, lastKP, f); + lastKP = f; + } else { + // Just put it at the front of + // fpAncestor->GetNextInFlow()'s lines. + nsLineBox* newLine = aState.NewLineBox(f, 1, PR_FALSE); + if (newLine) { + nsBlockFrame* target = + NS_STATIC_CAST(nsBlockFrame*, fpAncestor->GetNextInFlow()); + if (!target->mLines.empty()) { + f->SetNextSibling(target->mLines.front()->mFirstChild); + } else { + f->SetNextSibling(nsnull); + } + target->mLines.push_front(newLine); + ReparentFrame(f, this, target); + + nsIFrame* oof = f->GetOutOfFlowFrame(); +#ifdef DEBUG + PRBool found = +#endif + mFloats.RemoveFrame(oof); + NS_ASSERTION(found, "Float should have been put in our mFloats list"); + target->mFloats.InsertFrame(nsnull, nsnull, oof); + ReparentFrame(oof, this, target); + } + } + } + } + } + aState.FreeLineBox(line); + } + } + if (lastFrame) { + lastFrame->SetNextSibling(nsnull); + } + } + } + + // Now join the line lists into mLines + if (overflowLines) { + if (!overflowLines->empty()) { + // Join the line lists + if (! mLines.empty()) + { + // Remember to recompute the margins on the first line. This will + // also recompute the correct deltaY if necessary. + mLines.front()->MarkPreviousMarginDirty(); + // Join the sibling lists together + nsIFrame* lastFrame = overflowLines->back()->LastChild(); + lastFrame->SetNextSibling(mLines.front()->mFirstChild); + } // Place overflow lines at the front of our line list mLines.splice(mLines.begin(), *overflowLines); NS_ASSERTION(overflowLines->empty(), "splice should empty list"); - delete overflowLines; - - nsFrameList* overflowOutOfFlows = prevBlock->RemoveOverflowOutOfFlows(); - if (overflowOutOfFlows) { - for (nsIFrame* f = overflowOutOfFlows->FirstChild(); f; - f = f->GetNextSibling()) { - ReparentFrame(f, prevBlock, this); - } - delete overflowOutOfFlows; - } } - } - - // Now grab our own overflow lines - overflowLines = RemoveOverflowLines(); - if (overflowLines) { - NS_ASSERTION(! overflowLines->empty(), - "overflow lines should never be set and empty"); - // This can happen when we reflow and not everything fits and then - // we are told to reflow again before a next-in-flow is created - // and reflows. - - if (! mLines.empty()) { - mLines.back()->LastChild()->SetNextSibling( - overflowLines->front()->mFirstChild ); - } - // append the overflow to mLines - mLines.splice(mLines.end(), *overflowLines); - drained = PR_TRUE; delete overflowLines; - - // Likewise, drain our own overflow out-of-flows. We don't need to - // reparent them since they're already our children. We don't need - // to put them on any child list since BuildFloatList will put - // them on some child list. All we need to do is remove the - // property. - nsFrameList* overflowOutOfFlows = RemoveOverflowOutOfFlows(); - delete overflowOutOfFlows; } - return drained; + if (ourOverflowLines) { + if (!ourOverflowLines->empty()) { + if (!mLines.empty()) { + mLines.back()->LastChild()-> + SetNextSibling(ourOverflowLines->front()->mFirstChild); + } + // append the overflow to mLines + mLines.splice(mLines.end(), *ourOverflowLines); + } + delete ourOverflowLines; + } + + // store the placeholders that we're keeping in our frame list + if (keepPlaceholders.NotEmpty()) { + keepPlaceholders.SortByContentOrder(); + nsLineBox* newLine = aState.NewLineBox(keepPlaceholders.FirstChild(), + keepPlaceholders.GetLength(), PR_FALSE); + if (newLine) { + if (!mLines.empty()) { + keepPlaceholders.LastChild()->SetNextSibling(mLines.front()->mFirstChild); + } + mLines.push_front(newLine); + } + } + + return PR_TRUE; } nsLineList* @@ -4803,54 +5100,35 @@ nsBlockFrame::SetOverflowLines(nsLineList* aOverflowLines) return rv; } -nsFrameList* +nsFrameList nsBlockFrame::GetOverflowOutOfFlows() const { if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { - return nsnull; + return nsFrameList(); } - nsFrameList* result = NS_STATIC_CAST(nsFrameList*, + nsIFrame* result = NS_STATIC_CAST(nsIFrame*, GetProperty(nsLayoutAtoms::overflowOutOfFlowsProperty)); NS_ASSERTION(result, "value should always be non-empty when state set"); - return result; + return nsFrameList(result); } -nsFrameList* -nsBlockFrame::RemoveOverflowOutOfFlows() +// This takes ownership of the frames +void +nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList) { - if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { - return nsnull; + if (aList.IsEmpty()) { + if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { + return; + } + nsIFrame* result = NS_STATIC_CAST(nsIFrame*, + UnsetProperty(nsLayoutAtoms::overflowOutOfFlowsProperty)); + NS_ASSERTION(result, "value should always be non-empty when state set"); + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); + } else { + SetProperty(nsLayoutAtoms::overflowOutOfFlowsProperty, + aList.FirstChild(), nsnull); + AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); } - nsFrameList* result = NS_STATIC_CAST(nsFrameList*, - UnsetProperty(nsLayoutAtoms::overflowOutOfFlowsProperty)); - NS_ASSERTION(result, "value should always be non-empty when state set"); - RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); - return result; -} - -// Destructor function for the overflowPlaceholders frame property -static void -DestroyOverflowOOFs(void* aFrame, - nsIAtom* aPropertyName, - void* aPropertyValue, - void* aDtorData) -{ - NS_NOTREACHED("This helper method should never be called!"); - delete NS_STATIC_CAST(nsFrameList*, aPropertyValue); -} - -// This takes ownership of aFloaters. -nsresult -nsBlockFrame::SetOverflowOutOfFlows(nsFrameList* aOOFs) -{ - NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS), - "Overwriting existing overflow out-of-flows list"); - nsresult rv = SetProperty(nsLayoutAtoms::overflowOutOfFlowsProperty, - aOOFs, DestroyOverflowOOFs); - // Verify that we didn't overwrite an existing overflow list - NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow float list"); - AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); - return rv; } nsFrameList* @@ -4865,45 +5143,6 @@ nsBlockFrame::GetOverflowPlaceholders() const return result; } -nsFrameList* -nsBlockFrame::RemoveOverflowPlaceholders() -{ - if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS)) { - return nsnull; - } - nsFrameList* result = NS_STATIC_CAST(nsFrameList*, - UnsetProperty(nsLayoutAtoms::overflowPlaceholdersProperty)); - NS_ASSERTION(result, "value should always be non-empty when state set"); - RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS); - return result; -} - -// Destructor function for the overflowPlaceholders frame property -static void -DestroyOverflowPlaceholders(void* aFrame, - nsIAtom* aPropertyName, - void* aPropertyValue, - void* aDtorData) -{ - nsFrameList* overflowPlace = NS_STATIC_CAST(nsFrameList*, aPropertyValue); - delete overflowPlace; -} - -// This takes ownership of aOverflowLines. -// XXX We should allocate overflowLines from presShell arena! -nsresult -nsBlockFrame::SetOverflowPlaceholders(nsFrameList* aOverflowPlaceholders) -{ - NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS), - "Overwriting existing overflow placeholder list"); - nsresult rv = SetProperty(nsLayoutAtoms::overflowPlaceholdersProperty, - aOverflowPlaceholders, DestroyOverflowPlaceholders); - // Verify that we didn't overwrite an existing overflow list - NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing overflow placeholder list"); - AddStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS); - return rv; -} - ////////////////////////////////////////////////////////////////////// // Frame list manipulation routines @@ -5113,17 +5352,41 @@ nsBlockFrame::AddFrames(nsIFrame* aFrameList, nsBlockFrame::line_iterator nsBlockFrame::RemoveFloat(nsIFrame* aFloat) { - // Find which line contains the float + // Find which line contains the float, so we can update + // the float cache. line_iterator line = begin_lines(), line_end = end_lines(); for ( ; line != line_end; ++line) { if (line->IsInline() && line->RemoveFloat(aFloat)) { break; } } - - mFloats.DestroyFrame(GetPresContext(), aFloat); - return line; + // Unlink the placeholder *after* we searched the lines, because + // the line search uses the placeholder relationship. + nsFrameManager* fm = GetPresContext()->GetPresShell()->FrameManager(); + nsPlaceholderFrame* placeholder = fm->GetPlaceholderFrameFor(aFloat); + if (placeholder) { + fm->UnregisterPlaceholderFrame(placeholder); + placeholder->SetOutOfFlowFrame(nsnull); + } + + // Try to destroy if it's in mFloats. + if (mFloats.DestroyFrame(GetPresContext(), aFloat)) { + return line; + } + + // Try our overflow list + { + nsAutoOOFFrameList oofs(this); + if (oofs.mList.DestroyFrame(GetPresContext(), aFloat)) { + return line_end; + } + } + + // If aFloat's parent is being reflowed then mFloats may not be up + // to date so we won't find the float above. Just delete it. + aFloat->Destroy(GetPresContext()); + return line_end; } NS_IMETHODIMP @@ -5182,17 +5445,9 @@ nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame) } // Now remove aFrame const nsStyleDisplay* display = aFrame->GetStyleDisplay(); - // find the containing block, this is either the parent or the grandparent - // if the parent is an inline frame - nsIFrame* parent = aFrame->GetParent(); - nsIAtom* parentType = parent->GetType(); - while ((nsLayoutAtoms::blockFrame != parentType) && - (nsLayoutAtoms::areaFrame != parentType)) { - parent = parent->GetParent(); - parentType = parent->GetType(); - } - - nsBlockFrame* block = (nsBlockFrame*)parent; + + // The containing block is always the parent of aFrame. + nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent(); // Remove aFrame from the appropriate list. if (display->IsAbsolutelyPositioned()) { block->mAbsoluteContainer.RemoveFrame(block, @@ -5206,7 +5461,9 @@ nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame) } } -// This helps us iterate over the list of all normal + overflow lines +/** + * This helps us iterate over the list of all normal + overflow lines + */ void nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator, nsLineList::iterator* aEndIterator, @@ -5224,6 +5481,18 @@ nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator, } } +static nsresult RemoveBlockChild(nsIFrame* aFrame, PRBool aDestroyFrames) +{ + if (!aFrame) + return NS_OK; + + nsBlockFrame* nextBlock = NS_STATIC_CAST(nsBlockFrame*, aFrame->GetParent()); + NS_ASSERTION(nextBlock->GetType() == nsLayoutAtoms::blockFrame || + nextBlock->GetType() == nsLayoutAtoms::areaFrame, + "Our child's continuation's parent is not a block?"); + return nextBlock->DoRemoveFrame(aFrame, aDestroyFrames); +} + // This function removes aDeletedFrame and all its continuations. It // is optimized for deleting a whole series of frames. The easy // implementation would invoke itself recursively on @@ -5232,12 +5501,13 @@ nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator, // start by locating aDeletedFrame and then scanning from that point // on looking for continuations. nsresult -nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame) +nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRBool aDestroyFrames) { // Clear our line cursor, since our lines may change. ClearLineCursor(); if (aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + NS_ASSERTION(aDestroyFrames, "We can't not destroy out of flows"); DoRemoveOutOfFlowFrame(aDeletedFrame); return NS_OK; } @@ -5245,6 +5515,20 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame) nsPresContext* presContext = GetPresContext(); nsIPresShell* presShell = presContext->PresShell(); + PRBool isPlaceholder = nsLayoutAtoms::placeholderFrame == aDeletedFrame->GetType(); + if (isPlaceholder) { + nsFrameList* overflowPlaceholders = GetOverflowPlaceholders(); + if (overflowPlaceholders && overflowPlaceholders->RemoveFrame(aDeletedFrame)) { + nsIFrame* nif = aDeletedFrame->GetNextInFlow(); + if (aDestroyFrames) { + aDeletedFrame->Destroy(presContext); + } else { + aDeletedFrame->SetNextSibling(nsnull); + } + return RemoveBlockChild(nif, aDestroyFrames); + } + } + // Find the line and the previous sibling that contains // deletedFrame; we also find the pointer to the line. nsLineList::iterator line = mLines.begin(), @@ -5332,7 +5616,11 @@ found_frame:; nsFrame::ListTag(stdout, aDeletedFrame); printf(" prevSibling=%p deletedNextInFlow=%p\n", prevSibling, deletedNextInFlow); #endif - aDeletedFrame->Destroy(presContext); + if (aDestroyFrames) { + aDeletedFrame->Destroy(presContext); + } else { + aDeletedFrame->SetNextSibling(nsnull); + } aDeletedFrame = deletedNextInFlow; // If line is empty, remove it now. @@ -5376,6 +5664,12 @@ found_frame:; } if (nsnull != deletedNextInFlow) { + // Continuations for placeholder frames don't always appear in + // consecutive lines. So for placeholders, just continue the slow easy way. + if (isPlaceholder) { + return RemoveBlockChild(deletedNextInFlow, aDestroyFrames); + } + // See if we should keep looking in the current flow's line list. if (deletedNextInFlow->GetParent() != this) { // The deceased frames continuation is not a child of the @@ -5402,14 +5696,7 @@ found_frame:; #endif // Advance to next flow block if the frame has more continuations - if (aDeletedFrame) { - nsBlockFrame* nextBlock = NS_STATIC_CAST(nsBlockFrame*, aDeletedFrame->GetParent()); - NS_ASSERTION(nextBlock->GetType() == nsLayoutAtoms::blockFrame, - "Our child's continuation's parent is not a block?"); - return nextBlock->DoRemoveFrame(aDeletedFrame); - } - - return NS_OK; + return RemoveBlockChild(aDeletedFrame, aDestroyFrames); } void @@ -5458,13 +5745,8 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState, else { const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay(); - nsIFrame* prevInFlow = floatFrame->GetPrevInFlow(); - // if the float is continued, constrain its width to the prev-in-flow's width - if (prevInFlow) { - availWidth = prevInFlow->GetRect().width; - } - else if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || - eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) { + if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || + eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) { availWidth = aState.mContentArea.width; } else { @@ -5520,15 +5802,17 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState, do { nsCollapsingMargin margin; PRBool mayNeedRetry = PR_FALSE; - nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin, - clearanceFrame, &mayNeedRetry); + floatRS.mDiscoveredClearance = nsnull; + // Only first in flow gets a top margin. + if (!floatFrame->GetPrevInFlow()) { + nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin, + clearanceFrame, &mayNeedRetry); - if (mayNeedRetry && !clearanceFrame) { - floatRS.mDiscoveredClearance = &clearanceFrame; - // We don't need to push the space manager state because the the block has its own - // space manager that will be destroyed and recreated - } else { - floatRS.mDiscoveredClearance = nsnull; + if (mayNeedRetry && !clearanceFrame) { + floatRS.mDiscoveredClearance = &clearanceFrame; + // We don't need to push the space manager state because the the block has its own + // space manager that will be destroyed and recreated + } } rv = brc.ReflowBlock(availSpace, PR_TRUE, margin, @@ -5541,18 +5825,22 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState, // if the height is constrained (bug 145305). if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight)) aReflowStatus = NS_FRAME_COMPLETE; - + if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { // Float is now complete, so delete the placeholder's next in // flows, if any; their floats (which are this float's continuations) // have already been deleted. + // XXX won't this be done later in nsLineLayout::ReflowFrame anyway?? nsIFrame* nextInFlow = aPlaceholder->GetNextInFlow(); if (nextInFlow) { - // If aPlaceholder's parent is an inline, nextInFlow's will be a block. NS_STATIC_CAST(nsHTMLContainerFrame*, nextInFlow->GetParent()) ->DeleteNextInFlowChild(aState.mPresContext, nextInFlow); + // that takes care of all subsequent nextinflows too } } + if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; + } if (NS_SUCCEEDED(rv) && isAutoWidth) { nscoord maxElementWidth = brc.GetMaxElementWidth(); @@ -5611,7 +5899,10 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState, const nsMargin& m = brc.GetMargin(); aFloatCache->mMargins.top = brc.GetTopMargin(); aFloatCache->mMargins.right = m.right; - brc.GetCarriedOutBottomMargin().Include(m.bottom); + // Only last in flows get a bottom margin + if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { + brc.GetCarriedOutBottomMargin().Include(m.bottom); + } aFloatCache->mMargins.bottom = brc.GetCarriedOutBottomMargin().get(); aFloatCache->mMargins.left = m.left; @@ -6547,9 +6838,8 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) else printf("null"); printf(")\n"); - - gNoiseIndent++; } + AutoNoisyIndenter indent(gNoisyReflow); #endif if (aChild) { @@ -6572,7 +6862,6 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); printf("scheduled reflow command for absolutely positioned frame\n"); - --gNoiseIndent; } #endif @@ -6619,12 +6908,6 @@ nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild) } #endif } - -#ifdef DEBUG - if (gNoisyReflow) { - --gNoiseIndent; - } -#endif return NS_OK; } @@ -6670,15 +6953,22 @@ PRBool nsBlockFrame::IsChild(nsIFrame* aFrame) { // Continued out-of-flows don't satisfy InLineList(), continued out-of-flows - // and placeholders don't satisfy InSiblingList(). + // and placeholders don't satisfy InSiblingList(). PRBool skipLineList = PR_FALSE; PRBool skipSiblingList = PR_FALSE; nsIFrame* prevInFlow = aFrame->GetPrevInFlow(); + PRBool isPlaceholder = nsLayoutAtoms::placeholderFrame == aFrame->GetType(); if (prevInFlow) { nsFrameState state = aFrame->GetStateBits(); skipLineList = (state & NS_FRAME_OUT_OF_FLOW); - skipSiblingList = (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) || - (state & NS_FRAME_OUT_OF_FLOW); + skipSiblingList = isPlaceholder || (state & NS_FRAME_OUT_OF_FLOW); + } + + if (isPlaceholder) { + nsFrameList* overflowPlaceholders = GetOverflowPlaceholders(); + if (overflowPlaceholders && overflowPlaceholders->ContainsFrame(aFrame)) { + return PR_TRUE; + } } if (aFrame->GetParent() != (nsIFrame*)this) { @@ -7013,33 +7303,35 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState, mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED); } -// 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 CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent, - nsIFrame** aHead, nsIFrame** aTail) { +// This is used to scan frames for any float placeholders, add their +// floats to the list represented by aHead and aTail, and remove the +// floats from whatever list they might be in. We only search the +// inline descendants. The floats must be children of 'this'. +void nsBlockFrame::CollectFloats(nsIFrame* aFrame, nsFrameList& aList, nsIFrame** aTail, + PRBool aFromOverflow) { while (aFrame) { // Don't descend into block children if (!aFrame->GetStyleDisplay()->IsBlockLevel()) { 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. - NS_ASSERTION(outOfFlowFrame->GetParent() == aBlockParent, + // Make sure that its parent is us. 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. + NS_ASSERTION(outOfFlowFrame->GetParent() == this, "Out of flow frame doesn't have the expected parent"); - if (!*aHead) { - *aHead = *aTail = outOfFlowFrame; + if (aFromOverflow) { + nsAutoOOFFrameList oofs(this); + oofs.mList.RemoveFrame(outOfFlowFrame); } else { - (*aTail)->SetNextSibling(outOfFlowFrame); - *aTail = outOfFlowFrame; + mFloats.RemoveFrame(outOfFlowFrame); + } + aList.InsertFrame(nsnull, *aTail, outOfFlowFrame); + *aTail = outOfFlowFrame; } - CollectFloats(aFrame->GetFirstChild(nsnull), aBlockParent, - aHead, aTail); + CollectFloats(aFrame->GetFirstChild(nsnull), aList, aTail, aFromOverflow); } aFrame = aFrame->GetNextSibling(); @@ -7057,6 +7349,7 @@ nsBlockFrame::BuildFloatList(nsBlockReflowState& aState) for (line_iterator line = begin_lines(), line_end = end_lines(); line != line_end; ++line) { + if (line->HasFloats()) { nsFloatCache* fc = line->GetFirstFloat(); while (fc) { @@ -7084,15 +7377,11 @@ nsBlockFrame::BuildFloatList(nsBlockReflowState& aState) // because the float cache contains only laid-out floats nsLineList* overflowLines = GetOverflowLines(); if (overflowLines) { - head = nsnull; - current = nsnull; - - CollectFloats(overflowLines->front()->mFirstChild, - this, &head, ¤t); - - if (current) { - current->SetNextSibling(nsnull); + nsFrameList oofs; + nsIFrame* tail = nsnull; + CollectFloats(overflowLines->front()->mFirstChild, oofs, &tail, PR_TRUE); + if (oofs.NotEmpty()) { // 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 @@ -7103,12 +7392,8 @@ nsBlockFrame::BuildFloatList(nsBlockReflowState& aState) // 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); - } + aState.mSpaceManager->RemoveTrailingRegions(oofs.FirstChild()); + SetOverflowOutOfFlows(oofs); } } } diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index f2334cdc58f..3085c9c7f2f 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -63,6 +63,32 @@ class nsIntervalSet; #define NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX 4 #define NS_BLOCK_FRAME_LAST_LIST_INDEX NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX +/** + * Some invariants: + * -- The overflow out-of-flows list contains the out-of- + * flow frames whose placeholders are in the overflow list. + * -- A given piece of content has at most one placeholder + * frame in a block's normal child list. + * -- A given piece of content can have an unlimited number + * of placeholder frames in the overflow-lines list. + * -- A line containing a continuation placeholder contains + * only continuation placeholders. + * -- While a block is being reflowed, its overflowPlaceholdersList + * frame property points to an nsFrameList in its + * nsBlockReflowState. This list contains placeholders for + * floats whose prev-in-flow is in the block's regular line + * list. The list is always empty/non-existent after the + * block has been reflowed. + * -- In all these frame lists, if there are two frames for + * the same content appearing in the list, then the frames + * appear with the prev-in-flow before the next-in-flow. + * -- While reflowing a block, its overflow line list + * will usually be empty but in some cases will have lines + * (while we reflow the block at its shrink-wrap width). + * In this case any new overflowing content must be + * prepended to the overflow lines. + */ + // see nsHTMLParts.h for the public block state bits #define NS_BLOCK_HAS_LINE_CURSOR 0x01000000 #define NS_BLOCK_HAS_OVERFLOW_LINES 0x02000000 @@ -222,9 +248,11 @@ public: virtual void DeleteNextInFlowChild(nsPresContext* aPresContext, nsIFrame* aNextInFlow); - // Determines whether the collapsed margin carried out of the last - // line includes the margin-top of a line with clearance (in which - // case we must avoid collapsing that margin with our bottom margin) + /** + * Determines whether the collapsed margin carried out of the last + * line includes the margin-top of a line with clearance (in which + * case we must avoid collapsing that margin with our bottom margin) + */ PRBool CheckForCollapsedBottomMarginFromClearanceLine(); /** return the topmost block child based on y-index. @@ -243,7 +271,7 @@ public: // Create a contination for aPlaceholder and its out of flow frame and // add it to the list of overflow floats - nsresult SplitPlaceholder(nsPresContext& aPresContext, nsIFrame& aPlaceholder); + nsresult SplitPlaceholder(nsBlockReflowState& aState, nsIFrame* aPlaceholder); void UndoSplitPlaceholders(nsBlockReflowState& aState, nsIFrame* aLastPlaceholder); @@ -258,6 +286,12 @@ public: aDirtyRect, aFrame, aWhichLayer, aFlags); } + PRBool HandleOverflowPlaceholdersForPulledFrame( + nsBlockReflowState& aState, nsIFrame* aFrame); + + PRBool HandleOverflowPlaceholdersOnPulledLine( + nsBlockReflowState& aState, nsLineBox* aLine); + protected: nsBlockFrame(); virtual ~nsBlockFrame(); @@ -331,18 +365,23 @@ protected: nsresult AddFrames(nsIFrame* aFrameList, nsIFrame* aPrevSibling); +public: /** does all the real work for removing aDeletedFrame from this * finds the line containing aFrame. * handled continued frames * marks lines dirty as needed + * @param aDestroyFrames if false then we don't actually destroy the + * frame or its next in flows, we just remove them. This does NOT work + * on out of flow frames so always use PR_TRUE for out of flows. */ - nsresult DoRemoveFrame(nsIFrame* aDeletedFrame); + nsresult DoRemoveFrame(nsIFrame* aDeletedFrame, PRBool aDestroyFrames = PR_TRUE); +protected: /** grab overflow lines from this block's prevInFlow, and make them * part of this block's mLines list. * @return PR_TRUE if any lines were drained. */ - PRBool DrainOverflowLines(); + PRBool DrainOverflowLines(nsBlockReflowState& aState); /** * Remove a float from our float list and also the float cache @@ -350,6 +389,8 @@ protected: */ line_iterator RemoveFloat(nsIFrame* aFloat); + void CollectFloats(nsIFrame* aFrame, nsFrameList& aList, nsIFrame** aTail, + PRBool aFromOverflow); // Remove a float, abs, rel positioned frame from the appropriate block's list static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame); @@ -391,8 +432,11 @@ protected: */ nsresult PrepareResizeReflow(nsBlockReflowState& aState); - /** reflow all lines that have been marked dirty */ - nsresult ReflowDirtyLines(nsBlockReflowState& aState); + /** reflow all lines that have been marked dirty. + * @param aTryPull set this to PR_TRUE if you want to try pulling content from + * our next in flow while there is room. + */ + nsresult ReflowDirtyLines(nsBlockReflowState& aState, PRBool aTryPull); //---------------------------------------- // Methods for line reflow @@ -498,13 +542,13 @@ protected: PRBool aDamageDeletedLine, nsIFrame*& aFrameResult); - nsresult PullFrameFrom(nsBlockReflowState& aState, - nsLineBox* aLine, - nsBlockFrame* aFromContainer, - PRBool aFromOverflowLine, - nsLineList::iterator aFromLine, - PRBool aDamageDeletedLines, - nsIFrame*& aFrameResult); + PRBool PullFrameFrom(nsBlockReflowState& aState, + nsLineBox* aLine, + nsBlockFrame* aFromContainer, + PRBool aFromOverflowLine, + nsLineList::iterator aFromLine, + PRBool aDamageDeletedLines, + nsIFrame*& aFrameResult); void PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore); @@ -552,17 +596,39 @@ protected: //---------------------------------------- +public: nsLineList* GetOverflowLines() const; +protected: nsLineList* RemoveOverflowLines(); nsresult SetOverflowLines(nsLineList* aOverflowLines); nsFrameList* GetOverflowPlaceholders() const; - nsFrameList* RemoveOverflowPlaceholders(); - nsresult SetOverflowPlaceholders(nsFrameList* aOverflowPlaceholders); - nsFrameList* GetOverflowOutOfFlows() const; - nsFrameList* RemoveOverflowOutOfFlows(); - nsresult SetOverflowOutOfFlows(nsFrameList* aFloaters); + /** + * This class is useful for efficiently modifying the out of flow + * overflow list. It gives the client direct writable access to + * the frame list temporarily but ensures that property is only + * written back if absolutely necessary. + */ + struct nsAutoOOFFrameList { + nsFrameList mList; + + nsAutoOOFFrameList(nsBlockFrame* aBlock) : + mList(aBlock->GetOverflowOutOfFlows().FirstChild()), + aOldHead(mList.FirstChild()), mBlock(aBlock) {} + ~nsAutoOOFFrameList() { + if (mList.FirstChild() != aOldHead) { + mBlock->SetOverflowOutOfFlows(mList); + } + } + protected: + nsIFrame* aOldHead; + nsBlockFrame* mBlock; + }; + friend class nsAutoOOFFrameList; + + nsFrameList GetOverflowOutOfFlows() const; + void SetOverflowOutOfFlows(const nsFrameList& aList); nsIFrame* LastChild(); @@ -613,5 +679,23 @@ protected: #endif }; +#ifdef DEBUG +class AutoNoisyIndenter { +public: + AutoNoisyIndenter(PRBool aDoIndent) : mIndented(aDoIndent) { + if (mIndented) { + nsBlockFrame::gNoiseIndent++; + } + } + ~AutoNoisyIndenter() { + if (mIndented) { + nsBlockFrame::gNoiseIndent--; + } + } +private: + PRBool mIndented; +}; +#endif + #endif /* nsBlockFrame_h___ */ diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index 4164f3f56d1..680c149c824 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -107,55 +107,79 @@ nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, if (0 == aRS.mComputedBorderPadding.top && !(aRS.frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) && NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, &bf))) { - nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, aRS.frame); - - for (nsBlockFrame::line_iterator line = block->begin_lines(), - line_end = block->end_lines(); - line != line_end; ++line) { - if (!aClearanceFrame && line->HasClearance()) { - // If we don't have a clearance frame, then we're computing - // the collapsed margin in the first pass, assuming that all - // lines have no clearance. So clear their clearance flags. - line->ClearHasClearance(); - line->MarkDirty(); - dirtiedLine = PR_TRUE; + // iterate not just through the lines of 'block' but also its + // overflow lines and the normal and overflow lines of its next in + // flows. Note that this will traverse some frames more than once: + // for example, if A contains B and A->nextinflow contains + // B->nextinflow, we'll traverse B->nextinflow twice. But this is + // OK because our traversal is idempotent. + for (nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, aRS.frame); + block; block = NS_STATIC_CAST(nsBlockFrame*, block->GetNextInFlow())) { + for (PRBool overflowLines = PR_FALSE; overflowLines <= PR_TRUE; ++overflowLines) { + nsBlockFrame::line_iterator line; + nsBlockFrame::line_iterator line_end; + PRBool anyLines = PR_TRUE; + if (overflowLines) { + nsLineList* lines = block->GetOverflowLines(); + if (!lines) { + anyLines = PR_FALSE; + } else { + line = lines->begin(); + line_end = lines->end(); + } + } else { + line = block->begin_lines(); + line_end = block->end_lines(); + } + for (; anyLines && line != line_end; ++line) { + if (!aClearanceFrame && line->HasClearance()) { + // If we don't have a clearance frame, then we're computing + // the collapsed margin in the first pass, assuming that all + // lines have no clearance. So clear their clearance flags. + line->ClearHasClearance(); + line->MarkDirty(); + dirtiedLine = PR_TRUE; + } + + PRBool isEmpty = line->IsEmpty(); + if (line->IsBlock()) { + nsBlockFrame* kidBlock = NS_STATIC_CAST(nsBlockFrame*, line->mFirstChild); + if (kidBlock == aClearanceFrame) { + line->SetHasClearance(); + line->MarkDirty(); + dirtiedLine = PR_TRUE; + goto done; + } + // Here is where we recur. Now that we have determined that a + // generational collapse is required we need to compute the + // child blocks margin and so in so that we can look into + // it. For its margins to be computed we need to have a reflow + // state for it. Since the reflow reason is irrelevant, we'll + // arbitrarily make it a `resize' to avoid the path-plucking + // behavior if we're in an incremental reflow. + nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight); + nsHTMLReflowState reflowState(kidBlock->GetPresContext(), + aRS, kidBlock, + availSpace, eReflowReason_Resize); + // Record that we're being optimistic by assuming the kid + // has no clearance + if (kidBlock->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { + *aMayNeedRetry = PR_TRUE; + } + if (ComputeCollapsedTopMargin(reflowState, aMargin, aClearanceFrame, aMayNeedRetry)) { + line->MarkDirty(); + dirtiedLine = PR_TRUE; + } + if (isEmpty) + aMargin->Include(reflowState.mComputedMargin.bottom); + } + if (!isEmpty) + goto done; + } } - - PRBool isEmpty = line->IsEmpty(); - if (line->IsBlock()) { - nsBlockFrame* kidBlock = NS_STATIC_CAST(nsBlockFrame*, line->mFirstChild); - if (kidBlock == aClearanceFrame) { - line->SetHasClearance(); - line->MarkDirty(); - dirtiedLine = PR_TRUE; - break; - } - // Here is where we recur. Now that we have determined that a - // generational collapse is required we need to compute the - // child blocks margin and so in so that we can look into - // it. For its margins to be computed we need to have a reflow - // state for it. Since the reflow reason is irrelevant, we'll - // arbitrarily make it a `resize' to avoid the path-plucking - // behavior if we're in an incremental reflow. - nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight); - nsHTMLReflowState reflowState(kidBlock->GetPresContext(), - aRS, kidBlock, - availSpace, eReflowReason_Resize); - // Record that we're being optimistic by assuming the kid - // has no clearance - if (kidBlock->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { - *aMayNeedRetry = PR_TRUE; - } - if (ComputeCollapsedTopMargin(reflowState, aMargin, aClearanceFrame, aMayNeedRetry)) { - line->MarkDirty(); - dirtiedLine = PR_TRUE; - } - if (isEmpty) - aMargin->Include(reflowState.mComputedMargin.bottom); - } - if (!isEmpty) - break; } + done: + ; } #ifdef NOISY_VERTICAL_MARGINS @@ -624,7 +648,9 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace, if (nsnull != kidNextInFlow) { // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the - // parent is not this because we are executing pullup code) + // parent is not this because we are executing pullup code). + // Floats will eventually be removed via nsBlockFrame::RemoveFloat + // which detaches the placeholder from the float. /* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */ NS_STATIC_CAST(nsHTMLContainerFrame*, kidNextInFlow->GetParent()) ->DeleteNextInFlowChild(mPresContext, kidNextInFlow); @@ -654,17 +680,20 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, const nsMargin& aComputedOffsets, nsCollapsingMargin& aBottomMarginResult, nsRect& aInFlowBounds, - nsRect& aCombinedRect) + nsRect& aCombinedRect, + nsReflowStatus aReflowStatus) { - // Compute collapsed bottom margin value - aBottomMarginResult = mMetrics.mCarriedOutBottomMargin; - aBottomMarginResult.Include(mMargin.bottom); + // Compute collapsed bottom margin value. + if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { + aBottomMarginResult = mMetrics.mCarriedOutBottomMargin; + aBottomMarginResult.Include(mMargin.bottom); + } else { + // The used bottom-margin is set to zero above a break. + aBottomMarginResult.Zero(); + } - // See if the block will fit in the available space - PRBool fits; nscoord x = mX; nscoord y = mY; - nscoord backupContainingBlockAdvance = 0; // Check whether the block's bottom margin collapses with its top @@ -673,11 +702,12 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, // check that first. Note that a block can have clearance and still // have adjoining top/bottom margins, because the clearance goes // above the top margin. - if (0 == mMetrics.height && aLine->CachedIsEmpty()) - { + PRBool empty = 0 == mMetrics.height && aLine->CachedIsEmpty(); + if (empty) { // Collapse the bottom margin with the top margin that was already // applied. aBottomMarginResult.Include(mTopMargin); + #ifdef NOISY_VERTICAL_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); @@ -686,10 +716,6 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n", y, mSpace.y); #endif - - // Empty blocks always fit - fits = PR_TRUE; - // Section 8.3.1 of CSS 2.1 says that blocks with adjoining // top/bottom margins whose top margin collapses with their // parent's top margin should have their top border-edge at the @@ -709,97 +735,92 @@ nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, // point from where this empty block will be. backupContainingBlockAdvance = mTopMargin.get(); } - else { - // See if the frame fit. If it's the first frame then it always - // fits. If the height is unconstrained then it always fits, even - // if there's some sort of integer overflow that makes - // y + mMetrics.height appear to go beyond the available height. - // XXX This is a bit of a hack. We really need to use floats in layout! - if (aForceFit || - mSpace.height == NS_UNCONSTRAINEDSIZE || - y + mMetrics.height <= mSpace.YMost()) - { - fits = PR_TRUE; - // Adjust the max-element-size in the metrics to take into - // account the margins around the block element. - // Do not allow auto margins to impact the max-element size - // since they are springy and don't really count! - if (mMetrics.mComputeMEW) { - nsMargin maxElemMargin; - const nsStyleSides &styleMargin = mStyleMargin->mMargin; - nsStyleCoord coord; - if (styleMargin.GetLeftUnit() == eStyleUnit_Coord) - maxElemMargin.left = styleMargin.GetLeft(coord).GetCoordValue(); - else - maxElemMargin.left = 0; - if (styleMargin.GetRightUnit() == eStyleUnit_Coord) - maxElemMargin.right = styleMargin.GetRight(coord).GetCoordValue(); - else - maxElemMargin.right = 0; - - nscoord dummyXOffset; - // Base the margins on the max-element size - ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaxElementWidth, - maxElemMargin, dummyXOffset); - - mMetrics.mMaxElementWidth += maxElemMargin.left + maxElemMargin.right; - } - - // do the same for the maximum width - if (mComputeMaximumWidth) { - nsMargin maxWidthMargin; - const nsStyleSides &styleMargin = mStyleMargin->mMargin; - nsStyleCoord coord; - if (styleMargin.GetLeftUnit() == eStyleUnit_Coord) - maxWidthMargin.left = styleMargin.GetLeft(coord).GetCoordValue(); - else - maxWidthMargin.left = 0; - if (styleMargin.GetRightUnit() == eStyleUnit_Coord) - maxWidthMargin.right = styleMargin.GetRight(coord).GetCoordValue(); - else - maxWidthMargin.right = 0; - - nscoord dummyXOffset; - // Base the margins on the maximum width - ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaximumWidth, - maxWidthMargin, dummyXOffset); - - mMetrics.mMaximumWidth += maxWidthMargin.left + maxWidthMargin.right; - } - } - else { - // Send the DidReflow() notification, but don't bother placing - // the frame + // See if the frame fit. If it's the first frame or empty then it + // always fits. If the height is unconstrained then it always fits, + // even if there's some sort of integer overflow that makes y + + // mMetrics.height appear to go beyond the available height. + if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) { + nscoord yMost = y - backupContainingBlockAdvance + mMetrics.height; + if (yMost > mSpace.YMost()) { + // didn't fit, we must acquit. mFrame->DidReflow(mPresContext, &aReflowState, NS_FRAME_REFLOW_FINISHED); - fits = PR_FALSE; + return PR_FALSE; } } - if (fits) { - // Calculate the actual x-offset and left and right margin - nsBlockHorizontalAlign align; - align.mXOffset = x; - AlignBlockHorizontally(mMetrics.width, align); - x = align.mXOffset; - mMargin.left = align.mLeftMargin; - mMargin.right = align.mRightMargin; + if (!empty) + { + // Adjust the max-element-size in the metrics to take into + // account the margins around the block element. + // Do not allow auto margins to impact the max-element size + // since they are springy and don't really count! + if (mMetrics.mComputeMEW) { + nsMargin maxElemMargin; + const nsStyleSides &styleMargin = mStyleMargin->mMargin; + nsStyleCoord coord; + if (styleMargin.GetLeftUnit() == eStyleUnit_Coord) + maxElemMargin.left = styleMargin.GetLeft(coord).GetCoordValue(); + else + maxElemMargin.left = 0; + if (styleMargin.GetRightUnit() == eStyleUnit_Coord) + maxElemMargin.right = styleMargin.GetRight(coord).GetCoordValue(); + else + maxElemMargin.right = 0; + + nscoord dummyXOffset; + // Base the margins on the max-element size + ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaxElementWidth, + maxElemMargin, dummyXOffset); + + mMetrics.mMaxElementWidth += maxElemMargin.left + maxElemMargin.right; + } - aInFlowBounds = nsRect(x, y - backupContainingBlockAdvance, - mMetrics.width, mMetrics.height); - - // Apply CSS relative positioning - const nsStyleDisplay* styleDisp = mFrame->GetStyleDisplay(); - if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) { - x += aComputedOffsets.left; - y += aComputedOffsets.top; + // do the same for the maximum width + if (mComputeMaximumWidth) { + nsMargin maxWidthMargin; + const nsStyleSides &styleMargin = mStyleMargin->mMargin; + nsStyleCoord coord; + if (styleMargin.GetLeftUnit() == eStyleUnit_Coord) + maxWidthMargin.left = styleMargin.GetLeft(coord).GetCoordValue(); + else + maxWidthMargin.left = 0; + if (styleMargin.GetRightUnit() == eStyleUnit_Coord) + maxWidthMargin.right = styleMargin.GetRight(coord).GetCoordValue(); + else + maxWidthMargin.right = 0; + + nscoord dummyXOffset; + // Base the margins on the maximum width + ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaximumWidth, + maxWidthMargin, dummyXOffset); + + mMetrics.mMaximumWidth += maxWidthMargin.left + maxWidthMargin.right; } - - // Now place the frame and complete the reflow process - nsContainerFrame::FinishReflowChild(mFrame, mPresContext, &aReflowState, mMetrics, x, y, 0); - - aCombinedRect = mMetrics.mOverflowArea + nsPoint(x, y); } - return fits; + // Calculate the actual x-offset and left and right margin + nsBlockHorizontalAlign align; + align.mXOffset = x; + AlignBlockHorizontally(mMetrics.width, align); + x = align.mXOffset; + mMargin.left = align.mLeftMargin; + mMargin.right = align.mRightMargin; + + aInFlowBounds = nsRect(x, y - backupContainingBlockAdvance, + mMetrics.width, mMetrics.height); + + // Apply CSS relative positioning + const nsStyleDisplay* styleDisp = mFrame->GetStyleDisplay(); + if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) { + x += aComputedOffsets.left; + y += aComputedOffsets.top; + } + + // Now place the frame and complete the reflow process + nsContainerFrame::FinishReflowChild(mFrame, mPresContext, &aReflowState, mMetrics, x, y, 0); + + aCombinedRect = mMetrics.mOverflowArea + nsPoint(x, y); + + return PR_TRUE; } diff --git a/layout/generic/nsBlockReflowContext.h b/layout/generic/nsBlockReflowContext.h index 30161234b2c..a547cc3ece0 100644 --- a/layout/generic/nsBlockReflowContext.h +++ b/layout/generic/nsBlockReflowContext.h @@ -77,7 +77,8 @@ public: const nsMargin& aComputedOffsets, nsCollapsingMargin& aBottomMarginResult /* out */, nsRect& aInFlowBounds, - nsRect& aCombinedRect); + nsRect& aCombinedRect, + nsReflowStatus aReflowStatus); void AlignBlockHorizontally(nscoord aWidth, nsBlockHorizontalAlign&); diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index ae613befecb..a062aec2a1a 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -72,6 +72,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, { const nsMargin& borderPadding = BorderPadding(); + if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mBlock->SetProperty(nsLayoutAtoms::overflowPlaceholdersProperty, + &mOverflowPlaceholders, nsnull); + mBlock->AddStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS); + } + if (aTopMarginRoot || 0 != aReflowState.mComputedBorderPadding.top) { SetFlag(BRS_ISTOPMARGINROOT, PR_TRUE); } @@ -172,6 +178,11 @@ nsBlockReflowState::~nsBlockReflowState() const nsMargin& borderPadding = BorderPadding(); mSpaceManager->Translate(-borderPadding.left, -borderPadding.top); } + + if (mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mBlock->UnsetProperty(nsLayoutAtoms::overflowPlaceholdersProperty); + mBlock->RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS); + } } nsLineBox* @@ -543,7 +554,7 @@ nsBlockReflowState::IsImpactedByFloat() const } -void +PRBool nsBlockReflowState::InitFloat(nsLineLayout& aLineLayout, nsPlaceholderFrame* aPlaceholder, nsReflowStatus& aReflowStatus) @@ -554,7 +565,7 @@ nsBlockReflowState::InitFloat(nsLineLayout& aLineLayout, // Then add the float to the current line and place it when // appropriate - AddFloat(aLineLayout, aPlaceholder, PR_TRUE, aReflowStatus); + return AddFloat(aLineLayout, aPlaceholder, PR_TRUE, aReflowStatus); } // This is called by the line layout's AddFloat method when a @@ -567,7 +578,7 @@ nsBlockReflowState::InitFloat(nsLineLayout& aLineLayout, // technically we're supposed let the current line flow around the // float as well unless it won't fit next to what we already have. // But nobody else implements it that way... -void +PRBool nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, nsPlaceholderFrame* aPlaceholder, PRBool aInitialReflow, @@ -582,12 +593,11 @@ nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, fc->mIsCurrentLineFloat = aLineLayout.CanPlaceFloatNow(); fc->mMaxElementWidth = 0; + PRBool placed; + // Now place the float immediately if possible. Otherwise stash it // away in mPendingFloats and place it later. if (fc->mIsCurrentLineFloat) { - // Record this float in the current-line list - mCurrentLineFloats.Append(fc); - // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space @@ -601,15 +611,19 @@ nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, // And then place it PRBool isLeftFloat; - FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus); - - // Pass on updated available space to the current inline reflow engine - GetAvailableSpace(); - aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, - GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, - mAvailSpaceRect.height, - isLeftFloat, - aPlaceholder->GetOutOfFlowFrame()); + placed = FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus); + if (placed) { + // Pass on updated available space to the current inline reflow engine + GetAvailableSpace(); + aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, + GetFlag(BRS_UNCONSTRAINEDWIDTH) ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width, + mAvailSpaceRect.height, + isLeftFloat, + aPlaceholder->GetOutOfFlowFrame()); + + // Record this float in the current-line list + mCurrentLineFloats.Append(fc); + } // Restore coordinate system mSpaceManager->Translate(dx, dy); @@ -618,7 +632,16 @@ nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, // This float will be placed after the line is done (it is a // below-current-line float). mBelowCurrentLineFloats.Append(fc); + if (mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + // If the float might not be complete, mark it incomplete now to + // prevent the placeholders being torn down. We will destroy any + // placeholders later if PlaceBelowCurrentLineFloats finds the + // float is complete. + aReflowStatus = NS_FRAME_NOT_COMPLETE; + } + placed = PR_TRUE; } + return placed; } void @@ -716,6 +739,12 @@ nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize, nscoord saveY = mY; for (;;) { // Get the available space at the new Y coordinate + if (mAvailSpaceRect.height <= 0) { + // there is no more available space. We lose. + result = PR_FALSE; + break; + } + mY += mAvailSpaceRect.height; GetAvailableSpace(); @@ -752,7 +781,7 @@ nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize, return result; } -void +PRBool nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus) @@ -821,6 +850,12 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, PRBool keepFloatOnSameLine = PR_FALSE; while (! CanPlaceFloat(floatSize, floatDisplay->mFloats)) { + if (mAvailSpaceRect.height <= 0) { + // No space, nowhere to put anything. + mY = saveY; + return PR_FALSE; + } + // Nope. try to advance to the next band. if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) { @@ -880,25 +915,10 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, } // If the float is continued, it will get the same absolute x value as its prev-in-flow nsRect prevRect(0,0,0,0); - nsIFrame* prevInFlow = floatFrame->GetPrevInFlow(); - if (prevInFlow) { - prevRect = prevInFlow->GetRect(); - // If prevInFlow's placeholder is in a block that wasn't continued, we need to adjust - // prevRect.x to account for the missing frame offsets. - nsIFrame* placeParent = placeholder->GetParent(); - nsIFrame* placeParentPrev = placeParent->GetPrevInFlow(); - nsIFrame* prevPlace = - mPresContext->FrameManager()->GetPlaceholderFrameFor(prevInFlow); + // We don't worry about the geometry of the prev in flow, let the continuation + // place and size itself as required. - nsIFrame* prevPlaceParent = prevPlace->GetParent(); - - for (nsIFrame* ancestor = prevPlaceParent; - ancestor && (ancestor != placeParentPrev); - ancestor = ancestor->GetParent()) { - prevRect.x += ancestor->GetRect().x; - } - } // Assign an x and y coordinate to the float. Note that the x,y // coordinates are computed relative to the translation in the // spacemanager which means that the impacted region will be @@ -907,15 +927,12 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, nscoord floatX, floatY; if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { isLeftFloat = PR_TRUE; - floatX = (prevInFlow) ? prevRect.x : mAvailSpaceRect.x; + floatX = mAvailSpaceRect.x; } else { isLeftFloat = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != mAvailSpaceRect.width) { - if (prevInFlow) { - floatX = prevRect.x; - } - else if (!keepFloatOnSameLine) { + if (!keepFloatOnSameLine) { floatX = mAvailSpaceRect.XMost() - floatSize.width; } else { @@ -947,7 +964,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, // if the float split, then take up all of the vertical height if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE != mContentArea.height)) { - floatSize.height = PR_MAX(floatSize.height, mContentArea.height); + floatSize.height = PR_MAX(floatSize.height, mContentArea.height - floatY); } nsRect region(floatX, floatY, floatSize.width, floatSize.height); @@ -1041,6 +1058,8 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); } #endif + + return PR_TRUE; } /** @@ -1066,17 +1085,25 @@ nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheList& aList) // Place the float PRBool isLeftFloat; nsReflowStatus reflowStatus; - FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus); + PRBool placed = FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus); - if (NS_FRAME_IS_TRUNCATED(reflowStatus)) { + if (!placed || NS_FRAME_IS_TRUNCATED(reflowStatus)) { // return before processing all of the floats, since the line will be pushed. return PR_FALSE; } else if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Create a continuation for the incomplete float and its placeholder. - nsresult rv = mBlock->SplitPlaceholder(*mPresContext, *fc->mPlaceholder); + nsresult rv = mBlock->SplitPlaceholder(*this, fc->mPlaceholder); if (NS_FAILED(rv)) return PR_FALSE; + } else { + // Float is complete. We need to delete any leftover placeholders now. + nsIFrame* nextPlaceholder = fc->mPlaceholder->GetNextInFlow(); + if (nextPlaceholder) { + nsHTMLContainerFrame* parent = + NS_STATIC_CAST(nsHTMLContainerFrame*, nextPlaceholder->GetParent()); + parent->DeleteNextInFlowChild(mPresContext, nextPlaceholder); + } } } fc = fc->Next(); diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index f9483186f62..9ebfd6734e0 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -68,21 +68,22 @@ public: void GetAvailableSpace(nscoord aY); - void InitFloat(nsLineLayout& aLineLayout, - nsPlaceholderFrame* aPlaceholderFrame, - nsReflowStatus& aReflowStatus); - - void AddFloat(nsLineLayout& aLineLayout, - nsPlaceholderFrame* aPlaceholderFrame, - PRBool aInitialReflow, - nsReflowStatus& aReflowStatus); - + /* + * The following functions all return PR_TRUE if they were able to + * place the float, PR_FALSE if the float did not fit in available + * space. + */ + PRBool InitFloat(nsLineLayout& aLineLayout, + nsPlaceholderFrame* aPlaceholderFrame, + nsReflowStatus& aReflowStatus); + PRBool AddFloat(nsLineLayout& aLineLayout, + nsPlaceholderFrame* aPlaceholderFrame, + PRBool aInitialReflow, + nsReflowStatus& aReflowStatus); PRBool CanPlaceFloat(const nsSize& aFloatSize, PRUint8 aFloats); - - void FlowAndPlaceFloat(nsFloatCache* aFloatCache, - PRBool* aIsLeftFloat, - nsReflowStatus& aReflowStatus); - + PRBool FlowAndPlaceFloat(nsFloatCache* aFloatCache, + PRBool* aIsLeftFloat, + nsReflowStatus& aReflowStatus); PRBool PlaceBelowCurrentLineFloats(nsFloatCacheList& aFloats); // Returns the first coordinate >= aY that clears the @@ -162,6 +163,10 @@ public: // unconstrained area. nsSize mContentArea; + // Placeholders for continuation out-of-flow frames that need to + // move to our next in flow are placed here during reflow. + nsFrameList mOverflowPlaceholders; + //---------------------------------------- // This state is "running" state updated by the reflow of each line diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 4e64ed18beb..cd1b79245c4 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -69,6 +69,10 @@ public: NS_IMETHOD RemoveFrame(nsIAtom* aListName, nsIFrame* aOldFrame); + NS_IMETHOD GetFrameForPoint(const nsPoint& aPoint, + nsFramePaintLayer aWhichLayer, + nsIFrame** aFrame); + virtual nsIFrame* GetContentInsertionFrame() { return GetFirstChild(nsnull)->GetContentInsertionFrame(); } @@ -163,6 +167,16 @@ nsColumnSetFrame::GetType() const return nsLayoutAtoms::columnSetFrame; } +NS_IMETHODIMP +nsColumnSetFrame::GetFrameForPoint(const nsPoint& aPoint, + nsFramePaintLayer aWhichLayer, + nsIFrame** aFrame) +{ + // This frame counts as part of the background. + return GetFrameForPointUsing(aPoint, nsnull, aWhichLayer, + (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND), aFrame); +} + NS_IMETHODIMP nsColumnSetFrame::SetInitialChildList(nsPresContext* aPresContext, nsIAtom* aListName, @@ -260,6 +274,9 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState) // colWidth*nominalColumnCount is nearly availContentWidth // make sure to round down numColumns = (availContentWidth + colGap)/(colGap + colWidth); + if (numColumns <= 0) { + numColumns = 1; + } } // Compute extra space and divide it among the columns diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index a132aa5191a..fdb0ee62536 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -638,6 +638,20 @@ nsFrame::Destroy(nsPresContext* aPresContext) nsIPresShell *shell = aPresContext->GetPresShell(); if (shell) { + if (mState & NS_FRAME_OUT_OF_FLOW) { + nsPlaceholderFrame* placeholder + = shell->FrameManager()->GetPlaceholderFrameFor(this); + if (placeholder) { + NS_WARNING("Deleting out of flow without tearing down placeholder relationship"); + if (placeholder->GetOutOfFlowFrame()) { + NS_ASSERTION(placeholder->GetOutOfFlowFrame() == this, + "no-one told the frame manager about this"); + shell->FrameManager()->UnregisterPlaceholderFrame(placeholder); + placeholder->SetOutOfFlowFrame(nsnull); + } + } + } + // If the frame contains generated context, remove it from // the quoteList. if (mState & NS_FRAME_GENERATED_CONTENT) { @@ -2724,6 +2738,14 @@ nsFrame::List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent) const if (0 != mState) { fprintf(out, " [state=%08x]", mState); } + nsIFrame* prevInFlow = GetPrevInFlow(); + nsIFrame* nextInFlow = GetNextInFlow(); + if (nsnull != prevInFlow) { + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, prevInFlow)); + } + if (nsnull != nextInFlow) { + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, nextInFlow)); + } fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); nsFrame* f = NS_CONST_CAST(nsFrame*, this); nsRect* overflowArea = f->GetOverflowAreaProperty(PR_FALSE); diff --git a/layout/generic/nsFrameList.cpp b/layout/generic/nsFrameList.cpp index 90dba39ac52..d3575488d34 100644 --- a/layout/generic/nsFrameList.cpp +++ b/layout/generic/nsFrameList.cpp @@ -39,6 +39,8 @@ #ifdef NS_DEBUG #include "nsIFrameDebug.h" #endif +#include "nsVoidArray.h" +#include "nsLayoutUtils.h" #ifdef IBMBIDI #include "nsCOMPtr.h" @@ -160,6 +162,7 @@ nsFrameList::AppendFrame(nsIFrame* aParent, nsIFrame* aFrame) { NS_PRECONDITION(nsnull != aFrame, "null ptr"); if (nsnull != aFrame) { + NS_PRECONDITION(!aFrame->GetNextSibling(), "Can only append one frame here"); nsIFrame* lastChild = LastChild(); if (nsnull == lastChild) { mFirstChild = aFrame; @@ -180,11 +183,11 @@ PRBool nsFrameList::RemoveFrame(nsIFrame* aFrame, nsIFrame* aPrevSiblingHint) { NS_PRECONDITION(nsnull != aFrame, "null ptr"); - if (nsnull != aFrame) { + if (aFrame) { nsIFrame* nextFrame = aFrame->GetNextSibling(); - aFrame->SetNextSibling(nsnull); if (aFrame == mFirstChild) { mFirstChild = nextFrame; + aFrame->SetNextSibling(nsnull); return PR_TRUE; } else { @@ -194,6 +197,7 @@ nsFrameList::RemoveFrame(nsIFrame* aFrame, nsIFrame* aPrevSiblingHint) } if (prevSibling) { prevSibling->SetNextSibling(nextFrame); + aFrame->SetNextSibling(nsnull); return PR_TRUE; } } @@ -441,6 +445,58 @@ nsFrameList::GetLength() const return count; } +static int PR_CALLBACK CompareByContentOrder(const void* aF1, const void* aF2, + void* aDummy) +{ + const nsIFrame* f1 = NS_STATIC_CAST(const nsIFrame*, aF1); + const nsIFrame* f2 = NS_STATIC_CAST(const nsIFrame*, aF2); + if (f1->GetContent() != f2->GetContent()) { + return nsLayoutUtils::CompareTreePosition(f1->GetContent(), f2->GetContent()); + } + + if (f1 == f2) { + return 0; + } + + const nsIFrame* f; + for (f = f2; f; f = f->GetPrevInFlow()) { + if (f == f1) { + // f1 comes before f2 in the flow + return -1; + } + } + for (f = f1; f; f = f->GetPrevInFlow()) { + if (f == f2) { + // f1 comes after f2 in the flow + return 1; + } + } + + NS_ASSERTION(PR_FALSE, "Frames for same content but not in relative flow order"); + return 0; +} + +void +nsFrameList::SortByContentOrder() +{ + if (!mFirstChild) + return; + + nsAutoVoidArray array; + nsIFrame* f; + for (f = mFirstChild; f; f = f->GetNextSibling()) { + array.AppendElement(f); + } + array.Sort(CompareByContentOrder, nsnull); + f = mFirstChild = NS_STATIC_CAST(nsIFrame*, array.FastElementAt(0)); + for (PRInt32 i = 1; i < array.Count(); ++i) { + nsIFrame* ff = NS_STATIC_CAST(nsIFrame*, array.FastElementAt(i)); + f->SetNextSibling(ff); + f = ff; + } + f->SetNextSibling(nsnull); +} + nsIFrame* nsFrameList::GetPrevSiblingFor(nsIFrame* aFrame) const { diff --git a/layout/generic/nsFrameList.h b/layout/generic/nsFrameList.h index 845cf209e72..d22cb038340 100644 --- a/layout/generic/nsFrameList.h +++ b/layout/generic/nsFrameList.h @@ -115,6 +115,14 @@ public: PRBool Split(nsIFrame* aAfterFrame, nsIFrame** aNextFrameResult); + /** + * Sort the frames according to content order so that the first + * frame in the list is the first in content order. Frames for + * the same content will be ordered so that a prev in flow + * comes before its next in flow. + */ + void SortByContentOrder(); + nsIFrame* PullFrame(nsIFrame* aParent, nsIFrame* aLastChild, nsFrameList& aFromList); diff --git a/layout/generic/nsHTMLReflowState.h b/layout/generic/nsHTMLReflowState.h index 39d93fcda2d..299a853c00e 100644 --- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -147,14 +147,22 @@ struct nsHTMLReflowState { // reason == eReflowReason_Incremental. nsReflowPath *path; - // the available space in which to reflow the frame. The space represents the - // amount of room for the frame's border, padding, and content area (not the - // margin area. The parent frame deals with the child frame's margins). The - // frame size you choose should fit within the available space. - // A value of NS_UNCONSTRAINEDSIZE for the available height means you can - // choose whatever size you want. In galley mode the available height is always - // NS_UNCONSTRAINEDSIZE, and only page mode involves a constrained height - nscoord availableWidth, availableHeight; + // the available width in which to reflow the frame. The space + // represents the amount of room for the frame's border, padding, + // and content area (not the margin area. The parent frame deals + // with the child frame's margins). The frame size you choose should + // fit within the available width. + nscoord availableWidth; + + // A value of NS_UNCONSTRAINEDSIZE for the available height means + // you can choose whatever size you want. In galley mode the + // available height is always NS_UNCONSTRAINEDSIZE, and only page + // mode involves a constrained height. The element's the top border + // and padding, and content, must fit. If the element is complete + // after reflow then its bottom border, padding and margin (and + // similar for its complete ancestors) will need to fit in this + // height. + nscoord availableHeight; // rendering context to use for measurement nsIRenderingContext* rendContext; diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index e672075c116..a2920f0174c 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -772,7 +772,7 @@ nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext, else if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { if (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) { nsBlockReflowState* blockRS = lineLayout->mBlockRS; - blockRS->mBlock->SplitPlaceholder(*aPresContext, *aFrame); + blockRS->mBlock->SplitPlaceholder(*blockRS, aFrame); } else { nsIFrame* newFrame; diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp index b0f78795bd6..9e62f5b01ee 100644 --- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -44,6 +44,7 @@ #include "nsBlockFrame.h" #include "nsITextContent.h" #include "nsLayoutAtoms.h" +#include "nsFrameManager.h" #ifdef DEBUG static PRInt32 ctorCount; @@ -62,6 +63,12 @@ nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock) MOZ_COUNT_CTOR(nsLineBox); #ifdef DEBUG ++ctorCount; + NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child"); + nsIFrame* f = aFrame; + for (PRInt32 n = aCount; n > 0; f = f->GetNextSibling(), --n) { + NS_ASSERTION(aIsBlock == f->GetStyleDisplay()->IsBlockLevel(), + "wrong kind of child frame"); + } #endif mAllFlags = 0; @@ -464,7 +471,6 @@ nsLineBox::RemoveFloat(nsIFrame* aFrame) if (fc) { // Note: the placeholder is part of the line's child list // and will be removed later. - fc->mPlaceholder->SetOutOfFlowFrame(nsnull); mInlineData->mFloats.Remove(fc); MaybeFreeData(); return PR_TRUE; diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 6d3368143f0..7667019723d 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -1012,11 +1012,16 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); if (outOfFlowFrame) { nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, aFrame); + PRBool didPlace; if (eReflowReason_Incremental == reason) { - InitFloat(placeholder, aReflowStatus); + didPlace = InitFloat(placeholder, aReflowStatus); } else { - AddFloat(placeholder, aReflowStatus); + didPlace = AddFloat(placeholder, aReflowStatus); + } + printf("*** Reflowed float, didPlace=%d, status=%d\n", aReflowStatus); + if (!didPlace) { + aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); } if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) { SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index 94fad424242..bbaafbecee4 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -252,12 +252,12 @@ public: //---------------------------------------- // Inform the line-layout about the presence of a floating frame // XXX get rid of this: use get-frame-type? - void InitFloat(nsPlaceholderFrame* aFrame, nsReflowStatus& aReflowStatus) { - mBlockRS->InitFloat(*this, aFrame, aReflowStatus); + PRBool InitFloat(nsPlaceholderFrame* aFrame, nsReflowStatus& aReflowStatus) { + return mBlockRS->InitFloat(*this, aFrame, aReflowStatus); } - void AddFloat(nsPlaceholderFrame* aFrame, nsReflowStatus& aReflowStatus) { - mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus); + PRBool AddFloat(nsPlaceholderFrame* aFrame, nsReflowStatus& aReflowStatus) { + return mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus); } //---------------------------------------- diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index e81a660e001..d07bddeda4f 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -40,6 +40,7 @@ #include "nsPresContext.h" #include "nsIRenderingContext.h" #include "nsLayoutAtoms.h" +#include "nsFrameManager.h" nsresult NS_NewPlaceholderFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) @@ -86,6 +87,18 @@ nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, return NS_OK; } +NS_IMETHODIMP +nsPlaceholderFrame::Destroy(nsPresContext* aPresContext) +{ + nsIPresShell* shell = aPresContext->GetPresShell(); + if (shell && mOutOfFlowFrame) { + NS_ASSERTION(!shell->FrameManager()->GetPlaceholderFrameFor(mOutOfFlowFrame), + "Placeholder relationship should have been torn down"); + } + + return nsSplittableFrame::Destroy(aPresContext); +} + nsIAtom* nsPlaceholderFrame::GetType() const { @@ -136,6 +149,14 @@ nsPlaceholderFrame::List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent if (0 != mState) { fprintf(out, " [state=%08x]", mState); } + nsIFrame* prevInFlow = GetPrevInFlow(); + nsIFrame* nextInFlow = GetNextInFlow(); + if (nsnull != prevInFlow) { + fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, prevInFlow)); + } + if (nsnull != nextInFlow) { + fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, nextInFlow)); + } if (mOutOfFlowFrame) { fprintf(out, " outOfFlowFrame="); nsFrame::ListTag(out, mOutOfFlowFrame); diff --git a/layout/generic/nsPlaceholderFrame.h b/layout/generic/nsPlaceholderFrame.h index 9a097a74faf..c7bd70b998f 100644 --- a/layout/generic/nsPlaceholderFrame.h +++ b/layout/generic/nsPlaceholderFrame.h @@ -64,6 +64,8 @@ public: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD Destroy(nsPresContext* aPresContext); + // nsIFrame overrides #ifdef DEBUG NS_IMETHOD Paint(nsPresContext* aPresContext, diff --git a/layout/generic/nsSpaceManager.cpp b/layout/generic/nsSpaceManager.cpp index 068cfbec54e..332aed83e44 100644 --- a/layout/generic/nsSpaceManager.cpp +++ b/layout/generic/nsSpaceManager.cpp @@ -359,11 +359,13 @@ nsSpaceManager::GetBandData(nscoord aYOffset, // If there are no unavailable rects or the offset is below the bottommost // band, then all the space is available nscoord yMost; - + nscoord maxHeight = aMaxSize.height == NS_UNCONSTRAINEDSIZE ? NS_UNCONSTRAINEDSIZE + : PR_MAX(0, aMaxSize.height - aYOffset); + if (!YMost(yMost) || (y >= yMost)) { // All the requested space is available aBandData.mCount = 1; - aBandData.mTrapezoids[0] = nsRect(0, aYOffset, aMaxSize.width, aMaxSize.height); + aBandData.mTrapezoids[0] = nsRect(0, aYOffset, aMaxSize.width, maxHeight); aBandData.mTrapezoids[0].mState = nsBandTrapezoid::Available; aBandData.mTrapezoids[0].mFrame = nsnull; } else { @@ -378,14 +380,14 @@ nsSpaceManager::GetBandData(nscoord aYOffset, // the top of the band is available aBandData.mCount = 1; aBandData.mTrapezoids[0] = - nsRect(0, aYOffset, aMaxSize.width, PR_MIN(band->mTop - y, aMaxSize.height)); + nsRect(0, aYOffset, aMaxSize.width, PR_MIN(band->mTop - y, maxHeight)); aBandData.mTrapezoids[0].mState = nsBandTrapezoid::Available; aBandData.mTrapezoids[0].mFrame = nsnull; break; } else if (y < band->mBottom) { // The band contains the y-offset. Return a list of available and // unavailable rects within the band - return GetBandAvailableSpace(band, y, aMaxSize, aBandData); + return GetBandAvailableSpace(band, y, nsSize(aMaxSize.width, maxHeight), aBandData); } else { // Skip to the next band band = GetNextBand(band);