Bug 251162. Landing experimental implementation of CSS3 columns. r+sr=dbaron

This commit is contained in:
roc+%cs.cmu.edu 2004-10-08 12:17:10 +00:00
Родитель 7e1837f886
Коммит c237520c89
25 изменённых файлов: 2843 добавлений и 590 удалений

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

@ -83,6 +83,7 @@ CSS_ANON_BOX(page, ":-moz-page")
CSS_ANON_BOX(pageContent, ":-moz-pagecontent")
CSS_ANON_BOX(pageSequence, ":-moz-page-sequence")
CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content")
CSS_ANON_BOX(columnContent, ":-moz-column-content")
CSS_ANON_BOX(viewport, ":-moz-viewport")
CSS_ANON_BOX(viewportScroll, ":-moz-viewport-scroll")
CSS_ANON_BOX(selectScrolledContent, ":-moz-select-scrolled-content")

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

@ -97,6 +97,7 @@ LAYOUT_ATOM(blockFrame, "BlockFrame")
LAYOUT_ATOM(boxFrame, "BoxFrame")
LAYOUT_ATOM(brFrame, "BRFrame")
LAYOUT_ATOM(bulletFrame, "BulletFrame")
LAYOUT_ATOM(columnSetFrame, "ColumnSetFrame")
LAYOUT_ATOM(fieldSetFrame, "FieldSetFrame")
LAYOUT_ATOM(gfxButtonControlFrame, "gfxButtonControlFrame")
LAYOUT_ATOM(subDocumentFrame, "subDocumentFrame")

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

@ -806,12 +806,13 @@ nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const
{
if ((mColumnWidth.GetUnit() == eStyleUnit_Auto)
!= (aOther.mColumnWidth.GetUnit() == eStyleUnit_Auto) ||
(mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO)
!= (aOther.mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO))
mColumnCount != aOther.mColumnCount)
// We force column count changes to do a reframe, because it's tricky to handle
// some edge cases where the column count gets smaller and content overflows.
// XXX not ideal
return nsChangeHint_ReconstructFrame;
if (mColumnCount != aOther.mColumnCount ||
mColumnWidth != aOther.mColumnWidth ||
if (mColumnWidth != aOther.mColumnWidth ||
mColumnGap != aOther.mColumnGap)
return nsChangeHint_ReflowFrame;

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

@ -6289,7 +6289,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
aState.mFloatedItems.containingBlock, adjParentFrame,
aStyleContext, newFrame,
aStyleContext, &newFrame,
aDisplay->mPosition == NS_STYLE_POSITION_RELATIVE);
}
// See if it's relatively positioned
@ -6307,7 +6307,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
NS_NewRelativeItemWrapperFrame(aPresShell, &newFrame);
// XXXbz should we be passing in a non-null aContentParentFrame?
ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
adjParentFrame, nsnull, aStyleContext, newFrame, PR_TRUE);
adjParentFrame, nsnull, aStyleContext, &newFrame, PR_TRUE);
} else {
// Create a positioned inline frame
NS_NewPositionedInlineFrame(aPresShell, &newFrame);
@ -6332,7 +6332,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
aState.mPseudoFrames.Reset(&savePseudo);
// XXXbz should we be passing in a non-null aContentParentFrame?
rv = ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
adjParentFrame, nsnull, aStyleContext, newFrame,
adjParentFrame, nsnull, aStyleContext, &newFrame,
PR_FALSE);
if (!aState.mPseudoFrames.IsEmpty()) { // process pending pseudo frames
ProcessPseudoFrames(aPresContext, aState.mPseudoFrames, aFrameItems);
@ -7196,7 +7196,7 @@ nsCSSFrameConstructor::ConstructSVGFrame(nsIPresShell* aPresShell,
const nsStyleDisplay* disp = aStyleContext->GetStyleDisplay();
rv = ConstructBlock(aPresShell, aPresContext, aState, disp, aContent,
geometricParent, aParentFrame, aStyleContext,
newFrame, PR_TRUE);
&newFrame, PR_TRUE);
} else {
InitAndRestoreFrame(aPresContext, aState, aContent,
geometricParent, aStyleContext, nsnull, newFrame);
@ -10916,6 +10916,15 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
nsHTMLContainerFrame::CreateViewForFrame(newFrame, nsnull, PR_FALSE);
}
} else if (nsLayoutAtoms::columnSetFrame == frameType) {
rv = NS_NewColumnSetFrame(shell, &newFrame, 0);
if (NS_SUCCEEDED(rv)) {
newFrame->Init(aPresContext, content, aParentFrame, styleContext,
aFrame);
// XXXbz should we be passing in a non-null aContentParentFrame?
nsHTMLContainerFrame::CreateViewForFrame(newFrame, nsnull, PR_FALSE);
}
} else if (nsLayoutAtoms::positionedInlineFrame == frameType) {
rv = NS_NewPositionedInlineFrame(shell, &newFrame);
if (NS_SUCCEEDED(rv)) {
@ -12697,28 +12706,62 @@ nsCSSFrameConstructor::ConstructBlock(nsIPresShell* aPresShell,
nsIFrame* aParentFrame,
nsIFrame* aContentParentFrame,
nsStyleContext* aStyleContext,
nsIFrame* aNewFrame,
nsIFrame** aNewFrame,
PRBool aRelPos)
{
InitAndRestoreFrame(aPresContext, aState, aContent,
aParentFrame, aStyleContext, nsnull, aNewFrame);
// Create column wrapper if necessary
nsIFrame* blockFrame = *aNewFrame;
nsIFrame* parent = aParentFrame;
nsIFrame* contentParent = aContentParentFrame;
nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
const nsStyleColumn* columns = aStyleContext->GetStyleColumn();
if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
|| columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
nsIFrame* columnSetFrame = nsnull;
NS_NewColumnSetFrame(aPresShell, &columnSetFrame, 0);
if (!columnSetFrame) {
return NS_ERROR_OUT_OF_MEMORY;
}
InitAndRestoreFrame(aPresContext, aState, aContent,
aParentFrame, aStyleContext, nsnull, columnSetFrame);
// See if we need to create a view, e.g. the frame is absolutely positioned
nsHTMLContainerFrame::CreateViewForFrame(columnSetFrame, aContentParentFrame,
PR_FALSE);
blockStyle = aPresContext->StyleSet()->
ResolvePseudoStyleFor(aContent, nsCSSAnonBoxes::columnContent,
aStyleContext);
contentParent = columnSetFrame;
parent = columnSetFrame;
*aNewFrame = columnSetFrame;
columnSetFrame->SetInitialChildList(aPresContext, nsnull, blockFrame);
blockFrame->AddStateBits(NS_BLOCK_SPACE_MGR);
}
InitAndRestoreFrame(aPresContext, aState, aContent,
parent, blockStyle, nsnull, blockFrame);
// See if we need to create a view, e.g. the frame is absolutely positioned
nsHTMLContainerFrame::CreateViewForFrame(aNewFrame, aContentParentFrame,
PR_FALSE);
nsHTMLContainerFrame::CreateViewForFrame(blockFrame, contentParent, PR_FALSE);
// If we're the first block to be created (e.g., because we're
// contained inside a XUL document), then make sure that we've got a
// space manager so we can handle floats...
if (! aState.mFloatedItems.containingBlock) {
aNewFrame->AddStateBits(NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT);
blockFrame->AddStateBits(NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT);
}
// ...and that we're the absolute containing block.
// This makes absolute children of a block in columns be positioned
// relative to the container, not flowed into columns. I don't know if this
// is right...
nsFrameConstructorSaveState absoluteSaveState;
if (aRelPos || !aState.mAbsoluteItems.containingBlock) {
// NS_ASSERTION(aRelPos, "should have made area frame for this");
aState.PushAbsoluteContainingBlock(aPresContext, aNewFrame, absoluteSaveState);
aState.PushAbsoluteContainingBlock(aPresContext, *aNewFrame, absoluteSaveState);
}
// See if the block has first-letter style applied to it...
@ -12729,28 +12772,28 @@ nsCSSFrameConstructor::ConstructBlock(nsIPresShell* aPresShell,
// Process the child content
nsFrameItems childItems;
nsFrameConstructorSaveState floatSaveState;
aState.PushFloatContainingBlock(aNewFrame, floatSaveState,
aState.PushFloatContainingBlock(blockFrame, floatSaveState,
haveFirstLetterStyle,
haveFirstLineStyle);
nsresult rv = ProcessChildren(aPresShell, aPresContext, aState, aContent,
aNewFrame, PR_TRUE, childItems, PR_TRUE);
blockFrame, PR_TRUE, childItems, PR_TRUE);
CreateAnonymousFrames(aPresShell, aPresContext, aContent->Tag(), aState,
aContent, aNewFrame, PR_FALSE, childItems);
aContent, blockFrame, PR_FALSE, childItems);
// Set the frame's initial child list
aNewFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList);
blockFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList);
// Set the frame's float list if there were any floated children
if (aState.mFloatedItems.childList) {
aNewFrame->SetInitialChildList(aPresContext,
nsLayoutAtoms::floatList,
aState.mFloatedItems.childList);
blockFrame->SetInitialChildList(aPresContext,
nsLayoutAtoms::floatList,
aState.mFloatedItems.childList);
}
// and the same for absolutely positioned children.
if (aRelPos && aState.mAbsoluteItems.childList) {
aNewFrame->SetInitialChildList(aPresContext, nsLayoutAtoms::absoluteList,
aState.mAbsoluteItems.childList);
blockFrame->SetInitialChildList(aPresContext, nsLayoutAtoms::absoluteList,
aState.mAbsoluteItems.childList);
}
return rv;

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

@ -849,7 +849,7 @@ private:
nsIFrame* aParentFrame,
nsIFrame* aContentParentFrame,
nsStyleContext* aStyleContext,
nsIFrame* aNewFrame,
nsIFrame** aNewFrame,
PRBool aRelPos);
nsresult ConstructInline(nsIPresShell* aPresShell,

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

@ -97,6 +97,7 @@ LAYOUT_ATOM(blockFrame, "BlockFrame")
LAYOUT_ATOM(boxFrame, "BoxFrame")
LAYOUT_ATOM(brFrame, "BRFrame")
LAYOUT_ATOM(bulletFrame, "BulletFrame")
LAYOUT_ATOM(columnSetFrame, "ColumnSetFrame")
LAYOUT_ATOM(fieldSetFrame, "FieldSetFrame")
LAYOUT_ATOM(gfxButtonControlFrame, "gfxButtonControlFrame")
LAYOUT_ATOM(subDocumentFrame, "subDocumentFrame")

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

@ -244,10 +244,11 @@ struct nsHTMLReflowState {
struct ReflowStateFlags {
PRUint16 mSpecialHeightReflow:1; // used by tables to communicate special reflow (in process) to handle
// percent height frames inside cells which may not have computed heights
PRUint16 mNextInFlowUntouched:1; // nothing in the frame's next-in-flow (or its descendants)
// is changing
PRUint16 mIsTopOfPage:1; // is the current context at the top of a page?
PRUint16 mBlinks:1; // Keep track of text-decoration: blink
PRUint16 mVisualBidiFormControl:1; // Keep track of descendants of form controls on Visual Bidi pages
PRUint16 mUnused:12; // for future use
} mFlags;
#ifdef IBMBIDI

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

@ -730,7 +730,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(aPresContext);
DrainOverflowLines();
switch (aReflowState.reason) {
case eReflowReason_Initial:
@ -827,13 +827,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
if (NS_FAILED(rv)) return rv;
nsIFrame* nextInFlow = GetNextInFlow();
if (nextInFlow && NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
if (GetOverflowLines() || GetOverflowPlaceholders()) {
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
}
// If the block is complete, put continuted floats in the closest ancestor
// block that uses the same space manager and leave the block complete; this
// allows subsequent lines on the page to be impacted by floats. If the
@ -917,6 +910,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
if (GetOverflowLines()) {
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
if (NS_STYLE_OVERFLOW_CLIP == aReflowState.mStyleDisplay->mOverflowX) {
state.mReflowStatus = NS_FRAME_COMPLETE;
}
@ -953,9 +950,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
#endif
// Determine if we need to repaint our border, background or outline
CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
// Let the absolutely positioned container reflow any absolutely positioned
// child frames that need to be reflowed, e.g., elements with a percentage
// based width/height
@ -990,6 +984,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
FinishAndStoreOverflow(&aMetrics);
}
// Determine if we need to repaint our border, background or outline
CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
// Clear the space manager pointer in the block reflow state so we
// don't waste time translating the coordinate system back on a dead
// space manager.
@ -1830,7 +1828,7 @@ nsBlockFrame::FindLineFor(nsIFrame* aFrame)
if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame())
return line;
}
}
}
}
return line_end;
@ -1927,6 +1925,51 @@ WrappedLinesAreDirty(nsLineList::iterator aLine,
static void PlaceFrameView(nsPresContext* aPresContext, nsIFrame* aFrame);
static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent,
nsIFrame** aHead, nsIFrame** aTail);
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.
*/
void
nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
nsBlockFrame* aOldParent, PRBool aFromOverflow) {
nsIFrame* head = nsnull;
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()) {
ReparentFrame(f, aOldParent, this);
}
}
}
/**
* Reflow the dirty lines
@ -1976,6 +2019,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 lastLineMovedUp = PR_FALSE;
PRUint8 floatBreakType = NS_STYLE_CLEAR_NONE;
// Reflow the lines that are already ours
@ -1998,12 +2042,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If we're supposed to update our maximum width, then we'll also need to
// reflow this line if it's line wrapped and any of the continuing lines
// are dirty. If we are printing (constrained height), always reflow
// the line.
// are dirty.
// XXXperf XXXldb Check that the previous line was not wrapped
// before doing this check (it's O(N^2) as written now).
if ((NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight) ||
(!line->IsDirty() &&
if ((!line->IsDirty() &&
aState.GetFlag(BRS_COMPUTEMAXWIDTH) &&
::WrappedLinesAreDirty(line, line_end))) {
line->MarkDirty();
@ -2025,15 +2067,17 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
}
PRBool previousMarginWasDirty = line->IsPreviousMarginDirty();
if (previousMarginWasDirty && !line->IsDirty()) {
if (previousMarginWasDirty) {
// If the previous margin is dirty, reflow the current line
line->MarkDirty();
}
line->ClearPreviousMarginDirty();
// See if there's any reflow damage that requires that we mark the
// line dirty.
if (!line->IsDirty()) {
line->ClearPreviousMarginDirty();
} else if (line->mBounds.YMost() + deltaY > aState.mBottomEdge) {
// Lines that aren't dirty but get slid past our height constraint must
// be reflowed.
line->MarkDirty();
} else if (!line->IsDirty()) {
// See if there's any reflow damage that requires that we mark the
// line dirty.
PropagateFloatDamage(aState, line, deltaY);
}
@ -2050,6 +2094,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// Now repair the line and update |aState.mY| by calling
// |ReflowLine| or |SlideLine|.
if (line->IsDirty()) {
lastLineMovedUp = PR_TRUE;
// Compute the dirty lines "before" YMost, after factoring in
// the running deltaY value - the running value is implicit in
// aState.mY.
@ -2101,10 +2147,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// the next line dirty.
line.next()->MarkPreviousMarginDirty();
// since it's marked dirty, nobody will care about |deltaY|
} else {
deltaY = line->mBounds.YMost() - oldYMost;
}
deltaY = line->mBounds.YMost() - oldYMost;
} else {
lastLineMovedUp = deltaY < 0;
if (deltaY != 0)
SlideLine(aState, line, deltaY);
else
@ -2162,94 +2209,147 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
if (repositionViews)
::PlaceFrameView(aState.mPresContext, this);
PRBool touchedNextInFlow = PR_FALSE;
// Pull data from a next-in-flow if there's still room for more
// content here.
while (keepGoing && (nsnull != aState.mNextInFlow)) {
touchedNextInFlow = PR_TRUE;
// Grab first line from our next-in-flow
nsBlockFrame* nextInFlow = aState.mNextInFlow;
line_iterator nifLine = nextInFlow->begin_lines();
if (nifLine == nextInFlow->end_lines()) {
aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
continue;
// 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)) {
if (aState.mNextInFlow) {
aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
}
// XXX See if the line is not dirty; if it's not maybe we can
// avoid the pullup if it can't fit? This is important if we want
// to avoid reflowing our next-in-flow!
nsLineBox *toMove = nifLine;
nextInFlow->mLines.erase(nifLine);
if (0 == toMove->GetChildCount()) {
// The line is empty. Try the next one.
NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
aState.FreeLineBox(toMove);
continue;
}
// XXX move to a subroutine: run-in, overflow, pullframe and this do this
// Make the children in the line ours.
nsIFrame* frame = toMove->mFirstChild;
nsIFrame* lastFrame = nsnull;
PRInt32 n = toMove->GetChildCount();
while (--n >= 0) {
frame->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, mNextInFlow, this);
lastFrame = frame;
frame = frame->GetNextSibling();
}
lastFrame->SetNextSibling(nsnull);
// Add line to our line list
if (aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
}
line = mLines.before_insert(end_lines(), toMove);
// 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.
if (line->HasFloats()) {
nsFloatCache* fc = line->GetFirstFloat();
while (fc) {
if (fc->mPlaceholder) {
nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
if (floatFrame)
aState.mNextInFlow->mFloats.RemoveFrame(floatFrame);
} else {
// Pull data from a next-in-flow if there's still room for more
// content here.
while (keepGoing && (nsnull != aState.mNextInFlow)) {
// Grab first line from our next-in-flow
nsBlockFrame* nextInFlow = aState.mNextInFlow;
line_iterator nifLine = nextInFlow->begin_lines();
nsLineBox *toMove;
PRBool collectOverflowFloats;
if (nifLine != nextInFlow->end_lines()) {
toMove = nifLine;
nextInFlow->mLines.erase(nifLine);
collectOverflowFloats = PR_FALSE;
} else {
// Grab an overflow line if there are any
nsLineList* overflowLines = nextInFlow->RemoveOverflowLines();
if (!overflowLines) {
aState.mNextInFlow =
NS_STATIC_CAST(nsBlockFrame*, nextInFlow->mNextInFlow);
continue;
}
fc = fc->Next();
nifLine = overflowLines->begin();
NS_ASSERTION(nifLine != overflowLines->end(),
"Stored overflow line list should not be empty");
toMove = nifLine;
nifLine = overflowLines->erase(nifLine);
if (nifLine != overflowLines->end()) {
// We need to this remove-and-put-back dance because we want
// to avoid making the overflow line list empty while it's
// stored in the property (because the property has the
// invariant that the list is never empty).
nextInFlow->SetOverflowLines(overflowLines);
}
collectOverflowFloats = PR_TRUE;
}
if (0 == toMove->GetChildCount()) {
// The line is empty. Try the next one.
NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
aState.FreeLineBox(toMove);
continue;
}
// XXX move to a subroutine: run-in, overflow, pullframe and this do this
// Make the children in the line ours.
nsIFrame* frame = toMove->mFirstChild;
nsIFrame* lastFrame = nsnull;
PRInt32 n = toMove->GetChildCount();
while (--n >= 0) {
ReparentFrame(frame, nextInFlow, this);
lastFrame = frame;
frame = frame->GetNextSibling();
}
lastFrame->SetNextSibling(nsnull);
// Add line to our line list
if (aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
}
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();
}
}
}
// 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
// line to be created; see SplitLine's callers for examples of
// when this happens).
while (line != end_lines()) {
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
if (NS_FAILED(rv)) {
NS_WARNING("Line reflow failed");
return rv;
}
if (!keepGoing) {
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
nsRect lca(line->GetCombinedArea());
IndentBy(stdout, gNoiseIndent);
printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
NS_STATIC_CAST(void*, line.get()), aState.mY,
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lca.x, lca.y, lca.width, lca.height,
deltaY, aState.mPrevBottomMargin.get(),
line->GetChildCount());
}
#endif
if (0 == line->GetChildCount()) {
DeleteLine(aState, line, line_end);
}
break;
}
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
}
}
// 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
// line to be created; see SplitLine's callers for examples of
// when this happens).
while (line != end_lines()) {
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
if (NS_FAILED(rv)) {
return rv;
}
if (!keepGoing) {
if (0 == line->GetChildCount()) {
DeleteLine(aState, line, line_end);
}
break;
}
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
}
@ -2263,10 +2363,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.mY += metrics.height;
}
if (touchedNextInFlow && NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@ -2522,16 +2618,27 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
// First check our remaining lines
if (end_lines() != aLine.next()) {
return PullFrameFrom(aState, aLine, mLines, aLine.next(), PR_FALSE,
return PullFrameFrom(aState, aLine, this, PR_FALSE, aLine.next(),
aDamageDeletedLines, aFrameResult);
}
// Pull frames from the next-in-flow(s) until we can't
NS_ASSERTION(!GetOverflowLines(),
"Our overflow lines should have been removed at the start of reflow");
// Try each next in flows
nsBlockFrame* nextInFlow = aState.mNextInFlow;
while (nsnull != nextInFlow) {
if (! nextInFlow->mLines.empty()) {
return PullFrameFrom(aState, aLine, nextInFlow->mLines,
nextInFlow->mLines.begin(), PR_TRUE,
while (nextInFlow) {
// first normal lines, then overflow lines
if (!nextInFlow->mLines.empty()) {
return PullFrameFrom(aState, aLine, nextInFlow, PR_FALSE,
nextInFlow->mLines.begin(),
aDamageDeletedLines, aFrameResult);
}
nsLineList* overflowLines = nextInFlow->GetOverflowLines();
if (overflowLines) {
return PullFrameFrom(aState, aLine, nextInFlow, PR_TRUE,
overflowLines->begin(),
aDamageDeletedLines, aFrameResult);
}
@ -2558,9 +2665,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
nsresult
nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
nsLineBox* aLine,
nsLineList& aFromContainer,
nsBlockFrame* aFromContainer,
PRBool aFromOverflowLine,
nsLineList::iterator aFromLine,
PRBool aUpdateGeometricParent,
PRBool aDamageDeletedLines,
nsIFrame*& aFrameResult)
{
@ -2595,32 +2702,41 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
if (aDamageDeletedLines) {
Invalidate(fromLine->mBounds);
}
if (aFromLine.next() != aFromContainer.end())
nsLineList* fromLineList = aFromOverflowLine
? aFromContainer->RemoveOverflowLines()
: &aFromContainer->mLines;
if (aFromLine.next() != fromLineList->end())
aFromLine.next()->MarkPreviousMarginDirty();
Invalidate(fromLine->GetCombinedArea());
aFromContainer.erase(aFromLine);
fromLineList->erase(aFromLine);
// Note that aFromLine just got incremented, so don't use it again here!
aState.FreeLineBox(fromLine);
// Put any remaining overflow lines back.
if (aFromOverflowLine && !fromLineList->empty()) {
aFromContainer->SetOverflowLines(fromLineList);
}
}
// Change geometric parents
if (aUpdateGeometricParent) {
// Before we set the new parent frame get the current parent
nsIFrame* oldParentFrame = frame->GetParent();
frame->SetParent(this);
if (aFromContainer != this) {
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
NS_ASSERTION(oldParentFrame != this, "unexpected parent frame");
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, oldParentFrame, this);
NS_ASSERTION(frame->GetParent() == aFromContainer, "unexpected parent frame");
ReparentFrame(frame, aFromContainer, this);
// The frame is being pulled from a next-in-flow; therefore we
// need to add it to our sibling list.
frame->SetNextSibling(nsnull);
if (nsnull != aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(frame);
}
// The frame might have (or contain) floats that need to be
// brought over too.
ReparentFloats(frame, aFromContainer, aFromOverflowLine);
}
// Stop pulling because we found a frame to pull
@ -3093,7 +3209,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
UndoSplitPlaceholders(aState, lastPlaceholder);
PushLines(aState, aLine.prev());
*aKeepReflowGoing = PR_FALSE;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE | NS_FRAME_REFLOW_NEXTINFLOW;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
}
else {
// Note: line-break-after a block is a nop
@ -3144,10 +3260,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
if (NS_FAILED(rv))
return rv;
nsIFrame* nextFrame = frame->GetNextInFlow();
// Push continuation to a new line, but only if we actually made one.
if (madeContinuation) {
frame = frame->GetNextSibling();
nsLineBox* line = aState.NewLineBox(frame, 1, PR_TRUE);
nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -3162,6 +3279,20 @@ 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();
}
}
*aKeepReflowGoing = PR_FALSE;
@ -3441,8 +3572,11 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
--aLine;
if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
// Push the line with the truncated float
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
// Don't push any lines if we just want to calculate the maximum width
if (!aUpdateMaximumWidth) {
// Push the line with the truncated float
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
}
}
break;
}
@ -4028,7 +4162,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)) {
// If we're just updating the maximum width then we don't care if it
// fits; we'll assume it does, so that the maximum width will get
// updated below. The line will be reflowed again and pushed then
// if necessary.
if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)
&& !aUpdateMaximumWidth) {
// Push this line and all of it's children and anything else that
// follows to our next-in-flow
NS_ASSERTION((aState.mCurrentLine == aLine), "oops");
@ -4086,9 +4226,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// At least one float is truncated, so fix up any placeholders that got split and
// push the line. XXX It may be better to put the float on the next line, but this
// is not common enough to justify the complexity.
nsFrameList* overflowPlace = GetOverflowPlaceholders();
nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
// 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;
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
}
}
}
@ -4205,6 +4350,11 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState,
++line)
{
line->MarkDirty();
line->MarkPreviousMarginDirty();
line->mBounds.SetRect(0, 0, 0, 0);
if (line->HasFloats()) {
line->FreeFloats(aState.mFloatCacheFreeList);
}
}
}
@ -4222,7 +4372,7 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState,
// the invariant that the property is never set if the list is empty.
PRBool
nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
nsBlockFrame::DrainOverflowLines()
{
#ifdef DEBUG
VerifyOverflowSituation();
@ -4243,22 +4393,23 @@ nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
nsIFrame* lastFrame = nsnull;
nsIFrame* frame = overflowLines->front()->mFirstChild;
while (nsnull != frame) {
frame->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
// XXXldb Are float lists in sync with what floats are actually
// present in the lines, so their views can be reparented?
nsHTMLContainerFrame::ReparentFrameView(aPresContext, frame, prevBlock, this);
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()) {
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);
}
@ -4267,16 +4418,11 @@ nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
NS_ASSERTION(overflowLines->empty(), "splice should empty list");
delete overflowLines;
// Out-of-flow floats need to be reparented too.
nsFrameList* overflowOutOfFlows = prevBlock->RemoveOverflowOutOfFlows();
if (overflowOutOfFlows) {
for (nsIFrame* f = overflowOutOfFlows->FirstChild(); f;
f = f->GetNextSibling()) {
f->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevBlock, this);
ReparentFrame(f, prevBlock, this);
}
delete overflowOutOfFlows;
}
@ -4688,6 +4834,21 @@ nsBlockFrame::AddFrames(nsPresContext* aPresContext,
return NS_OK;
}
nsBlockFrame::line_iterator
nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
// Find which line contains the float
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;
}
NS_IMETHODIMP
nsBlockFrame::RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
@ -4711,15 +4872,8 @@ nsBlockFrame::RemoveFrame(nsPresContext* aPresContext,
aListName, aOldFrame);
}
else if (nsLayoutAtoms::floatList == aListName) {
// Find which line contains the float
line_iterator line = begin_lines(), line_end = end_lines();
for ( ; line != line_end; ++line) {
if (line->IsInline() && line->RemoveFloat(aOldFrame)) {
break;
}
}
mFloats.DestroyFrame(aPresContext, aOldFrame);
line_iterator line = RemoveFloat(aOldFrame);
line_iterator line_end = end_lines();
// Mark every line at and below the line where the float was dirty
// XXXldb This could be done more efficiently.
@ -4771,20 +4925,19 @@ nsBlockFrame::DoRemoveOutOfFlowFrame(nsPresContext* aPresContext,
block->mAbsoluteContainer.RemoveFrame(block, aPresContext,
*(aPresContext->PresShell()),
block->mAbsoluteContainer.GetChildListName(), aFrame);
aFrame->Destroy(aPresContext);
}
else {
block->mFloats.RemoveFrame(aFrame);
// This also destroys the frame.
block->RemoveFloat(aFrame);
}
// Destroy aFrame
aFrame->Destroy(aPresContext);
}
// This helps us iterate over the list of all normal + overflow lines
void
nsBlockFrame::NextOverAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines) {
(*aIterator)++;
nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines) {
if (*aIterator == *aEndIterator) {
if (!*aInOverflowLines) {
*aInOverflowLines = PR_TRUE;
@ -4825,7 +4978,10 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
line_end = mLines.end();
PRBool searchingOverflowList = PR_FALSE;
nsIFrame* prevSibling = nsnull;
for (; line != line_end; NextOverAllLines(&line, &line_end, &searchingOverflowList)) {
// Make sure we look in the overflow lines even if the normal line
// list is empty
TryAllLines(&line, &line_end, &searchingOverflowList);
while (line != line_end) {
nsIFrame* frame = line->mFirstChild;
PRInt32 n = line->GetChildCount();
while (--n >= 0) {
@ -4835,8 +4991,10 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
prevSibling = frame;
frame = frame->GetNextSibling();
}
++line;
TryAllLines(&line, &line_end, &searchingOverflowList);
}
found_frame:;
found_frame:;
if (line == line_end) {
NS_ERROR("can't find deleted frame in lines");
return NS_ERROR_FAILURE;
@ -4849,64 +5007,66 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
}
NS_ASSERTION(!prevSibling || prevSibling->GetNextSibling() == aDeletedFrame, "bad prevSibling");
while ((line != line_end) && (nsnull != aDeletedFrame)) {
while ((line != line_end) && (nsnull != aDeletedFrame)) {
NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
// See if the frame being deleted is the last one on the line
PRBool isLastFrameOnLine = PR_FALSE;
if (1 == line->GetChildCount()) {
isLastFrameOnLine = PR_TRUE;
}
else if (line->LastChild() == aDeletedFrame) {
isLastFrameOnLine = PR_TRUE;
}
// See if the frame being deleted is the last one on the line
PRBool isLastFrameOnLine = PR_FALSE;
if (1 == line->GetChildCount()) {
isLastFrameOnLine = PR_TRUE;
}
else if (line->LastChild() == aDeletedFrame) {
isLastFrameOnLine = PR_TRUE;
}
// Remove aDeletedFrame from the line
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
if (line->mFirstChild == aDeletedFrame) {
line->mFirstChild = nextFrame;
}
// Remove aDeletedFrame from the line
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
if (line->mFirstChild == aDeletedFrame) {
// We should be setting this to null if aDeletedFrame
// is the only frame on the line. HOWEVER in that case
// we will be removing the line anyway, see below.
line->mFirstChild = nextFrame;
}
// Hmm, this won't do anything if we're removing a frame in the first
// overflow line... Hopefully doesn't matter
--line;
if (line != line_end && !line->IsBlock()) {
// Since we just removed a frame that follows some inline
// frames, we need to reflow the previous line.
line->MarkDirty();
}
++line;
--line;
if (line != line_end && !line->IsBlock()) {
// Since we just removed a frame that follows some inline
// frames, we need to reflow the previous line.
line->MarkDirty();
}
++line;
// Take aDeletedFrame out of the sibling list. Note that
// prevSibling will only be nsnull when we are deleting the very
// Take aDeletedFrame out of the sibling list. Note that
// prevSibling will only be nsnull when we are deleting the very
// first frame in the main or overflow list.
if (prevSibling) {
prevSibling->SetNextSibling(nextFrame);
}
if (prevSibling) {
prevSibling->SetNextSibling(nextFrame);
}
// Update the child count of the line to be accurate
PRInt32 lineChildCount = line->GetChildCount();
lineChildCount--;
line->SetChildCount(lineChildCount);
// Update the child count of the line to be accurate
PRInt32 lineChildCount = line->GetChildCount();
lineChildCount--;
line->SetChildCount(lineChildCount);
// Destroy frame; capture its next-in-flow first in case we need
// to destroy that too.
nsIFrame* nextInFlow = aDeletedFrame->GetNextInFlow();
// Destroy frame; capture its next-in-flow first in case we need
// to destroy that too.
nsIFrame* nextInFlow = aDeletedFrame->GetNextInFlow();
#ifdef NOISY_REMOVE_FRAME
printf("DoRemoveFrame: line=%p frame=", line);
nsFrame::ListTag(stdout, aDeletedFrame);
printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
printf("DoRemoveFrame: line=%p frame=", line);
nsFrame::ListTag(stdout, aDeletedFrame);
printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
#endif
aDeletedFrame->Destroy(aPresContext);
aDeletedFrame = nextInFlow;
aDeletedFrame->Destroy(aPresContext);
aDeletedFrame = nextInFlow;
// If line is empty, remove it now.
// Don't bother removing empty lines in the overflow list, they'll get
// annihilated later
if (!searchingOverflowList && 0 == lineChildCount) {
nsLineBox *cur = line;
line = mLines.erase(line);
if (0 == lineChildCount) {
nsLineBox *cur = line;
if (!searchingOverflowList) {
line = mLines.erase(line);
// Invalidate the space taken up by the line.
// XXX We need to do this if we're removing a frame as a result of
// a call to RemoveFrame(), but we may not need to do this in all
@ -4917,44 +5077,51 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height);
#endif
Invalidate(lineCombinedArea);
cur->Destroy(presShell);
// If we're removing a line, ReflowDirtyLines isn't going to
// know that it needs to slide lines unless something is marked
// dirty. So mark the previous margin of the next line dirty if
// there is one.
if (line != line_end)
line->MarkPreviousMarginDirty();
}
else {
// Make the line that just lost a frame dirty
line->MarkDirty();
// If we just removed the last frame on the line then we need
// to advance to the next line.
if (isLastFrameOnLine) {
NextOverAllLines(&line, &line_end, &searchingOverflowList);
// Detect the case when we've run off the end of the normal line
// list and we're starting the overflow line list
if (prevSibling && !prevSibling->GetNextSibling()) {
prevSibling = nsnull;
}
} else {
nsLineList* lineList = RemoveOverflowLines();
line = lineList->erase(line);
if (!lineList->empty()) {
SetOverflowLines(lineList);
}
}
cur->Destroy(presShell);
// See if we should keep looking in the current flow's line list.
if (nsnull != aDeletedFrame) {
if (aDeletedFrame != nextFrame) {
// The deceased frames continuation is not the next frame in
// the current flow's frame list. Therefore we know that the
// continuation is in a different parent. So break out of
// the loop so that we advance to the next parent.
NS_ASSERTION(aDeletedFrame->GetParent() != this, "strange continuation");
break;
}
// If we're removing a line, ReflowDirtyLines isn't going to
// know that it needs to slide lines unless something is marked
// dirty. So mark the previous margin of the next line dirty if
// there is one.
if (line != line_end) {
line->MarkPreviousMarginDirty();
}
} else {
// Make the line that just lost a frame dirty, and advance to
// the next line.
line->MarkDirty();
++line;
}
// If we just removed the last frame on the line then we need
// to advance to the next line.
if (isLastFrameOnLine) {
TryAllLines(&line, &line_end, &searchingOverflowList);
// Detect the case when we've run off the end of the normal line
// list and we're starting the overflow line list
if (prevSibling && !prevSibling->GetNextSibling()) {
prevSibling = nsnull;
}
}
// See if we should keep looking in the current flow's line list.
if (nsnull != aDeletedFrame) {
if (aDeletedFrame->GetParent() != this) {
// The deceased frames continuation is not a child of the
// current block. So break out of the loop so that we advance
// to the next parent.
break;
}
}
}
#ifdef DEBUG
VerifyLines(PR_TRUE);
#endif
@ -4996,13 +5163,6 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
nsFloatCache* aFloatCache,
nsReflowStatus& aReflowStatus)
{
// Delete the placeholder's next in flows, if any
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);
}
// Reflow the float.
nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
aReflowStatus = NS_FRAME_COMPLETE;
@ -5089,6 +5249,18 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
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.
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);
}
}
if (NS_SUCCEEDED(rv) && isAutoWidth) {
nscoord maxElementWidth = brc.GetMaxElementWidth();
if (maxElementWidth > availSpace.width) {

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

@ -286,9 +286,9 @@ protected:
const nsPoint &aPoint,
PRInt32 &aClosestLine);
void NextOverAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines);
void TryAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines);
void SetFlags(PRUint32 aFlags) {
mState &= ~NS_BLOCK_FLAGS_MASK;
@ -310,12 +310,6 @@ protected:
void SlideLine(nsBlockReflowState& aState,
nsLineBox* aLine, nscoord aDY);
/** 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(nsPresContext* aPresContext);
virtual PRIntn GetSkipSides() const;
virtual void ComputeFinalSize(const nsHTMLReflowState& aReflowState,
@ -343,6 +337,18 @@ protected:
nsresult DoRemoveFrame(nsPresContext* aPresContext,
nsIFrame* aDeletedFrame);
/** 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();
/**
* Remove a float from our float list and also the float cache
* for the line its placeholder is on.
*/
line_iterator RemoveFloat(nsIFrame* aFloat);
// Remove a float, abs, rel positioned frame from the appropriate block's list
static void DoRemoveOutOfFlowFrame(nsPresContext* aPresContext,
nsIFrame* aFrame);
@ -493,16 +499,20 @@ protected:
nsIFrame*& aFrameResult);
nsresult PullFrameFrom(nsBlockReflowState& aState,
nsLineBox* aToLine,
nsLineList& aFromContainer,
nsLineBox* aLine,
nsBlockFrame* aFromContainer,
PRBool aFromOverflowLine,
nsLineList::iterator aFromLine,
PRBool aUpdateGeometricParent,
PRBool aDamageDeletedLines,
nsIFrame*& aFrameResult);
void PushLines(nsBlockReflowState& aState,
nsLineList::iterator aLineBefore);
void ReparentFloats(nsIFrame* aFirstFrame,
nsBlockFrame* aOldParent, PRBool aFromOverflow);
//----------------------------------------
//XXX
virtual void PaintChildren(nsPresContext* aPresContext,

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

@ -0,0 +1,876 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <roc@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsHTMLContainerFrame.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsISupports.h"
#include "nsIAtom.h"
#include "nsPresContext.h"
#include "nsHTMLParts.h"
#include "nsLayoutAtoms.h"
#include "nsStyleConsts.h"
#include "nsCOMPtr.h"
#include "nsReflowPath.h"
class nsColumnSetFrame : public nsHTMLContainerFrame {
public:
nsColumnSetFrame();
NS_IMETHOD SetInitialChildList(nsPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList);
NS_IMETHOD Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD AppendFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aFrameList);
NS_IMETHOD InsertFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList);
NS_IMETHOD RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aOldFrame);
virtual nsIFrame* GetContentInsertionFrame() {
return GetFirstChild(nsnull)->GetContentInsertionFrame();
}
virtual nsIAtom* GetType() const;
#ifdef DEBUG
NS_IMETHOD GetFrameName(nsAString& aResult) const {
return MakeFrameName(NS_LITERAL_STRING("ColumnSet"), aResult);
}
#endif
protected:
nscoord mLastBalanceHeight;
nsReflowStatus mLastFrameStatus;
virtual PRIntn GetSkipSides() const;
/**
* These are the parameters that control the layout of columns.
*/
struct ReflowConfig {
PRInt32 mBalanceColCount;
nscoord mColWidth;
nscoord mExpectedWidthLeftOver;
nscoord mColGap;
nscoord mColMaxHeight;
};
/**
* Similar to nsBlockFrame::DrainOverflowLines. Locate any columns not
* handled by our prev-in-flow, and any columns sitting on our own
* overflow list, and put them in our primary child list for reflowing.
*/
void DrainOverflowColumns();
/**
* The basic reflow strategy is to call this function repeatedly to
* obtain specific parameters that determine the layout of the
* columns. This function will compute those parameters from the CSS
* style. This function will also be responsible for implementing
* the state machine that controls column balancing.
*/
ReflowConfig ChooseColumnStrategy(const nsHTMLReflowState& aReflowState);
/**
* Reflow column children. Returns PR_TRUE iff the content that was reflowed
* fit into the mColMaxHeight.
*/
PRBool ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowReason aReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aLastColumnUnbounded);
};
/**
* Tracking issues:
*
* XXX cursor movement around the top and bottom of colums seems to make the editor
* lose the caret.
*
* XXX should we support CSS columns applied to table elements?
*/
nsresult
NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame;
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
// set the state flags (if any are provided)
it->AddStateBits(aStateFlags);
*aNewFrame = it;
return NS_OK;
}
nsColumnSetFrame::nsColumnSetFrame()
: nsHTMLContainerFrame(), mLastBalanceHeight(NS_INTRINSICSIZE),
mLastFrameStatus(NS_FRAME_COMPLETE)
{
}
nsIAtom*
nsColumnSetFrame::GetType() const
{
return nsLayoutAtoms::columnSetFrame;
}
NS_IMETHODIMP
nsColumnSetFrame::SetInitialChildList(nsPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList)
{
NS_ASSERTION(!aListName, "Only default child list supported");
NS_ASSERTION(aChildList && !aChildList->GetNextSibling(),
"initial child list must have exactly one child");
// Queue up the frames for the content frame
return nsHTMLContainerFrame::SetInitialChildList(aPresContext, nsnull, aChildList);
}
static nscoord GetAvailableContentWidth(const nsHTMLReflowState& aReflowState) {
if (aReflowState.availableWidth == NS_INTRINSICSIZE) {
return NS_INTRINSICSIZE;
}
nscoord borderPaddingWidth =
aReflowState.mComputedBorderPadding.left +
aReflowState.mComputedBorderPadding.right;
return PR_MAX(0, aReflowState.availableWidth - borderPaddingWidth);
}
static nscoord GetAvailableContentHeight(const nsHTMLReflowState& aReflowState) {
if (aReflowState.availableHeight == NS_INTRINSICSIZE) {
return NS_INTRINSICSIZE;
}
nscoord borderPaddingHeight =
aReflowState.mComputedBorderPadding.top +
aReflowState.mComputedBorderPadding.bottom;
return PR_MAX(0, aReflowState.availableHeight - borderPaddingHeight);
}
nsColumnSetFrame::ReflowConfig
nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState)
{
const nsStyleColumn* colStyle = GetStyleColumn();
nscoord availContentWidth = GetAvailableContentWidth(aReflowState);
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
availContentWidth = aReflowState.mComputedWidth;
}
nscoord colHeight = GetAvailableContentHeight(aReflowState);
if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) {
colHeight = aReflowState.mComputedHeight;
}
nscoord colGap = 0;
switch (colStyle->mColumnGap.GetUnit()) {
case eStyleUnit_Coord:
colGap = colStyle->mColumnGap.GetCoordValue();
break;
case eStyleUnit_Percent:
if (availContentWidth != NS_INTRINSICSIZE) {
colGap = NSToCoordRound(colStyle->mColumnGap.GetPercentValue()*availContentWidth);
}
break;
default:
NS_NOTREACHED("Unknown gap type");
break;
}
PRInt32 numColumns = colStyle->mColumnCount;
nscoord colWidth = NS_INTRINSICSIZE;
if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
colWidth = colStyle->mColumnWidth.GetCoordValue();
// Reduce column count if necesary to make columns fit in the
// available width. Compute max number of columns that fit in
// availContentWidth, satisfying colGap*(maxColumns - 1) +
// colWidth*maxColumns <= availContentWidth
if (availContentWidth != NS_INTRINSICSIZE && colWidth + colGap > 0
&& numColumns > 0) {
// This expression uses truncated rounding, which is what we
// want
PRInt32 maxColumns = (availContentWidth + colGap)/(colGap + colWidth);
numColumns = PR_MAX(1, PR_MIN(numColumns, maxColumns));
}
} else if (numColumns > 0 && availContentWidth != NS_INTRINSICSIZE) {
nscoord widthMinusGaps = availContentWidth - colGap*(numColumns - 1);
colWidth = widthMinusGaps/numColumns;
}
// Take care of the situation where there's only one column but it's
// still too wide
colWidth = PR_MAX(1, PR_MIN(colWidth, availContentWidth));
nscoord expectedWidthLeftOver = 0;
if (colWidth != NS_INTRINSICSIZE && availContentWidth != NS_INTRINSICSIZE) {
// distribute leftover space
// First, determine how many columns will be showing if the column
// count is auto
if (numColumns <= 0) {
// choose so that colGap*(nominalColumnCount - 1) +
// colWidth*nominalColumnCount is nearly availContentWidth
// make sure to round down
numColumns = (availContentWidth + colGap)/(colGap + colWidth);
}
// Compute extra space and divide it among the columns
nscoord extraSpace = availContentWidth - (colWidth*numColumns + colGap*(numColumns - 1));
nscoord extraToColumns = extraSpace/numColumns;
colWidth += extraToColumns;
expectedWidthLeftOver = extraSpace - (extraToColumns*numColumns);
}
// NOTE that the non-balancing behavior for non-auto computed height
// is not in the CSS3 columns draft as of 18 January 2001
if (aReflowState.mComputedHeight == NS_INTRINSICSIZE) {
// Balancing!
if (numColumns <= 0) {
// Hmm, auto column count, column width or available width is unknown,
// and balancing is required. Let's just use one column then.
numColumns = 1;
}
colHeight = PR_MIN(mLastBalanceHeight, GetAvailableContentHeight(aReflowState));
} else {
// No balancing, so don't limit the column count
numColumns = PR_INT32_MAX;
}
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colWidth=%d, expectedWidthLeftOver=%d, colHeight=%d, colGap=%d\n",
numColumns, colWidth, expectedWidthLeftOver, colHeight, colGap);
#endif
ReflowConfig config = { numColumns, colWidth, expectedWidthLeftOver, colGap, colHeight };
return config;
}
// XXX copied from nsBlockFrame, should this be moved to nsContainerFrame?
static void
PlaceFrameView(nsIFrame* aFrame)
{
if (aFrame->HasView())
nsContainerFrame::PositionFrameView(aFrame->GetPresContext(), aFrame);
else
nsContainerFrame::PositionChildViews(aFrame->GetPresContext(), aFrame);
}
static void MoveChildTo(nsIFrame* aParent, nsIFrame* aChild, nsPoint aOrigin) {
if (aChild->GetPosition() == aOrigin) {
return;
}
nsRect* overflowArea = aChild->GetOverflowAreaProperty(PR_FALSE);
nsRect r = overflowArea ? *overflowArea : nsRect(nsPoint(0, 0), aChild->GetSize());
r += aChild->GetPosition();
aParent->Invalidate(r);
r -= aChild->GetPosition();
aChild->SetPosition(aOrigin);
r += aOrigin;
aParent->Invalidate(r);
PlaceFrameView(aChild);
}
PRBool
nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowReason aKidReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aUnboundedLastColumn) {
PRBool allFit = PR_TRUE;
PRBool RTL = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
PRBool shrinkingHeightOnly = aKidReason == eReflowReason_Resize &&
mLastBalanceHeight > aConfig.mColMaxHeight;
#ifdef DEBUG_roc
printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n",
mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount,
aConfig.mColWidth, aConfig.mColGap);
#endif
DrainOverflowColumns();
if (mLastBalanceHeight != aConfig.mColMaxHeight) {
mLastBalanceHeight = aConfig.mColMaxHeight;
// XXX Seems like this could fire if incremental reflow pushed the column set
// down so we reflow incrementally with a different available height.
// We need a way to do an incremental reflow and be sure availableHeight
// changes are taken account of! Right now I think block frames with absolute
// children might exit early.
NS_ASSERTION(aKidReason != eReflowReason_Incremental,
"incremental reflow should not have changed the balance height");
}
// get our border and padding
const nsMargin &borderPadding = aReflowState.mComputedBorderPadding;
nsRect contentRect(0, 0, 0, 0);
aDesiredSize.mMaxElementWidth = 0;
nsRect overflowRect(0, 0, 0, 0);
nsIFrame* child = mFrames.FirstChild();
nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top);
// For RTL, figure out where the last column's left edge should be. Since the
// columns might not fill the frame exactly, we need to account for the
// slop. Otherwise we'll waste time moving the columns by some tiny
// amount unnecessarily.
nscoord targetX = borderPadding.left;
if (RTL) {
nscoord availWidth = aReflowState.availableWidth;
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
availWidth = aReflowState.mComputedWidth;
}
if (availWidth != NS_INTRINSICSIZE) {
childOrigin.x += availWidth - aConfig.mColWidth;
targetX += aConfig.mExpectedWidthLeftOver;
#ifdef DEBUG_roc
printf("*** childOrigin.x = %d\n", childOrigin.x);
#endif
}
}
int columnCount = 0;
PRBool reflowNext = PR_FALSE;
while (child) {
// Try to skip reflowing the child. We can't skip if the child is dirty. We also can't
// skip if the next column is dirty, because the next column's first line(s)
// might be pullable back to this column.
PRBool skipIncremental = aKidReason == eReflowReason_Incremental
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)
&& (!child->GetNextSibling()
|| !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY))
&& !aDesiredSize.mComputeMEW;
PRBool skipResizeHeightShrink = shrinkingHeightOnly
&& child->GetSize().height <= aConfig.mColMaxHeight;
if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) {
// This child does not need to be reflowed, but we may need to move it
MoveChildTo(this, child, childOrigin);
// If this is the last frame then make sure we get the right status
if (child->GetNextSibling()) {
aStatus = NS_FRAME_NOT_COMPLETE;
} else {
aStatus = mLastFrameStatus;
}
#ifdef DEBUG_roc
printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n",
columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus);
#endif
} else {
nsSize availSize(aConfig.mColWidth, aConfig.mColMaxHeight);
if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
availSize.height = GetAvailableContentHeight(aReflowState);
}
nsReflowReason tmpReason = aKidReason;
if (reflowNext && aKidReason == eReflowReason_Incremental
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)) {
// If this frame was not being incrementally reflowed but was
// just reflowed because the previous frame wants us to
// reflow, then force this child to reflow its dirty lines!
// XXX what we should really do here is add the child block to
// the incremental reflow path! Currently I think if there's an
// incremental reflow targeted only at the absolute frames of a
// column, then the column will be dirty BUT reflowing it will
// not reflow any lines affected by the prev-in-flow!
tmpReason = eReflowReason_Dirty;
}
nsHTMLReflowState kidReflowState(GetPresContext(), aReflowState, child,
availSize, tmpReason);
#ifdef DEBUG_roc
printf("*** Reflowing child #%d %p: reason = %d, availHeight=%d\n",
columnCount, (void*)child, tmpReason, availSize.height);
#endif
// Note if the column's next in flow is not being changed by this incremental reflow.
// This may allow the current column to avoid trying to pull lines from the next column.
if (child->GetNextSibling() && aKidReason == eReflowReason_Incremental &&
!(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) {
kidReflowState.mFlags.mNextInFlowUntouched = PR_TRUE;
}
nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags);
// XXX it would be cool to consult the space manager for the
// previous block to figure out the region of floats from the
// previous column that extend into this column, and subtract
// that region from the new space manager. So you could stick a
// really big float in the first column and text in following
// columns would flow around it.
// Reflow the frame
ReflowChild(child, GetPresContext(), kidDesiredSize, kidReflowState,
childOrigin.x + kidReflowState.mComputedMargin.left,
childOrigin.y + kidReflowState.mComputedMargin.top,
0, aStatus);
if (kidDesiredSize.height > aConfig.mColMaxHeight) {
allFit = PR_FALSE;
}
reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
#ifdef DEBUG_roc
printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d\n",
columnCount, (void*)child, aStatus, kidDesiredSize.width, kidDesiredSize.height);
#endif
NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
FinishReflowChild(child, GetPresContext(), &kidReflowState,
kidDesiredSize, childOrigin.x, childOrigin.y, 0);
if (aDesiredSize.mComputeMEW) {
aDesiredSize.mMaxElementWidth = PR_MAX(aDesiredSize.mMaxElementWidth,
kidDesiredSize.mMaxElementWidth);
}
}
contentRect.UnionRect(contentRect, child->GetRect());
ConsiderChildOverflow(GetPresContext(), overflowRect, child);
// Build a continuation column if necessary
nsIFrame* kidNextInFlow = child->GetNextInFlow();
if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
break;
} else {
++columnCount;
// Make sure that the column has a next-in-flow. If not, we must
// create one to hold the overflowing stuff, even if we're just
// going to put it on our overflow list and let *our*
// next in flow handle it.
if (!kidNextInFlow) {
NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
"We have to create a continuation, but the block doesn't want us to reflow it?");
// We need to create a continuing column
nsIFrame* continuation;
nsresult rv = CreateNextInFlow(GetPresContext(), this, child, continuation);
if (NS_FAILED(rv)) {
NS_NOTREACHED("Couldn't create continuation");
break;
}
continuation->AddStateBits(NS_BLOCK_SPACE_MGR);
// Do an initial reflow if we're going to reflow this thing.
aKidReason = eReflowReason_Initial;
}
if (columnCount >= aConfig.mBalanceColCount) {
// No more columns allowed here. Stop.
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
// Move any of our leftover columns to our overflow list. Our
// next-in-flow will eventually pick them up.
nsIFrame* continuationColumns = child->GetNextSibling();
if (continuationColumns) {
SetOverflowFrames(GetPresContext(), continuationColumns);
child->SetNextSibling(nsnull);
}
break;
}
}
// Advance to the next column
child = child->GetNextSibling();
if (child) {
if (!RTL) {
childOrigin.x += aConfig.mColWidth + aConfig.mColGap;
} else {
childOrigin.x -= aConfig.mColWidth + aConfig.mColGap;
}
#ifdef DEBUG_roc
printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x);
#endif
}
}
// If we're doing RTL, we need to make sure our last column is at the left-hand side of the frame.
if (RTL && childOrigin.x != targetX) {
overflowRect = nsRect(0, 0, 0, 0);
contentRect = nsRect(0, 0, 0, 0);
PRInt32 deltaX = targetX - childOrigin.x;
#ifdef DEBUG_roc
printf("*** CHILDORIGIN.x = %d, targetX = %d, DELTAX = %d\n", childOrigin.x, targetX, deltaX);
#endif
for (child = mFrames.FirstChild(); child; child = child->GetNextSibling()) {
MoveChildTo(this, child, child->GetPosition() + nsPoint(deltaX, 0));
ConsiderChildOverflow(GetPresContext(), overflowRect, child);
contentRect.UnionRect(contentRect, child->GetRect());
}
}
mLastFrameStatus = aStatus;
// contentRect included the borderPadding.left,borderPadding.top of the child rects
contentRect -= nsPoint(borderPadding.left, borderPadding.top);
nsSize contentSize = nsSize(contentRect.XMost(), contentRect.YMost());
// Apply computed and min/max values
if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) {
contentSize.height = aReflowState.mComputedHeight;
} else {
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) {
contentSize.height = PR_MIN(aReflowState.mComputedMaxHeight, contentSize.height);
}
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
contentSize.height = PR_MAX(aReflowState.mComputedMinHeight, contentSize.height);
}
}
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
contentSize.width = aReflowState.mComputedWidth;
} else {
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
contentSize.width = PR_MIN(aReflowState.mComputedMaxWidth, contentSize.width);
}
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
contentSize.width = PR_MAX(aReflowState.mComputedMinWidth, contentSize.width);
}
}
aDesiredSize.height = borderPadding.top + contentSize.height +
borderPadding.bottom;
aDesiredSize.width = contentSize.width + borderPadding.left + borderPadding.right;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
aDesiredSize.mMaximumWidth = aDesiredSize.width;
if (aDesiredSize.mComputeMEW) {
// add in padding.
aDesiredSize.mMaxElementWidth += borderPadding.left + borderPadding.right;
}
overflowRect.UnionRect(overflowRect, nsRect(0, 0, aDesiredSize.width, aDesiredSize.height));
aDesiredSize.mOverflowArea = overflowRect;
#ifdef DEBUG_roc
printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_COMPLETE(aStatus)
&& !NS_FRAME_IS_TRUNCATED(aStatus));
#endif
return allFit && NS_FRAME_IS_COMPLETE(aStatus)
&& !NS_FRAME_IS_TRUNCATED(aStatus);
}
static nscoord ComputeSumOfChildHeights(nsIFrame* aFrame) {
nscoord totalHeight = 0;
for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
// individual columns don't have borders or padding so this is a
// reasonable way to get their content height
totalHeight += f->GetSize().height;
}
return totalHeight;
}
void
nsColumnSetFrame::DrainOverflowColumns()
{
// First grab the prev-in-flows overflows and reparent them to this
// frame.
nsColumnSetFrame* prev = NS_STATIC_CAST(nsColumnSetFrame*, mPrevInFlow);
if (prev) {
nsIFrame* overflows = prev->GetOverflowFrames(GetPresContext(), PR_TRUE);
if (overflows) {
// Make all the frames on the overflow list mine
nsIFrame* lastFrame = nsnull;
for (nsIFrame* f = overflows; f; f = f->GetNextSibling()) {
f->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(GetPresContext(), f, prev, this);
// Get the next frame
lastFrame = f;
}
NS_ASSERTION(lastFrame, "overflow list was created with no frames");
lastFrame->SetNextSibling(mFrames.FirstChild());
mFrames.SetFrames(overflows);
}
}
// Now pull back our own overflows and append them to our children.
// We don't need to reparent them since we're already their parent.
nsIFrame* overflows = GetOverflowFrames(GetPresContext(), PR_TRUE);
if (overflows) {
mFrames.AppendFrames(this, overflows);
}
}
NS_IMETHODIMP
nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame", aReflowState.reason);
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
// Initialize OUT parameter
aStatus = NS_FRAME_COMPLETE;
//------------ Handle Incremental Reflow -----------------
nsReflowReason kidReason = aReflowState.reason;
if ( aReflowState.reason == eReflowReason_Incremental ) {
nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
// Dirty any frames on the incremental reflow path
nsReflowPath *path = aReflowState.path;
nsReflowPath::iterator iter = path->FirstChild();
nsReflowPath::iterator end = path->EndChildren();
for ( ; iter != end; ++iter) {
(*iter)->AddStateBits(NS_FRAME_IS_DIRTY);
}
// See if it's targeted at us
if (command) {
nsReflowType reflowType;
command->GetType(reflowType);
switch (reflowType) {
case eReflowType_StyleChanged:
kidReason = eReflowReason_StyleChange;
break;
// if its a dirty type then reflow us with a dirty reflow
case eReflowType_ReflowDirty:
kidReason = eReflowReason_Dirty;
break;
default:
NS_ERROR("Unexpected Reflow Type");
}
}
}
ReflowConfig config = ChooseColumnStrategy(aReflowState);
PRBool isBalancing = config.mBalanceColCount < PR_INT32_MAX;
// If balancing, then we allow the last column to grow to unbounded
// height during the first reflow. This gives us a way to estimate
// what the average column height should be, because we can measure
// the heights of all the columns and sum them up. But don't do this
// if we have a next in flow because we don't want to suck all its
// content back here and then have to push it out again!
nsIFrame* nextInFlow = GetNextInFlow();
PRBool unboundedLastColumn = isBalancing && nextInFlow;
PRBool feasible = ReflowChildren(aDesiredSize, aReflowState, kidReason,
aStatus, config, unboundedLastColumn);
if (isBalancing) {
nscoord availableContentHeight = GetAvailableContentHeight(aReflowState);
// Termination of the algorithm below is guaranteed because
// knownFeasibleHeight - knownInfeasibleHeight decreases in every
// iteration.
nscoord knownFeasibleHeight = NS_INTRINSICSIZE;
nscoord knownInfeasibleHeight = 0;
while (1) {
nscoord maxHeight = 0;
for (nsIFrame* f = mFrames.FirstChild(); f; f = f->GetNextSibling()) {
maxHeight = PR_MAX(maxHeight, f->GetSize().height);
}
// Record what we learned from the last reflow
if (feasible) {
// maxHeight is feasible (and always maxHeight <=
// mLastBalanceHeight)
knownFeasibleHeight = PR_MIN(knownFeasibleHeight, maxHeight);
// Furthermore, no height less than the height of the last
// column can ever be feasible.
if (mFrames.GetLength() == config.mBalanceColCount) {
knownInfeasibleHeight = PR_MAX(knownInfeasibleHeight,
mFrames.LastChild()->GetSize().height - 1);
}
} else {
knownInfeasibleHeight = PR_MAX(knownInfeasibleHeight, mLastBalanceHeight);
if (unboundedLastColumn) {
// The last column is unbounded, so all content got reflowed, so the
// maxHeight is feasible.
knownFeasibleHeight = PR_MIN(knownFeasibleHeight, maxHeight);
}
}
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
knownInfeasibleHeight, knownFeasibleHeight);
#endif
if (knownInfeasibleHeight >= knownFeasibleHeight - 1) {
// knownFeasibleHeight is where we want to be
break;
}
if (knownInfeasibleHeight >= availableContentHeight) {
break;
}
nscoord nextGuess = (knownFeasibleHeight + knownInfeasibleHeight)/2;
// The constant of 600 twips is arbitrary. It's about two line-heights.
if (knownFeasibleHeight - nextGuess < 600) {
// We're close to our target, so just try shrinking just the
// minimum amount that will cause one of our columns to break
// differently.
nextGuess = knownFeasibleHeight - 1;
} else if (unboundedLastColumn) {
// Make a guess by dividing that into N columns. Add some slop
// to try to make it on the feasible side. The constant of
// 600 twips is arbitrary. It's about two line-heights.
nextGuess = ComputeSumOfChildHeights(this)/config.mBalanceColCount + 600;
// Sanitize it
nextGuess = PR_MIN(PR_MAX(nextGuess, knownInfeasibleHeight + 1),
knownFeasibleHeight - 1);
} else if (knownFeasibleHeight == NS_INTRINSICSIZE) {
// This can happen when we had a next-in-flow so we didn't
// want to do an unbounded height measuring step. Let's just increase
// from the infeasible height by some reasonable amount.
nextGuess = knownInfeasibleHeight*2 + 600;
}
// Don't bother guessing more than our height constraint.
nextGuess = PR_MIN(availableContentHeight, nextGuess);
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
#endif
config.mColMaxHeight = nextGuess;
unboundedLastColumn = PR_FALSE;
feasible = ReflowChildren(aDesiredSize, aReflowState,
eReflowReason_Resize, aStatus, config, PR_FALSE);
}
if (!feasible) {
// We may need to reflow one more time at the feasible height to
// get a valid layout.
PRBool skip = PR_FALSE;
if (knownInfeasibleHeight >= availableContentHeight) {
config.mColMaxHeight = availableContentHeight;
if (mLastBalanceHeight == availableContentHeight) {
skip = PR_TRUE;
}
} else {
config.mColMaxHeight = knownFeasibleHeight;
}
if (!skip) {
ReflowChildren(aDesiredSize, aReflowState,
eReflowReason_Resize, aStatus, config, PR_FALSE);
}
}
}
CheckInvalidateSizeChange(GetPresContext(), aDesiredSize, aReflowState);
FinishAndStoreOverflow(&aDesiredSize);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
return NS_OK;
}
PRIntn
nsColumnSetFrame::GetSkipSides() const
{
return 0;
}
NS_IMETHODIMP
nsColumnSetFrame::AppendFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aFrameList)
{
NS_NOTREACHED("AppendFrames not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsColumnSetFrame::InsertFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
NS_NOTREACHED("InsertFrames not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsColumnSetFrame::RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aOldFrame)
{
NS_NOTREACHED("RemoveFrame not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}

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

@ -177,6 +177,10 @@ inline nsresult
NS_NewWBRFrame(nsIPresShell* aPresShell, nsIFrame** aResult) {
return NS_NewEmptyFrame(aPresShell, aResult);
}
nsresult
NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags );
nsresult
NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsIFrame** aResult);
nsresult

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

@ -110,7 +110,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mLineLayout = nsnull;
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mUnused = 0;
mFlags.mNextInFlowUntouched = PR_FALSE;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -142,7 +142,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mLineLayout = nsnull;
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mUnused = 0;
mFlags.mNextInFlowUntouched = PR_FALSE;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -152,6 +152,13 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
#endif // IBMBIDI
}
static PRBool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
{
nsIFrame* frameNext = aFrame->GetNextInFlow();
nsIFrame* parentNext = aParent->GetNextInFlow();
return frameNext && parentNext && frameNext->GetParent() == parentNext;
}
// Initialize a reflow state for a child frames reflow. Some state
// is copied from the parent reflow state; the remaining state is
// computed.
@ -184,6 +191,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -229,6 +238,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -274,6 +285,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;

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

@ -244,10 +244,11 @@ struct nsHTMLReflowState {
struct ReflowStateFlags {
PRUint16 mSpecialHeightReflow:1; // used by tables to communicate special reflow (in process) to handle
// percent height frames inside cells which may not have computed heights
PRUint16 mNextInFlowUntouched:1; // nothing in the frame's next-in-flow (or its descendants)
// is changing
PRUint16 mIsTopOfPage:1; // is the current context at the top of a page?
PRUint16 mBlinks:1; // Keep track of text-decoration: blink
PRUint16 mVisualBidiFormControl:1; // Keep track of descendants of form controls on Visual Bidi pages
PRUint16 mUnused:12; // for future use
} mFlags;
#ifdef IBMBIDI

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

@ -90,6 +90,7 @@ CPPSRCS = \
nsBlockReflowContext.cpp \
nsBlockReflowState.cpp \
nsBulletFrame.cpp \
nsColumnSetFrame.cpp \
nsContainerFrame.cpp \
nsFirstLetterFrame.cpp \
nsFrame.cpp \

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

@ -730,7 +730,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(aPresContext);
DrainOverflowLines();
switch (aReflowState.reason) {
case eReflowReason_Initial:
@ -827,13 +827,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
if (NS_FAILED(rv)) return rv;
nsIFrame* nextInFlow = GetNextInFlow();
if (nextInFlow && NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
if (GetOverflowLines() || GetOverflowPlaceholders()) {
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
}
// If the block is complete, put continuted floats in the closest ancestor
// block that uses the same space manager and leave the block complete; this
// allows subsequent lines on the page to be impacted by floats. If the
@ -917,6 +910,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
if (GetOverflowLines()) {
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
if (NS_STYLE_OVERFLOW_CLIP == aReflowState.mStyleDisplay->mOverflowX) {
state.mReflowStatus = NS_FRAME_COMPLETE;
}
@ -953,9 +950,6 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
#endif
// Determine if we need to repaint our border, background or outline
CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
// Let the absolutely positioned container reflow any absolutely positioned
// child frames that need to be reflowed, e.g., elements with a percentage
// based width/height
@ -990,6 +984,10 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
FinishAndStoreOverflow(&aMetrics);
}
// Determine if we need to repaint our border, background or outline
CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
// Clear the space manager pointer in the block reflow state so we
// don't waste time translating the coordinate system back on a dead
// space manager.
@ -1830,7 +1828,7 @@ nsBlockFrame::FindLineFor(nsIFrame* aFrame)
if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame())
return line;
}
}
}
}
return line_end;
@ -1927,6 +1925,51 @@ WrappedLinesAreDirty(nsLineList::iterator aLine,
static void PlaceFrameView(nsPresContext* aPresContext, nsIFrame* aFrame);
static PRUint8 CombineBreakType(PRUint8 aOrigBreakType, PRUint8 aNewBreakType);
static void CollectFloats(nsIFrame* aFrame, nsIFrame* aBlockParent,
nsIFrame** aHead, nsIFrame** aTail);
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.
*/
void
nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
nsBlockFrame* aOldParent, PRBool aFromOverflow) {
nsIFrame* head = nsnull;
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()) {
ReparentFrame(f, aOldParent, this);
}
}
}
/**
* Reflow the dirty lines
@ -1976,6 +2019,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 lastLineMovedUp = PR_FALSE;
PRUint8 floatBreakType = NS_STYLE_CLEAR_NONE;
// Reflow the lines that are already ours
@ -1998,12 +2042,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If we're supposed to update our maximum width, then we'll also need to
// reflow this line if it's line wrapped and any of the continuing lines
// are dirty. If we are printing (constrained height), always reflow
// the line.
// are dirty.
// XXXperf XXXldb Check that the previous line was not wrapped
// before doing this check (it's O(N^2) as written now).
if ((NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight) ||
(!line->IsDirty() &&
if ((!line->IsDirty() &&
aState.GetFlag(BRS_COMPUTEMAXWIDTH) &&
::WrappedLinesAreDirty(line, line_end))) {
line->MarkDirty();
@ -2025,15 +2067,17 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
}
PRBool previousMarginWasDirty = line->IsPreviousMarginDirty();
if (previousMarginWasDirty && !line->IsDirty()) {
if (previousMarginWasDirty) {
// If the previous margin is dirty, reflow the current line
line->MarkDirty();
}
line->ClearPreviousMarginDirty();
// See if there's any reflow damage that requires that we mark the
// line dirty.
if (!line->IsDirty()) {
line->ClearPreviousMarginDirty();
} else if (line->mBounds.YMost() + deltaY > aState.mBottomEdge) {
// Lines that aren't dirty but get slid past our height constraint must
// be reflowed.
line->MarkDirty();
} else if (!line->IsDirty()) {
// See if there's any reflow damage that requires that we mark the
// line dirty.
PropagateFloatDamage(aState, line, deltaY);
}
@ -2050,6 +2094,8 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// Now repair the line and update |aState.mY| by calling
// |ReflowLine| or |SlideLine|.
if (line->IsDirty()) {
lastLineMovedUp = PR_TRUE;
// Compute the dirty lines "before" YMost, after factoring in
// the running deltaY value - the running value is implicit in
// aState.mY.
@ -2101,10 +2147,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// the next line dirty.
line.next()->MarkPreviousMarginDirty();
// since it's marked dirty, nobody will care about |deltaY|
} else {
deltaY = line->mBounds.YMost() - oldYMost;
}
deltaY = line->mBounds.YMost() - oldYMost;
} else {
lastLineMovedUp = deltaY < 0;
if (deltaY != 0)
SlideLine(aState, line, deltaY);
else
@ -2162,94 +2209,147 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
if (repositionViews)
::PlaceFrameView(aState.mPresContext, this);
PRBool touchedNextInFlow = PR_FALSE;
// Pull data from a next-in-flow if there's still room for more
// content here.
while (keepGoing && (nsnull != aState.mNextInFlow)) {
touchedNextInFlow = PR_TRUE;
// Grab first line from our next-in-flow
nsBlockFrame* nextInFlow = aState.mNextInFlow;
line_iterator nifLine = nextInFlow->begin_lines();
if (nifLine == nextInFlow->end_lines()) {
aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
continue;
// 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)) {
if (aState.mNextInFlow) {
aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
}
// XXX See if the line is not dirty; if it's not maybe we can
// avoid the pullup if it can't fit? This is important if we want
// to avoid reflowing our next-in-flow!
nsLineBox *toMove = nifLine;
nextInFlow->mLines.erase(nifLine);
if (0 == toMove->GetChildCount()) {
// The line is empty. Try the next one.
NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
aState.FreeLineBox(toMove);
continue;
}
// XXX move to a subroutine: run-in, overflow, pullframe and this do this
// Make the children in the line ours.
nsIFrame* frame = toMove->mFirstChild;
nsIFrame* lastFrame = nsnull;
PRInt32 n = toMove->GetChildCount();
while (--n >= 0) {
frame->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, mNextInFlow, this);
lastFrame = frame;
frame = frame->GetNextSibling();
}
lastFrame->SetNextSibling(nsnull);
// Add line to our line list
if (aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
}
line = mLines.before_insert(end_lines(), toMove);
// 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.
if (line->HasFloats()) {
nsFloatCache* fc = line->GetFirstFloat();
while (fc) {
if (fc->mPlaceholder) {
nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
if (floatFrame)
aState.mNextInFlow->mFloats.RemoveFrame(floatFrame);
} else {
// Pull data from a next-in-flow if there's still room for more
// content here.
while (keepGoing && (nsnull != aState.mNextInFlow)) {
// Grab first line from our next-in-flow
nsBlockFrame* nextInFlow = aState.mNextInFlow;
line_iterator nifLine = nextInFlow->begin_lines();
nsLineBox *toMove;
PRBool collectOverflowFloats;
if (nifLine != nextInFlow->end_lines()) {
toMove = nifLine;
nextInFlow->mLines.erase(nifLine);
collectOverflowFloats = PR_FALSE;
} else {
// Grab an overflow line if there are any
nsLineList* overflowLines = nextInFlow->RemoveOverflowLines();
if (!overflowLines) {
aState.mNextInFlow =
NS_STATIC_CAST(nsBlockFrame*, nextInFlow->mNextInFlow);
continue;
}
fc = fc->Next();
nifLine = overflowLines->begin();
NS_ASSERTION(nifLine != overflowLines->end(),
"Stored overflow line list should not be empty");
toMove = nifLine;
nifLine = overflowLines->erase(nifLine);
if (nifLine != overflowLines->end()) {
// We need to this remove-and-put-back dance because we want
// to avoid making the overflow line list empty while it's
// stored in the property (because the property has the
// invariant that the list is never empty).
nextInFlow->SetOverflowLines(overflowLines);
}
collectOverflowFloats = PR_TRUE;
}
if (0 == toMove->GetChildCount()) {
// The line is empty. Try the next one.
NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
aState.FreeLineBox(toMove);
continue;
}
// XXX move to a subroutine: run-in, overflow, pullframe and this do this
// Make the children in the line ours.
nsIFrame* frame = toMove->mFirstChild;
nsIFrame* lastFrame = nsnull;
PRInt32 n = toMove->GetChildCount();
while (--n >= 0) {
ReparentFrame(frame, nextInFlow, this);
lastFrame = frame;
frame = frame->GetNextSibling();
}
lastFrame->SetNextSibling(nsnull);
// Add line to our line list
if (aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
}
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();
}
}
}
// 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
// line to be created; see SplitLine's callers for examples of
// when this happens).
while (line != end_lines()) {
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
if (NS_FAILED(rv)) {
NS_WARNING("Line reflow failed");
return rv;
}
if (!keepGoing) {
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
nsRect lca(line->GetCombinedArea());
IndentBy(stdout, gNoiseIndent);
printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
NS_STATIC_CAST(void*, line.get()), aState.mY,
line->mBounds.x, line->mBounds.y,
line->mBounds.width, line->mBounds.height,
lca.x, lca.y, lca.width, lca.height,
deltaY, aState.mPrevBottomMargin.get(),
line->GetChildCount());
}
#endif
if (0 == line->GetChildCount()) {
DeleteLine(aState, line, line_end);
}
break;
}
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
}
}
// 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
// line to be created; see SplitLine's callers for examples of
// when this happens).
while (line != end_lines()) {
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
if (NS_FAILED(rv)) {
return rv;
}
if (!keepGoing) {
if (0 == line->GetChildCount()) {
DeleteLine(aState, line, line_end);
}
break;
}
// If this is an inline frame then its time to stop
++line;
aState.AdvanceToNextLine();
if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
}
@ -2263,10 +2363,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.mY += metrics.height;
}
if (touchedNextInFlow && NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
#ifdef DEBUG
if (gNoisyReflow) {
gNoiseIndent--;
@ -2522,16 +2618,27 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
// First check our remaining lines
if (end_lines() != aLine.next()) {
return PullFrameFrom(aState, aLine, mLines, aLine.next(), PR_FALSE,
return PullFrameFrom(aState, aLine, this, PR_FALSE, aLine.next(),
aDamageDeletedLines, aFrameResult);
}
// Pull frames from the next-in-flow(s) until we can't
NS_ASSERTION(!GetOverflowLines(),
"Our overflow lines should have been removed at the start of reflow");
// Try each next in flows
nsBlockFrame* nextInFlow = aState.mNextInFlow;
while (nsnull != nextInFlow) {
if (! nextInFlow->mLines.empty()) {
return PullFrameFrom(aState, aLine, nextInFlow->mLines,
nextInFlow->mLines.begin(), PR_TRUE,
while (nextInFlow) {
// first normal lines, then overflow lines
if (!nextInFlow->mLines.empty()) {
return PullFrameFrom(aState, aLine, nextInFlow, PR_FALSE,
nextInFlow->mLines.begin(),
aDamageDeletedLines, aFrameResult);
}
nsLineList* overflowLines = nextInFlow->GetOverflowLines();
if (overflowLines) {
return PullFrameFrom(aState, aLine, nextInFlow, PR_TRUE,
overflowLines->begin(),
aDamageDeletedLines, aFrameResult);
}
@ -2558,9 +2665,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
nsresult
nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
nsLineBox* aLine,
nsLineList& aFromContainer,
nsBlockFrame* aFromContainer,
PRBool aFromOverflowLine,
nsLineList::iterator aFromLine,
PRBool aUpdateGeometricParent,
PRBool aDamageDeletedLines,
nsIFrame*& aFrameResult)
{
@ -2595,32 +2702,41 @@ nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
if (aDamageDeletedLines) {
Invalidate(fromLine->mBounds);
}
if (aFromLine.next() != aFromContainer.end())
nsLineList* fromLineList = aFromOverflowLine
? aFromContainer->RemoveOverflowLines()
: &aFromContainer->mLines;
if (aFromLine.next() != fromLineList->end())
aFromLine.next()->MarkPreviousMarginDirty();
Invalidate(fromLine->GetCombinedArea());
aFromContainer.erase(aFromLine);
fromLineList->erase(aFromLine);
// Note that aFromLine just got incremented, so don't use it again here!
aState.FreeLineBox(fromLine);
// Put any remaining overflow lines back.
if (aFromOverflowLine && !fromLineList->empty()) {
aFromContainer->SetOverflowLines(fromLineList);
}
}
// Change geometric parents
if (aUpdateGeometricParent) {
// Before we set the new parent frame get the current parent
nsIFrame* oldParentFrame = frame->GetParent();
frame->SetParent(this);
if (aFromContainer != this) {
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
NS_ASSERTION(oldParentFrame != this, "unexpected parent frame");
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, oldParentFrame, this);
NS_ASSERTION(frame->GetParent() == aFromContainer, "unexpected parent frame");
ReparentFrame(frame, aFromContainer, this);
// The frame is being pulled from a next-in-flow; therefore we
// need to add it to our sibling list.
frame->SetNextSibling(nsnull);
if (nsnull != aState.mPrevChild) {
aState.mPrevChild->SetNextSibling(frame);
}
// The frame might have (or contain) floats that need to be
// brought over too.
ReparentFloats(frame, aFromContainer, aFromOverflowLine);
}
// Stop pulling because we found a frame to pull
@ -3093,7 +3209,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
UndoSplitPlaceholders(aState, lastPlaceholder);
PushLines(aState, aLine.prev());
*aKeepReflowGoing = PR_FALSE;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE | NS_FRAME_REFLOW_NEXTINFLOW;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
}
else {
// Note: line-break-after a block is a nop
@ -3144,10 +3260,11 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
if (NS_FAILED(rv))
return rv;
nsIFrame* nextFrame = frame->GetNextInFlow();
// Push continuation to a new line, but only if we actually made one.
if (madeContinuation) {
frame = frame->GetNextSibling();
nsLineBox* line = aState.NewLineBox(frame, 1, PR_TRUE);
nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -3162,6 +3279,20 @@ 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();
}
}
*aKeepReflowGoing = PR_FALSE;
@ -3441,8 +3572,11 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
--aLine;
if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
// Push the line with the truncated float
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
// Don't push any lines if we just want to calculate the maximum width
if (!aUpdateMaximumWidth) {
// Push the line with the truncated float
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
}
}
break;
}
@ -4028,7 +4162,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// See if the line fit. If it doesn't we need to push it. Our first
// line will always fit.
if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)) {
// If we're just updating the maximum width then we don't care if it
// fits; we'll assume it does, so that the maximum width will get
// updated below. The line will be reflowed again and pushed then
// if necessary.
if ((mLines.front() != aLine) && (newY > aState.mBottomEdge)
&& !aUpdateMaximumWidth) {
// Push this line and all of it's children and anything else that
// follows to our next-in-flow
NS_ASSERTION((aState.mCurrentLine == aLine), "oops");
@ -4086,9 +4226,14 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// At least one float is truncated, so fix up any placeholders that got split and
// push the line. XXX It may be better to put the float on the next line, but this
// is not common enough to justify the complexity.
nsFrameList* overflowPlace = GetOverflowPlaceholders();
nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
// 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;
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
}
}
}
@ -4205,6 +4350,11 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState,
++line)
{
line->MarkDirty();
line->MarkPreviousMarginDirty();
line->mBounds.SetRect(0, 0, 0, 0);
if (line->HasFloats()) {
line->FreeFloats(aState.mFloatCacheFreeList);
}
}
}
@ -4222,7 +4372,7 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState,
// the invariant that the property is never set if the list is empty.
PRBool
nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
nsBlockFrame::DrainOverflowLines()
{
#ifdef DEBUG
VerifyOverflowSituation();
@ -4243,22 +4393,23 @@ nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
nsIFrame* lastFrame = nsnull;
nsIFrame* frame = overflowLines->front()->mFirstChild;
while (nsnull != frame) {
frame->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
// XXXldb Are float lists in sync with what floats are actually
// present in the lines, so their views can be reparented?
nsHTMLContainerFrame::ReparentFrameView(aPresContext, frame, prevBlock, this);
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()) {
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);
}
@ -4267,16 +4418,11 @@ nsBlockFrame::DrainOverflowLines(nsPresContext* aPresContext)
NS_ASSERTION(overflowLines->empty(), "splice should empty list");
delete overflowLines;
// Out-of-flow floats need to be reparented too.
nsFrameList* overflowOutOfFlows = prevBlock->RemoveOverflowOutOfFlows();
if (overflowOutOfFlows) {
for (nsIFrame* f = overflowOutOfFlows->FirstChild(); f;
f = f->GetNextSibling()) {
f->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevBlock, this);
ReparentFrame(f, prevBlock, this);
}
delete overflowOutOfFlows;
}
@ -4688,6 +4834,21 @@ nsBlockFrame::AddFrames(nsPresContext* aPresContext,
return NS_OK;
}
nsBlockFrame::line_iterator
nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
// Find which line contains the float
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;
}
NS_IMETHODIMP
nsBlockFrame::RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
@ -4711,15 +4872,8 @@ nsBlockFrame::RemoveFrame(nsPresContext* aPresContext,
aListName, aOldFrame);
}
else if (nsLayoutAtoms::floatList == aListName) {
// Find which line contains the float
line_iterator line = begin_lines(), line_end = end_lines();
for ( ; line != line_end; ++line) {
if (line->IsInline() && line->RemoveFloat(aOldFrame)) {
break;
}
}
mFloats.DestroyFrame(aPresContext, aOldFrame);
line_iterator line = RemoveFloat(aOldFrame);
line_iterator line_end = end_lines();
// Mark every line at and below the line where the float was dirty
// XXXldb This could be done more efficiently.
@ -4771,20 +4925,19 @@ nsBlockFrame::DoRemoveOutOfFlowFrame(nsPresContext* aPresContext,
block->mAbsoluteContainer.RemoveFrame(block, aPresContext,
*(aPresContext->PresShell()),
block->mAbsoluteContainer.GetChildListName(), aFrame);
aFrame->Destroy(aPresContext);
}
else {
block->mFloats.RemoveFrame(aFrame);
// This also destroys the frame.
block->RemoveFloat(aFrame);
}
// Destroy aFrame
aFrame->Destroy(aPresContext);
}
// This helps us iterate over the list of all normal + overflow lines
void
nsBlockFrame::NextOverAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines) {
(*aIterator)++;
nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines) {
if (*aIterator == *aEndIterator) {
if (!*aInOverflowLines) {
*aInOverflowLines = PR_TRUE;
@ -4825,7 +4978,10 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
line_end = mLines.end();
PRBool searchingOverflowList = PR_FALSE;
nsIFrame* prevSibling = nsnull;
for (; line != line_end; NextOverAllLines(&line, &line_end, &searchingOverflowList)) {
// Make sure we look in the overflow lines even if the normal line
// list is empty
TryAllLines(&line, &line_end, &searchingOverflowList);
while (line != line_end) {
nsIFrame* frame = line->mFirstChild;
PRInt32 n = line->GetChildCount();
while (--n >= 0) {
@ -4835,8 +4991,10 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
prevSibling = frame;
frame = frame->GetNextSibling();
}
++line;
TryAllLines(&line, &line_end, &searchingOverflowList);
}
found_frame:;
found_frame:;
if (line == line_end) {
NS_ERROR("can't find deleted frame in lines");
return NS_ERROR_FAILURE;
@ -4849,64 +5007,66 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
}
NS_ASSERTION(!prevSibling || prevSibling->GetNextSibling() == aDeletedFrame, "bad prevSibling");
while ((line != line_end) && (nsnull != aDeletedFrame)) {
while ((line != line_end) && (nsnull != aDeletedFrame)) {
NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
// See if the frame being deleted is the last one on the line
PRBool isLastFrameOnLine = PR_FALSE;
if (1 == line->GetChildCount()) {
isLastFrameOnLine = PR_TRUE;
}
else if (line->LastChild() == aDeletedFrame) {
isLastFrameOnLine = PR_TRUE;
}
// See if the frame being deleted is the last one on the line
PRBool isLastFrameOnLine = PR_FALSE;
if (1 == line->GetChildCount()) {
isLastFrameOnLine = PR_TRUE;
}
else if (line->LastChild() == aDeletedFrame) {
isLastFrameOnLine = PR_TRUE;
}
// Remove aDeletedFrame from the line
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
if (line->mFirstChild == aDeletedFrame) {
line->mFirstChild = nextFrame;
}
// Remove aDeletedFrame from the line
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
if (line->mFirstChild == aDeletedFrame) {
// We should be setting this to null if aDeletedFrame
// is the only frame on the line. HOWEVER in that case
// we will be removing the line anyway, see below.
line->mFirstChild = nextFrame;
}
// Hmm, this won't do anything if we're removing a frame in the first
// overflow line... Hopefully doesn't matter
--line;
if (line != line_end && !line->IsBlock()) {
// Since we just removed a frame that follows some inline
// frames, we need to reflow the previous line.
line->MarkDirty();
}
++line;
--line;
if (line != line_end && !line->IsBlock()) {
// Since we just removed a frame that follows some inline
// frames, we need to reflow the previous line.
line->MarkDirty();
}
++line;
// Take aDeletedFrame out of the sibling list. Note that
// prevSibling will only be nsnull when we are deleting the very
// Take aDeletedFrame out of the sibling list. Note that
// prevSibling will only be nsnull when we are deleting the very
// first frame in the main or overflow list.
if (prevSibling) {
prevSibling->SetNextSibling(nextFrame);
}
if (prevSibling) {
prevSibling->SetNextSibling(nextFrame);
}
// Update the child count of the line to be accurate
PRInt32 lineChildCount = line->GetChildCount();
lineChildCount--;
line->SetChildCount(lineChildCount);
// Update the child count of the line to be accurate
PRInt32 lineChildCount = line->GetChildCount();
lineChildCount--;
line->SetChildCount(lineChildCount);
// Destroy frame; capture its next-in-flow first in case we need
// to destroy that too.
nsIFrame* nextInFlow = aDeletedFrame->GetNextInFlow();
// Destroy frame; capture its next-in-flow first in case we need
// to destroy that too.
nsIFrame* nextInFlow = aDeletedFrame->GetNextInFlow();
#ifdef NOISY_REMOVE_FRAME
printf("DoRemoveFrame: line=%p frame=", line);
nsFrame::ListTag(stdout, aDeletedFrame);
printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
printf("DoRemoveFrame: line=%p frame=", line);
nsFrame::ListTag(stdout, aDeletedFrame);
printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
#endif
aDeletedFrame->Destroy(aPresContext);
aDeletedFrame = nextInFlow;
aDeletedFrame->Destroy(aPresContext);
aDeletedFrame = nextInFlow;
// If line is empty, remove it now.
// Don't bother removing empty lines in the overflow list, they'll get
// annihilated later
if (!searchingOverflowList && 0 == lineChildCount) {
nsLineBox *cur = line;
line = mLines.erase(line);
if (0 == lineChildCount) {
nsLineBox *cur = line;
if (!searchingOverflowList) {
line = mLines.erase(line);
// Invalidate the space taken up by the line.
// XXX We need to do this if we're removing a frame as a result of
// a call to RemoveFrame(), but we may not need to do this in all
@ -4917,44 +5077,51 @@ nsBlockFrame::DoRemoveFrame(nsPresContext* aPresContext,
this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height);
#endif
Invalidate(lineCombinedArea);
cur->Destroy(presShell);
// If we're removing a line, ReflowDirtyLines isn't going to
// know that it needs to slide lines unless something is marked
// dirty. So mark the previous margin of the next line dirty if
// there is one.
if (line != line_end)
line->MarkPreviousMarginDirty();
}
else {
// Make the line that just lost a frame dirty
line->MarkDirty();
// If we just removed the last frame on the line then we need
// to advance to the next line.
if (isLastFrameOnLine) {
NextOverAllLines(&line, &line_end, &searchingOverflowList);
// Detect the case when we've run off the end of the normal line
// list and we're starting the overflow line list
if (prevSibling && !prevSibling->GetNextSibling()) {
prevSibling = nsnull;
}
} else {
nsLineList* lineList = RemoveOverflowLines();
line = lineList->erase(line);
if (!lineList->empty()) {
SetOverflowLines(lineList);
}
}
cur->Destroy(presShell);
// See if we should keep looking in the current flow's line list.
if (nsnull != aDeletedFrame) {
if (aDeletedFrame != nextFrame) {
// The deceased frames continuation is not the next frame in
// the current flow's frame list. Therefore we know that the
// continuation is in a different parent. So break out of
// the loop so that we advance to the next parent.
NS_ASSERTION(aDeletedFrame->GetParent() != this, "strange continuation");
break;
}
// If we're removing a line, ReflowDirtyLines isn't going to
// know that it needs to slide lines unless something is marked
// dirty. So mark the previous margin of the next line dirty if
// there is one.
if (line != line_end) {
line->MarkPreviousMarginDirty();
}
} else {
// Make the line that just lost a frame dirty, and advance to
// the next line.
line->MarkDirty();
++line;
}
// If we just removed the last frame on the line then we need
// to advance to the next line.
if (isLastFrameOnLine) {
TryAllLines(&line, &line_end, &searchingOverflowList);
// Detect the case when we've run off the end of the normal line
// list and we're starting the overflow line list
if (prevSibling && !prevSibling->GetNextSibling()) {
prevSibling = nsnull;
}
}
// See if we should keep looking in the current flow's line list.
if (nsnull != aDeletedFrame) {
if (aDeletedFrame->GetParent() != this) {
// The deceased frames continuation is not a child of the
// current block. So break out of the loop so that we advance
// to the next parent.
break;
}
}
}
#ifdef DEBUG
VerifyLines(PR_TRUE);
#endif
@ -4996,13 +5163,6 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
nsFloatCache* aFloatCache,
nsReflowStatus& aReflowStatus)
{
// Delete the placeholder's next in flows, if any
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);
}
// Reflow the float.
nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
aReflowStatus = NS_FRAME_COMPLETE;
@ -5089,6 +5249,18 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
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.
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);
}
}
if (NS_SUCCEEDED(rv) && isAutoWidth) {
nscoord maxElementWidth = brc.GetMaxElementWidth();
if (maxElementWidth > availSpace.width) {

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

@ -286,9 +286,9 @@ protected:
const nsPoint &aPoint,
PRInt32 &aClosestLine);
void NextOverAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines);
void TryAllLines(nsLineList::iterator* aIterator,
nsLineList::iterator* aEndIterator,
PRBool* aInOverflowLines);
void SetFlags(PRUint32 aFlags) {
mState &= ~NS_BLOCK_FLAGS_MASK;
@ -310,12 +310,6 @@ protected:
void SlideLine(nsBlockReflowState& aState,
nsLineBox* aLine, nscoord aDY);
/** 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(nsPresContext* aPresContext);
virtual PRIntn GetSkipSides() const;
virtual void ComputeFinalSize(const nsHTMLReflowState& aReflowState,
@ -343,6 +337,18 @@ protected:
nsresult DoRemoveFrame(nsPresContext* aPresContext,
nsIFrame* aDeletedFrame);
/** 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();
/**
* Remove a float from our float list and also the float cache
* for the line its placeholder is on.
*/
line_iterator RemoveFloat(nsIFrame* aFloat);
// Remove a float, abs, rel positioned frame from the appropriate block's list
static void DoRemoveOutOfFlowFrame(nsPresContext* aPresContext,
nsIFrame* aFrame);
@ -493,16 +499,20 @@ protected:
nsIFrame*& aFrameResult);
nsresult PullFrameFrom(nsBlockReflowState& aState,
nsLineBox* aToLine,
nsLineList& aFromContainer,
nsLineBox* aLine,
nsBlockFrame* aFromContainer,
PRBool aFromOverflowLine,
nsLineList::iterator aFromLine,
PRBool aUpdateGeometricParent,
PRBool aDamageDeletedLines,
nsIFrame*& aFrameResult);
void PushLines(nsBlockReflowState& aState,
nsLineList::iterator aLineBefore);
void ReparentFloats(nsIFrame* aFirstFrame,
nsBlockFrame* aOldParent, PRBool aFromOverflow);
//----------------------------------------
//XXX
virtual void PaintChildren(nsPresContext* aPresContext,

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

@ -0,0 +1,876 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <roc@ocallahan.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsHTMLContainerFrame.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsISupports.h"
#include "nsIAtom.h"
#include "nsPresContext.h"
#include "nsHTMLParts.h"
#include "nsLayoutAtoms.h"
#include "nsStyleConsts.h"
#include "nsCOMPtr.h"
#include "nsReflowPath.h"
class nsColumnSetFrame : public nsHTMLContainerFrame {
public:
nsColumnSetFrame();
NS_IMETHOD SetInitialChildList(nsPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList);
NS_IMETHOD Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD AppendFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aFrameList);
NS_IMETHOD InsertFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList);
NS_IMETHOD RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aOldFrame);
virtual nsIFrame* GetContentInsertionFrame() {
return GetFirstChild(nsnull)->GetContentInsertionFrame();
}
virtual nsIAtom* GetType() const;
#ifdef DEBUG
NS_IMETHOD GetFrameName(nsAString& aResult) const {
return MakeFrameName(NS_LITERAL_STRING("ColumnSet"), aResult);
}
#endif
protected:
nscoord mLastBalanceHeight;
nsReflowStatus mLastFrameStatus;
virtual PRIntn GetSkipSides() const;
/**
* These are the parameters that control the layout of columns.
*/
struct ReflowConfig {
PRInt32 mBalanceColCount;
nscoord mColWidth;
nscoord mExpectedWidthLeftOver;
nscoord mColGap;
nscoord mColMaxHeight;
};
/**
* Similar to nsBlockFrame::DrainOverflowLines. Locate any columns not
* handled by our prev-in-flow, and any columns sitting on our own
* overflow list, and put them in our primary child list for reflowing.
*/
void DrainOverflowColumns();
/**
* The basic reflow strategy is to call this function repeatedly to
* obtain specific parameters that determine the layout of the
* columns. This function will compute those parameters from the CSS
* style. This function will also be responsible for implementing
* the state machine that controls column balancing.
*/
ReflowConfig ChooseColumnStrategy(const nsHTMLReflowState& aReflowState);
/**
* Reflow column children. Returns PR_TRUE iff the content that was reflowed
* fit into the mColMaxHeight.
*/
PRBool ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowReason aReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aLastColumnUnbounded);
};
/**
* Tracking issues:
*
* XXX cursor movement around the top and bottom of colums seems to make the editor
* lose the caret.
*
* XXX should we support CSS columns applied to table elements?
*/
nsresult
NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame;
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
// set the state flags (if any are provided)
it->AddStateBits(aStateFlags);
*aNewFrame = it;
return NS_OK;
}
nsColumnSetFrame::nsColumnSetFrame()
: nsHTMLContainerFrame(), mLastBalanceHeight(NS_INTRINSICSIZE),
mLastFrameStatus(NS_FRAME_COMPLETE)
{
}
nsIAtom*
nsColumnSetFrame::GetType() const
{
return nsLayoutAtoms::columnSetFrame;
}
NS_IMETHODIMP
nsColumnSetFrame::SetInitialChildList(nsPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList)
{
NS_ASSERTION(!aListName, "Only default child list supported");
NS_ASSERTION(aChildList && !aChildList->GetNextSibling(),
"initial child list must have exactly one child");
// Queue up the frames for the content frame
return nsHTMLContainerFrame::SetInitialChildList(aPresContext, nsnull, aChildList);
}
static nscoord GetAvailableContentWidth(const nsHTMLReflowState& aReflowState) {
if (aReflowState.availableWidth == NS_INTRINSICSIZE) {
return NS_INTRINSICSIZE;
}
nscoord borderPaddingWidth =
aReflowState.mComputedBorderPadding.left +
aReflowState.mComputedBorderPadding.right;
return PR_MAX(0, aReflowState.availableWidth - borderPaddingWidth);
}
static nscoord GetAvailableContentHeight(const nsHTMLReflowState& aReflowState) {
if (aReflowState.availableHeight == NS_INTRINSICSIZE) {
return NS_INTRINSICSIZE;
}
nscoord borderPaddingHeight =
aReflowState.mComputedBorderPadding.top +
aReflowState.mComputedBorderPadding.bottom;
return PR_MAX(0, aReflowState.availableHeight - borderPaddingHeight);
}
nsColumnSetFrame::ReflowConfig
nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState)
{
const nsStyleColumn* colStyle = GetStyleColumn();
nscoord availContentWidth = GetAvailableContentWidth(aReflowState);
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
availContentWidth = aReflowState.mComputedWidth;
}
nscoord colHeight = GetAvailableContentHeight(aReflowState);
if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) {
colHeight = aReflowState.mComputedHeight;
}
nscoord colGap = 0;
switch (colStyle->mColumnGap.GetUnit()) {
case eStyleUnit_Coord:
colGap = colStyle->mColumnGap.GetCoordValue();
break;
case eStyleUnit_Percent:
if (availContentWidth != NS_INTRINSICSIZE) {
colGap = NSToCoordRound(colStyle->mColumnGap.GetPercentValue()*availContentWidth);
}
break;
default:
NS_NOTREACHED("Unknown gap type");
break;
}
PRInt32 numColumns = colStyle->mColumnCount;
nscoord colWidth = NS_INTRINSICSIZE;
if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
colWidth = colStyle->mColumnWidth.GetCoordValue();
// Reduce column count if necesary to make columns fit in the
// available width. Compute max number of columns that fit in
// availContentWidth, satisfying colGap*(maxColumns - 1) +
// colWidth*maxColumns <= availContentWidth
if (availContentWidth != NS_INTRINSICSIZE && colWidth + colGap > 0
&& numColumns > 0) {
// This expression uses truncated rounding, which is what we
// want
PRInt32 maxColumns = (availContentWidth + colGap)/(colGap + colWidth);
numColumns = PR_MAX(1, PR_MIN(numColumns, maxColumns));
}
} else if (numColumns > 0 && availContentWidth != NS_INTRINSICSIZE) {
nscoord widthMinusGaps = availContentWidth - colGap*(numColumns - 1);
colWidth = widthMinusGaps/numColumns;
}
// Take care of the situation where there's only one column but it's
// still too wide
colWidth = PR_MAX(1, PR_MIN(colWidth, availContentWidth));
nscoord expectedWidthLeftOver = 0;
if (colWidth != NS_INTRINSICSIZE && availContentWidth != NS_INTRINSICSIZE) {
// distribute leftover space
// First, determine how many columns will be showing if the column
// count is auto
if (numColumns <= 0) {
// choose so that colGap*(nominalColumnCount - 1) +
// colWidth*nominalColumnCount is nearly availContentWidth
// make sure to round down
numColumns = (availContentWidth + colGap)/(colGap + colWidth);
}
// Compute extra space and divide it among the columns
nscoord extraSpace = availContentWidth - (colWidth*numColumns + colGap*(numColumns - 1));
nscoord extraToColumns = extraSpace/numColumns;
colWidth += extraToColumns;
expectedWidthLeftOver = extraSpace - (extraToColumns*numColumns);
}
// NOTE that the non-balancing behavior for non-auto computed height
// is not in the CSS3 columns draft as of 18 January 2001
if (aReflowState.mComputedHeight == NS_INTRINSICSIZE) {
// Balancing!
if (numColumns <= 0) {
// Hmm, auto column count, column width or available width is unknown,
// and balancing is required. Let's just use one column then.
numColumns = 1;
}
colHeight = PR_MIN(mLastBalanceHeight, GetAvailableContentHeight(aReflowState));
} else {
// No balancing, so don't limit the column count
numColumns = PR_INT32_MAX;
}
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colWidth=%d, expectedWidthLeftOver=%d, colHeight=%d, colGap=%d\n",
numColumns, colWidth, expectedWidthLeftOver, colHeight, colGap);
#endif
ReflowConfig config = { numColumns, colWidth, expectedWidthLeftOver, colGap, colHeight };
return config;
}
// XXX copied from nsBlockFrame, should this be moved to nsContainerFrame?
static void
PlaceFrameView(nsIFrame* aFrame)
{
if (aFrame->HasView())
nsContainerFrame::PositionFrameView(aFrame->GetPresContext(), aFrame);
else
nsContainerFrame::PositionChildViews(aFrame->GetPresContext(), aFrame);
}
static void MoveChildTo(nsIFrame* aParent, nsIFrame* aChild, nsPoint aOrigin) {
if (aChild->GetPosition() == aOrigin) {
return;
}
nsRect* overflowArea = aChild->GetOverflowAreaProperty(PR_FALSE);
nsRect r = overflowArea ? *overflowArea : nsRect(nsPoint(0, 0), aChild->GetSize());
r += aChild->GetPosition();
aParent->Invalidate(r);
r -= aChild->GetPosition();
aChild->SetPosition(aOrigin);
r += aOrigin;
aParent->Invalidate(r);
PlaceFrameView(aChild);
}
PRBool
nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowReason aKidReason,
nsReflowStatus& aStatus,
const ReflowConfig& aConfig,
PRBool aUnboundedLastColumn) {
PRBool allFit = PR_TRUE;
PRBool RTL = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
PRBool shrinkingHeightOnly = aKidReason == eReflowReason_Resize &&
mLastBalanceHeight > aConfig.mColMaxHeight;
#ifdef DEBUG_roc
printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n",
mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount,
aConfig.mColWidth, aConfig.mColGap);
#endif
DrainOverflowColumns();
if (mLastBalanceHeight != aConfig.mColMaxHeight) {
mLastBalanceHeight = aConfig.mColMaxHeight;
// XXX Seems like this could fire if incremental reflow pushed the column set
// down so we reflow incrementally with a different available height.
// We need a way to do an incremental reflow and be sure availableHeight
// changes are taken account of! Right now I think block frames with absolute
// children might exit early.
NS_ASSERTION(aKidReason != eReflowReason_Incremental,
"incremental reflow should not have changed the balance height");
}
// get our border and padding
const nsMargin &borderPadding = aReflowState.mComputedBorderPadding;
nsRect contentRect(0, 0, 0, 0);
aDesiredSize.mMaxElementWidth = 0;
nsRect overflowRect(0, 0, 0, 0);
nsIFrame* child = mFrames.FirstChild();
nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top);
// For RTL, figure out where the last column's left edge should be. Since the
// columns might not fill the frame exactly, we need to account for the
// slop. Otherwise we'll waste time moving the columns by some tiny
// amount unnecessarily.
nscoord targetX = borderPadding.left;
if (RTL) {
nscoord availWidth = aReflowState.availableWidth;
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
availWidth = aReflowState.mComputedWidth;
}
if (availWidth != NS_INTRINSICSIZE) {
childOrigin.x += availWidth - aConfig.mColWidth;
targetX += aConfig.mExpectedWidthLeftOver;
#ifdef DEBUG_roc
printf("*** childOrigin.x = %d\n", childOrigin.x);
#endif
}
}
int columnCount = 0;
PRBool reflowNext = PR_FALSE;
while (child) {
// Try to skip reflowing the child. We can't skip if the child is dirty. We also can't
// skip if the next column is dirty, because the next column's first line(s)
// might be pullable back to this column.
PRBool skipIncremental = aKidReason == eReflowReason_Incremental
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)
&& (!child->GetNextSibling()
|| !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY))
&& !aDesiredSize.mComputeMEW;
PRBool skipResizeHeightShrink = shrinkingHeightOnly
&& child->GetSize().height <= aConfig.mColMaxHeight;
if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) {
// This child does not need to be reflowed, but we may need to move it
MoveChildTo(this, child, childOrigin);
// If this is the last frame then make sure we get the right status
if (child->GetNextSibling()) {
aStatus = NS_FRAME_NOT_COMPLETE;
} else {
aStatus = mLastFrameStatus;
}
#ifdef DEBUG_roc
printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n",
columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus);
#endif
} else {
nsSize availSize(aConfig.mColWidth, aConfig.mColMaxHeight);
if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
availSize.height = GetAvailableContentHeight(aReflowState);
}
nsReflowReason tmpReason = aKidReason;
if (reflowNext && aKidReason == eReflowReason_Incremental
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)) {
// If this frame was not being incrementally reflowed but was
// just reflowed because the previous frame wants us to
// reflow, then force this child to reflow its dirty lines!
// XXX what we should really do here is add the child block to
// the incremental reflow path! Currently I think if there's an
// incremental reflow targeted only at the absolute frames of a
// column, then the column will be dirty BUT reflowing it will
// not reflow any lines affected by the prev-in-flow!
tmpReason = eReflowReason_Dirty;
}
nsHTMLReflowState kidReflowState(GetPresContext(), aReflowState, child,
availSize, tmpReason);
#ifdef DEBUG_roc
printf("*** Reflowing child #%d %p: reason = %d, availHeight=%d\n",
columnCount, (void*)child, tmpReason, availSize.height);
#endif
// Note if the column's next in flow is not being changed by this incremental reflow.
// This may allow the current column to avoid trying to pull lines from the next column.
if (child->GetNextSibling() && aKidReason == eReflowReason_Incremental &&
!(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) {
kidReflowState.mFlags.mNextInFlowUntouched = PR_TRUE;
}
nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags);
// XXX it would be cool to consult the space manager for the
// previous block to figure out the region of floats from the
// previous column that extend into this column, and subtract
// that region from the new space manager. So you could stick a
// really big float in the first column and text in following
// columns would flow around it.
// Reflow the frame
ReflowChild(child, GetPresContext(), kidDesiredSize, kidReflowState,
childOrigin.x + kidReflowState.mComputedMargin.left,
childOrigin.y + kidReflowState.mComputedMargin.top,
0, aStatus);
if (kidDesiredSize.height > aConfig.mColMaxHeight) {
allFit = PR_FALSE;
}
reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
#ifdef DEBUG_roc
printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d\n",
columnCount, (void*)child, aStatus, kidDesiredSize.width, kidDesiredSize.height);
#endif
NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
FinishReflowChild(child, GetPresContext(), &kidReflowState,
kidDesiredSize, childOrigin.x, childOrigin.y, 0);
if (aDesiredSize.mComputeMEW) {
aDesiredSize.mMaxElementWidth = PR_MAX(aDesiredSize.mMaxElementWidth,
kidDesiredSize.mMaxElementWidth);
}
}
contentRect.UnionRect(contentRect, child->GetRect());
ConsiderChildOverflow(GetPresContext(), overflowRect, child);
// Build a continuation column if necessary
nsIFrame* kidNextInFlow = child->GetNextInFlow();
if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
break;
} else {
++columnCount;
// Make sure that the column has a next-in-flow. If not, we must
// create one to hold the overflowing stuff, even if we're just
// going to put it on our overflow list and let *our*
// next in flow handle it.
if (!kidNextInFlow) {
NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
"We have to create a continuation, but the block doesn't want us to reflow it?");
// We need to create a continuing column
nsIFrame* continuation;
nsresult rv = CreateNextInFlow(GetPresContext(), this, child, continuation);
if (NS_FAILED(rv)) {
NS_NOTREACHED("Couldn't create continuation");
break;
}
continuation->AddStateBits(NS_BLOCK_SPACE_MGR);
// Do an initial reflow if we're going to reflow this thing.
aKidReason = eReflowReason_Initial;
}
if (columnCount >= aConfig.mBalanceColCount) {
// No more columns allowed here. Stop.
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
// Move any of our leftover columns to our overflow list. Our
// next-in-flow will eventually pick them up.
nsIFrame* continuationColumns = child->GetNextSibling();
if (continuationColumns) {
SetOverflowFrames(GetPresContext(), continuationColumns);
child->SetNextSibling(nsnull);
}
break;
}
}
// Advance to the next column
child = child->GetNextSibling();
if (child) {
if (!RTL) {
childOrigin.x += aConfig.mColWidth + aConfig.mColGap;
} else {
childOrigin.x -= aConfig.mColWidth + aConfig.mColGap;
}
#ifdef DEBUG_roc
printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x);
#endif
}
}
// If we're doing RTL, we need to make sure our last column is at the left-hand side of the frame.
if (RTL && childOrigin.x != targetX) {
overflowRect = nsRect(0, 0, 0, 0);
contentRect = nsRect(0, 0, 0, 0);
PRInt32 deltaX = targetX - childOrigin.x;
#ifdef DEBUG_roc
printf("*** CHILDORIGIN.x = %d, targetX = %d, DELTAX = %d\n", childOrigin.x, targetX, deltaX);
#endif
for (child = mFrames.FirstChild(); child; child = child->GetNextSibling()) {
MoveChildTo(this, child, child->GetPosition() + nsPoint(deltaX, 0));
ConsiderChildOverflow(GetPresContext(), overflowRect, child);
contentRect.UnionRect(contentRect, child->GetRect());
}
}
mLastFrameStatus = aStatus;
// contentRect included the borderPadding.left,borderPadding.top of the child rects
contentRect -= nsPoint(borderPadding.left, borderPadding.top);
nsSize contentSize = nsSize(contentRect.XMost(), contentRect.YMost());
// Apply computed and min/max values
if (aReflowState.mComputedHeight != NS_INTRINSICSIZE) {
contentSize.height = aReflowState.mComputedHeight;
} else {
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) {
contentSize.height = PR_MIN(aReflowState.mComputedMaxHeight, contentSize.height);
}
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
contentSize.height = PR_MAX(aReflowState.mComputedMinHeight, contentSize.height);
}
}
if (aReflowState.mComputedWidth != NS_INTRINSICSIZE) {
contentSize.width = aReflowState.mComputedWidth;
} else {
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
contentSize.width = PR_MIN(aReflowState.mComputedMaxWidth, contentSize.width);
}
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
contentSize.width = PR_MAX(aReflowState.mComputedMinWidth, contentSize.width);
}
}
aDesiredSize.height = borderPadding.top + contentSize.height +
borderPadding.bottom;
aDesiredSize.width = contentSize.width + borderPadding.left + borderPadding.right;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
aDesiredSize.mMaximumWidth = aDesiredSize.width;
if (aDesiredSize.mComputeMEW) {
// add in padding.
aDesiredSize.mMaxElementWidth += borderPadding.left + borderPadding.right;
}
overflowRect.UnionRect(overflowRect, nsRect(0, 0, aDesiredSize.width, aDesiredSize.height));
aDesiredSize.mOverflowArea = overflowRect;
#ifdef DEBUG_roc
printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_COMPLETE(aStatus)
&& !NS_FRAME_IS_TRUNCATED(aStatus));
#endif
return allFit && NS_FRAME_IS_COMPLETE(aStatus)
&& !NS_FRAME_IS_TRUNCATED(aStatus);
}
static nscoord ComputeSumOfChildHeights(nsIFrame* aFrame) {
nscoord totalHeight = 0;
for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
// individual columns don't have borders or padding so this is a
// reasonable way to get their content height
totalHeight += f->GetSize().height;
}
return totalHeight;
}
void
nsColumnSetFrame::DrainOverflowColumns()
{
// First grab the prev-in-flows overflows and reparent them to this
// frame.
nsColumnSetFrame* prev = NS_STATIC_CAST(nsColumnSetFrame*, mPrevInFlow);
if (prev) {
nsIFrame* overflows = prev->GetOverflowFrames(GetPresContext(), PR_TRUE);
if (overflows) {
// Make all the frames on the overflow list mine
nsIFrame* lastFrame = nsnull;
for (nsIFrame* f = overflows; f; f = f->GetNextSibling()) {
f->SetParent(this);
// When pushing and pulling frames we need to check for whether any
// views need to be reparented
nsHTMLContainerFrame::ReparentFrameView(GetPresContext(), f, prev, this);
// Get the next frame
lastFrame = f;
}
NS_ASSERTION(lastFrame, "overflow list was created with no frames");
lastFrame->SetNextSibling(mFrames.FirstChild());
mFrames.SetFrames(overflows);
}
}
// Now pull back our own overflows and append them to our children.
// We don't need to reparent them since we're already their parent.
nsIFrame* overflows = GetOverflowFrames(GetPresContext(), PR_TRUE);
if (overflows) {
mFrames.AppendFrames(this, overflows);
}
}
NS_IMETHODIMP
nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame", aReflowState.reason);
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
// Initialize OUT parameter
aStatus = NS_FRAME_COMPLETE;
//------------ Handle Incremental Reflow -----------------
nsReflowReason kidReason = aReflowState.reason;
if ( aReflowState.reason == eReflowReason_Incremental ) {
nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
// Dirty any frames on the incremental reflow path
nsReflowPath *path = aReflowState.path;
nsReflowPath::iterator iter = path->FirstChild();
nsReflowPath::iterator end = path->EndChildren();
for ( ; iter != end; ++iter) {
(*iter)->AddStateBits(NS_FRAME_IS_DIRTY);
}
// See if it's targeted at us
if (command) {
nsReflowType reflowType;
command->GetType(reflowType);
switch (reflowType) {
case eReflowType_StyleChanged:
kidReason = eReflowReason_StyleChange;
break;
// if its a dirty type then reflow us with a dirty reflow
case eReflowType_ReflowDirty:
kidReason = eReflowReason_Dirty;
break;
default:
NS_ERROR("Unexpected Reflow Type");
}
}
}
ReflowConfig config = ChooseColumnStrategy(aReflowState);
PRBool isBalancing = config.mBalanceColCount < PR_INT32_MAX;
// If balancing, then we allow the last column to grow to unbounded
// height during the first reflow. This gives us a way to estimate
// what the average column height should be, because we can measure
// the heights of all the columns and sum them up. But don't do this
// if we have a next in flow because we don't want to suck all its
// content back here and then have to push it out again!
nsIFrame* nextInFlow = GetNextInFlow();
PRBool unboundedLastColumn = isBalancing && nextInFlow;
PRBool feasible = ReflowChildren(aDesiredSize, aReflowState, kidReason,
aStatus, config, unboundedLastColumn);
if (isBalancing) {
nscoord availableContentHeight = GetAvailableContentHeight(aReflowState);
// Termination of the algorithm below is guaranteed because
// knownFeasibleHeight - knownInfeasibleHeight decreases in every
// iteration.
nscoord knownFeasibleHeight = NS_INTRINSICSIZE;
nscoord knownInfeasibleHeight = 0;
while (1) {
nscoord maxHeight = 0;
for (nsIFrame* f = mFrames.FirstChild(); f; f = f->GetNextSibling()) {
maxHeight = PR_MAX(maxHeight, f->GetSize().height);
}
// Record what we learned from the last reflow
if (feasible) {
// maxHeight is feasible (and always maxHeight <=
// mLastBalanceHeight)
knownFeasibleHeight = PR_MIN(knownFeasibleHeight, maxHeight);
// Furthermore, no height less than the height of the last
// column can ever be feasible.
if (mFrames.GetLength() == config.mBalanceColCount) {
knownInfeasibleHeight = PR_MAX(knownInfeasibleHeight,
mFrames.LastChild()->GetSize().height - 1);
}
} else {
knownInfeasibleHeight = PR_MAX(knownInfeasibleHeight, mLastBalanceHeight);
if (unboundedLastColumn) {
// The last column is unbounded, so all content got reflowed, so the
// maxHeight is feasible.
knownFeasibleHeight = PR_MIN(knownFeasibleHeight, maxHeight);
}
}
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
knownInfeasibleHeight, knownFeasibleHeight);
#endif
if (knownInfeasibleHeight >= knownFeasibleHeight - 1) {
// knownFeasibleHeight is where we want to be
break;
}
if (knownInfeasibleHeight >= availableContentHeight) {
break;
}
nscoord nextGuess = (knownFeasibleHeight + knownInfeasibleHeight)/2;
// The constant of 600 twips is arbitrary. It's about two line-heights.
if (knownFeasibleHeight - nextGuess < 600) {
// We're close to our target, so just try shrinking just the
// minimum amount that will cause one of our columns to break
// differently.
nextGuess = knownFeasibleHeight - 1;
} else if (unboundedLastColumn) {
// Make a guess by dividing that into N columns. Add some slop
// to try to make it on the feasible side. The constant of
// 600 twips is arbitrary. It's about two line-heights.
nextGuess = ComputeSumOfChildHeights(this)/config.mBalanceColCount + 600;
// Sanitize it
nextGuess = PR_MIN(PR_MAX(nextGuess, knownInfeasibleHeight + 1),
knownFeasibleHeight - 1);
} else if (knownFeasibleHeight == NS_INTRINSICSIZE) {
// This can happen when we had a next-in-flow so we didn't
// want to do an unbounded height measuring step. Let's just increase
// from the infeasible height by some reasonable amount.
nextGuess = knownInfeasibleHeight*2 + 600;
}
// Don't bother guessing more than our height constraint.
nextGuess = PR_MIN(availableContentHeight, nextGuess);
#ifdef DEBUG_roc
printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
#endif
config.mColMaxHeight = nextGuess;
unboundedLastColumn = PR_FALSE;
feasible = ReflowChildren(aDesiredSize, aReflowState,
eReflowReason_Resize, aStatus, config, PR_FALSE);
}
if (!feasible) {
// We may need to reflow one more time at the feasible height to
// get a valid layout.
PRBool skip = PR_FALSE;
if (knownInfeasibleHeight >= availableContentHeight) {
config.mColMaxHeight = availableContentHeight;
if (mLastBalanceHeight == availableContentHeight) {
skip = PR_TRUE;
}
} else {
config.mColMaxHeight = knownFeasibleHeight;
}
if (!skip) {
ReflowChildren(aDesiredSize, aReflowState,
eReflowReason_Resize, aStatus, config, PR_FALSE);
}
}
}
CheckInvalidateSizeChange(GetPresContext(), aDesiredSize, aReflowState);
FinishAndStoreOverflow(&aDesiredSize);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
return NS_OK;
}
PRIntn
nsColumnSetFrame::GetSkipSides() const
{
return 0;
}
NS_IMETHODIMP
nsColumnSetFrame::AppendFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aFrameList)
{
NS_NOTREACHED("AppendFrames not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsColumnSetFrame::InsertFrames(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
NS_NOTREACHED("InsertFrames not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsColumnSetFrame::RemoveFrame(nsPresContext* aPresContext,
nsIPresShell& aPresShell,
nsIAtom* aListName,
nsIFrame* aOldFrame)
{
NS_NOTREACHED("RemoveFrame not supported");
return NS_ERROR_NOT_IMPLEMENTED;
}

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

@ -177,6 +177,10 @@ inline nsresult
NS_NewWBRFrame(nsIPresShell* aPresShell, nsIFrame** aResult) {
return NS_NewEmptyFrame(aPresShell, aResult);
}
nsresult
NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags );
nsresult
NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsIFrame** aResult);
nsresult

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

@ -110,7 +110,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mLineLayout = nsnull;
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mUnused = 0;
mFlags.mNextInFlowUntouched = PR_FALSE;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -142,7 +142,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mLineLayout = nsnull;
mFlags.mSpecialHeightReflow = PR_FALSE;
mFlags.mIsTopOfPage = PR_FALSE;
mFlags.mUnused = 0;
mFlags.mNextInFlowUntouched = PR_FALSE;
mPercentHeightObserver = nsnull;
mPercentHeightReflowInitiator = nsnull;
Init(aPresContext);
@ -152,6 +152,13 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
#endif // IBMBIDI
}
static PRBool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
{
nsIFrame* frameNext = aFrame->GetNextInFlow();
nsIFrame* parentNext = aParent->GetNextInFlow();
return frameNext && parentNext && frameNext->GetParent() == parentNext;
}
// Initialize a reflow state for a child frames reflow. Some state
// is copied from the parent reflow state; the remaining state is
// computed.
@ -184,6 +191,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -229,6 +238,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;
@ -274,6 +285,8 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
mSpaceManager = aParentReflowState.mSpaceManager;
mLineLayout = aParentReflowState.mLineLayout;
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
? aParentReflowState.mPercentHeightObserver : nsnull;

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

@ -147,6 +147,10 @@
-moz-counter-reset: inherit;
}
*|*::-moz-column-content {
/* the column boxes inside a column-flowed block */
}
*|*::-moz-page, *|*::-moz-page-sequence {
display: block !important;
background: transparent;

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

@ -6289,7 +6289,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
aState.mFloatedItems.containingBlock, adjParentFrame,
aStyleContext, newFrame,
aStyleContext, &newFrame,
aDisplay->mPosition == NS_STYLE_POSITION_RELATIVE);
}
// See if it's relatively positioned
@ -6307,7 +6307,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
NS_NewRelativeItemWrapperFrame(aPresShell, &newFrame);
// XXXbz should we be passing in a non-null aContentParentFrame?
ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
adjParentFrame, nsnull, aStyleContext, newFrame, PR_TRUE);
adjParentFrame, nsnull, aStyleContext, &newFrame, PR_TRUE);
} else {
// Create a positioned inline frame
NS_NewPositionedInlineFrame(aPresShell, &newFrame);
@ -6332,7 +6332,7 @@ nsCSSFrameConstructor::ConstructFrameByDisplayType(nsIPresShell* aPre
aState.mPseudoFrames.Reset(&savePseudo);
// XXXbz should we be passing in a non-null aContentParentFrame?
rv = ConstructBlock(aPresShell, aPresContext, aState, aDisplay, aContent,
adjParentFrame, nsnull, aStyleContext, newFrame,
adjParentFrame, nsnull, aStyleContext, &newFrame,
PR_FALSE);
if (!aState.mPseudoFrames.IsEmpty()) { // process pending pseudo frames
ProcessPseudoFrames(aPresContext, aState.mPseudoFrames, aFrameItems);
@ -7196,7 +7196,7 @@ nsCSSFrameConstructor::ConstructSVGFrame(nsIPresShell* aPresShell,
const nsStyleDisplay* disp = aStyleContext->GetStyleDisplay();
rv = ConstructBlock(aPresShell, aPresContext, aState, disp, aContent,
geometricParent, aParentFrame, aStyleContext,
newFrame, PR_TRUE);
&newFrame, PR_TRUE);
} else {
InitAndRestoreFrame(aPresContext, aState, aContent,
geometricParent, aStyleContext, nsnull, newFrame);
@ -10916,6 +10916,15 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
nsHTMLContainerFrame::CreateViewForFrame(newFrame, nsnull, PR_FALSE);
}
} else if (nsLayoutAtoms::columnSetFrame == frameType) {
rv = NS_NewColumnSetFrame(shell, &newFrame, 0);
if (NS_SUCCEEDED(rv)) {
newFrame->Init(aPresContext, content, aParentFrame, styleContext,
aFrame);
// XXXbz should we be passing in a non-null aContentParentFrame?
nsHTMLContainerFrame::CreateViewForFrame(newFrame, nsnull, PR_FALSE);
}
} else if (nsLayoutAtoms::positionedInlineFrame == frameType) {
rv = NS_NewPositionedInlineFrame(shell, &newFrame);
if (NS_SUCCEEDED(rv)) {
@ -12697,28 +12706,62 @@ nsCSSFrameConstructor::ConstructBlock(nsIPresShell* aPresShell,
nsIFrame* aParentFrame,
nsIFrame* aContentParentFrame,
nsStyleContext* aStyleContext,
nsIFrame* aNewFrame,
nsIFrame** aNewFrame,
PRBool aRelPos)
{
InitAndRestoreFrame(aPresContext, aState, aContent,
aParentFrame, aStyleContext, nsnull, aNewFrame);
// Create column wrapper if necessary
nsIFrame* blockFrame = *aNewFrame;
nsIFrame* parent = aParentFrame;
nsIFrame* contentParent = aContentParentFrame;
nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
const nsStyleColumn* columns = aStyleContext->GetStyleColumn();
if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO
|| columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
nsIFrame* columnSetFrame = nsnull;
NS_NewColumnSetFrame(aPresShell, &columnSetFrame, 0);
if (!columnSetFrame) {
return NS_ERROR_OUT_OF_MEMORY;
}
InitAndRestoreFrame(aPresContext, aState, aContent,
aParentFrame, aStyleContext, nsnull, columnSetFrame);
// See if we need to create a view, e.g. the frame is absolutely positioned
nsHTMLContainerFrame::CreateViewForFrame(columnSetFrame, aContentParentFrame,
PR_FALSE);
blockStyle = aPresContext->StyleSet()->
ResolvePseudoStyleFor(aContent, nsCSSAnonBoxes::columnContent,
aStyleContext);
contentParent = columnSetFrame;
parent = columnSetFrame;
*aNewFrame = columnSetFrame;
columnSetFrame->SetInitialChildList(aPresContext, nsnull, blockFrame);
blockFrame->AddStateBits(NS_BLOCK_SPACE_MGR);
}
InitAndRestoreFrame(aPresContext, aState, aContent,
parent, blockStyle, nsnull, blockFrame);
// See if we need to create a view, e.g. the frame is absolutely positioned
nsHTMLContainerFrame::CreateViewForFrame(aNewFrame, aContentParentFrame,
PR_FALSE);
nsHTMLContainerFrame::CreateViewForFrame(blockFrame, contentParent, PR_FALSE);
// If we're the first block to be created (e.g., because we're
// contained inside a XUL document), then make sure that we've got a
// space manager so we can handle floats...
if (! aState.mFloatedItems.containingBlock) {
aNewFrame->AddStateBits(NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT);
blockFrame->AddStateBits(NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT);
}
// ...and that we're the absolute containing block.
// This makes absolute children of a block in columns be positioned
// relative to the container, not flowed into columns. I don't know if this
// is right...
nsFrameConstructorSaveState absoluteSaveState;
if (aRelPos || !aState.mAbsoluteItems.containingBlock) {
// NS_ASSERTION(aRelPos, "should have made area frame for this");
aState.PushAbsoluteContainingBlock(aPresContext, aNewFrame, absoluteSaveState);
aState.PushAbsoluteContainingBlock(aPresContext, *aNewFrame, absoluteSaveState);
}
// See if the block has first-letter style applied to it...
@ -12729,28 +12772,28 @@ nsCSSFrameConstructor::ConstructBlock(nsIPresShell* aPresShell,
// Process the child content
nsFrameItems childItems;
nsFrameConstructorSaveState floatSaveState;
aState.PushFloatContainingBlock(aNewFrame, floatSaveState,
aState.PushFloatContainingBlock(blockFrame, floatSaveState,
haveFirstLetterStyle,
haveFirstLineStyle);
nsresult rv = ProcessChildren(aPresShell, aPresContext, aState, aContent,
aNewFrame, PR_TRUE, childItems, PR_TRUE);
blockFrame, PR_TRUE, childItems, PR_TRUE);
CreateAnonymousFrames(aPresShell, aPresContext, aContent->Tag(), aState,
aContent, aNewFrame, PR_FALSE, childItems);
aContent, blockFrame, PR_FALSE, childItems);
// Set the frame's initial child list
aNewFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList);
blockFrame->SetInitialChildList(aPresContext, nsnull, childItems.childList);
// Set the frame's float list if there were any floated children
if (aState.mFloatedItems.childList) {
aNewFrame->SetInitialChildList(aPresContext,
nsLayoutAtoms::floatList,
aState.mFloatedItems.childList);
blockFrame->SetInitialChildList(aPresContext,
nsLayoutAtoms::floatList,
aState.mFloatedItems.childList);
}
// and the same for absolutely positioned children.
if (aRelPos && aState.mAbsoluteItems.childList) {
aNewFrame->SetInitialChildList(aPresContext, nsLayoutAtoms::absoluteList,
aState.mAbsoluteItems.childList);
blockFrame->SetInitialChildList(aPresContext, nsLayoutAtoms::absoluteList,
aState.mAbsoluteItems.childList);
}
return rv;

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

@ -849,7 +849,7 @@ private:
nsIFrame* aParentFrame,
nsIFrame* aContentParentFrame,
nsStyleContext* aStyleContext,
nsIFrame* aNewFrame,
nsIFrame** aNewFrame,
PRBool aRelPos);
nsresult ConstructInline(nsIPresShell* aPresShell,

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

@ -83,6 +83,7 @@ CSS_ANON_BOX(page, ":-moz-page")
CSS_ANON_BOX(pageContent, ":-moz-pagecontent")
CSS_ANON_BOX(pageSequence, ":-moz-page-sequence")
CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content")
CSS_ANON_BOX(columnContent, ":-moz-column-content")
CSS_ANON_BOX(viewport, ":-moz-viewport")
CSS_ANON_BOX(viewportScroll, ":-moz-viewport-scroll")
CSS_ANON_BOX(selectScrolledContent, ":-moz-select-scrolled-content")

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

@ -806,12 +806,13 @@ nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const
{
if ((mColumnWidth.GetUnit() == eStyleUnit_Auto)
!= (aOther.mColumnWidth.GetUnit() == eStyleUnit_Auto) ||
(mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO)
!= (aOther.mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO))
mColumnCount != aOther.mColumnCount)
// We force column count changes to do a reframe, because it's tricky to handle
// some edge cases where the column count gets smaller and content overflows.
// XXX not ideal
return nsChangeHint_ReconstructFrame;
if (mColumnCount != aOther.mColumnCount ||
mColumnWidth != aOther.mColumnWidth ||
if (mColumnWidth != aOther.mColumnWidth ||
mColumnGap != aOther.mColumnGap)
return nsChangeHint_ReflowFrame;

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

@ -147,6 +147,10 @@
-moz-counter-reset: inherit;
}
*|*::-moz-column-content {
/* the column boxes inside a column-flowed block */
}
*|*::-moz-page, *|*::-moz-page-sequence {
display: block !important;
background: transparent;