/** * Place below-current-line floats. */ PRBool nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRBool aForceFit) { nsFloatCache* fc = aList.Head(); while (fc) { { #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("placing bcl float: "); nsFrame::ListTag(stdout, fc->mPlaceholder->GetOutOfFlowFrame()); printf("\n"); } #endif // Place the float PRBool isLeftFloat; nsReflowStatus reflowStatus; PRBool placed = FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus, aForceFit); NS_ASSERTION(placed || !aForceFit, "If we're in force-fit mode, we should have placed the float"); if (!placed || (NS_FRAME_IS_TRUNCATED(reflowStatus) && !aForceFit)) { // return before processing all of the floats, since the line will be pushed. return PR_FALSE; } else if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Create a continuation for the incomplete float and its placeholder. nsresult rv = mBlock->SplitPlaceholder(*this, fc->mPlaceholder); if (NS_FAILED(rv)) return PR_FALSE; } else { // XXX We could deal with truncated frames better by breaking before // the associated placeholder NS_WARN_IF_FALSE(!NS_FRAME_IS_TRUNCATED(reflowStatus), "This situation currently leads to data not printing"); // Float is complete. We need to delete any leftover placeholders now. nsIFrame* nextPlaceholder = fc->mPlaceholder->GetNextInFlow(); if (nextPlaceholder) { nsHTMLContainerFrame* parent = static_cast<nsHTMLContainerFrame*>(nextPlaceholder->GetParent()); parent->DeleteNextInFlowChild(mPresContext, nextPlaceholder); } } } fc = fc->Next(); } return PR_TRUE; }
/** * Place below-current-line floats. */ PRBool nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRBool aForceFit) { nsFloatCache* fc = aList.Head(); while (fc) { { #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("placing bcl float: "); nsFrame::ListTag(stdout, fc->mFloat); printf("\n"); } #endif // Place the float nsReflowStatus reflowStatus; PRBool placed = FlowAndPlaceFloat(fc->mFloat, reflowStatus, aForceFit); NS_ASSERTION(placed || !aForceFit, "If we're in force-fit mode, we should have placed the float"); if (!placed || (NS_FRAME_IS_TRUNCATED(reflowStatus) && !aForceFit)) { // return before processing all of the floats, since the line will be pushed. return PR_FALSE; } else if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) { // Create a continuation for the incomplete float nsresult rv = mBlock->SplitFloat(*this, fc->mFloat, reflowStatus); if (NS_FAILED(rv)) return PR_FALSE; } else { // XXX We could deal with truncated frames better by breaking before // the associated placeholder NS_WARN_IF_FALSE(!NS_FRAME_IS_TRUNCATED(reflowStatus), "This situation currently leads to data not printing"); // Float is complete. } } fc = fc->Next(); } return PR_TRUE; }
// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- // technically we're supposed let the current line flow around the // float as well unless it won't fit next to what we already have. // But nobody else implements it that way... PRBool nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, nsPlaceholderFrame* aPlaceholder, PRBool aInitialReflow, nsReflowStatus& aReflowStatus) { NS_PRECONDITION(mBlock->end_lines() != mCurrentLine, "null ptr"); aReflowStatus = NS_FRAME_COMPLETE; // Allocate a nsFloatCache for the float nsFloatCache* fc = mFloatCacheFreeList.Alloc(); fc->mPlaceholder = aPlaceholder; PRBool placed; // Now place the float immediately if possible. Otherwise stash it // away in mPendingFloats and place it later. if (aLineLayout.CanPlaceFloatNow()) { // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space // manager's translation to the space that the block resides in // before placing the float. nscoord ox, oy; mSpaceManager->GetTranslation(ox, oy); nscoord dx = ox - mSpaceManagerX; nscoord dy = oy - mSpaceManagerY; mSpaceManager->Translate(-dx, -dy); // And then place it PRBool isLeftFloat; // force it to fit if we're at the top of the block and we can't // break before this PRBool forceFit = IsAdjacentWithTop() && !aLineLayout.LineIsBreakable(); placed = FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus, forceFit); NS_ASSERTION(placed || !forceFit, "If we asked for force-fit, it should have been placed"); if (forceFit || (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus))) { // Pass on updated available space to the current inline reflow engine GetAvailableSpace(mY, forceFit); aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, mAvailSpaceRect.width, mAvailSpaceRect.height, isLeftFloat, aPlaceholder->GetOutOfFlowFrame()); // Record this float in the current-line list mCurrentLineFloats.Append(fc); // If we can't break here, hide the fact that it's truncated // XXX We can probably do this more cleanly aReflowStatus &= ~NS_FRAME_TRUNCATED; } else { if (IsAdjacentWithTop()) { // Pushing the line to the next page won't give us any more space; // therefore, we break. NS_ASSERTION(aLineLayout.LineIsBreakable(), "We can't get here unless forceFit is false"); aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); } else { // Make sure we propagate the truncated status; this signals the // block to push the line to the next page. aReflowStatus |= NS_FRAME_TRUNCATED; } delete fc; } // Restore coordinate system mSpaceManager->Translate(dx, dy); } else { // Always claim to be placed; we don't know whether we fit yet, so we // deal with this in PlaceBelowCurrentLineFloats placed = PR_TRUE; // This float will be placed after the line is done (it is a // below-current-line float). mBelowCurrentLineFloats.Append(fc); if (aPlaceholder->GetNextInFlow()) { // If the float might not be complete, mark it incomplete now to // prevent its next-in-flow placeholders being torn down. We will destroy any // placeholders later if PlaceBelowCurrentLineFloats finds the // float is complete. // Note that we could have unconstrained height and yet have // a next-in-flow placeholder --- for example columns can switch // from constrained height to unconstrained height. if (aPlaceholder->GetSplittableType() != NS_FRAME_NOT_SPLITTABLE) { aReflowStatus = NS_FRAME_NOT_COMPLETE; } } } return placed; }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aBottomMarginCarriedOut, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; bool RTL = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; bool shrinkingHeightOnly = !NS_SUBTREE_DIRTY(this) && 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); nsOverflowAreas overflowRects; 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.ComputedWidth() != NS_INTRINSICSIZE) { availWidth = aReflowState.ComputedWidth(); } 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; int contentBottom = 0; bool reflowNext = 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. We can't skip if it's the last child // because we need to obtain the bottom margin. We can't skip // if this is the last column and we're supposed to assign unbounded // height to it, because that could change the available height from // the last time we reflowed it and we should try to pull all the // content from its next sibling. (Note that it might be the last // column, but not be the last child because the desired number of columns // has changed.) bool skipIncremental = !aReflowState.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(child) && child->GetNextSibling() && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) && !NS_SUBTREE_DIRTY(child->GetNextSibling()); // If we need to pull up content from the prev-in-flow then this is not just // a height shrink. The prev in flow will have set the dirty bit. // Check the overflow rect YMost instead of just the child's content height. The child // may have overflowing content that cares about the available height boundary. // (It may also have overflowing content that doesn't care about the available height // boundary, but if so, too bad, this optimization is defeated.) // We want scrollable overflow here since this is a calculation that // affects layout. bool skipResizeHeightShrink = shrinkingHeightOnly && child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight; nscoord childContentBottom = 0; 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 nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { aStatus = mLastFrameStatus; } childContentBottom = nsLayoutUtils::CalculateContentBottom(child); #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); } if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, availSize.width, aReflowState.ComputedHeight()); kidReflowState.mFlags.mIsTopOfPage = PR_TRUE; kidReflowState.mFlags.mTableIsSplittable = PR_FALSE; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,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() && !(GetStateBits() & NS_FRAME_IS_DIRTY) && !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { kidReflowState.mFlags.mNextInFlowUntouched = PR_TRUE; } nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags); // XXX it would be cool to consult the float 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 float 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, PresContext(), kidDesiredSize, kidReflowState, childOrigin.x + kidReflowState.mComputedMargin.left, childOrigin.y + kidReflowState.mComputedMargin.top, 0, aStatus); 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); *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBottomMargin; FinishReflowChild(child, PresContext(), &kidReflowState, kidDesiredSize, childOrigin.x, childOrigin.y, 0); childContentBottom = nsLayoutUtils::CalculateContentBottom(child); if (childContentBottom > aConfig.mColMaxHeight) { allFit = PR_FALSE; } if (childContentBottom > availSize.height) { aColData.mMaxOverflowingHeight = NS_MAX(childContentBottom, aColData.mMaxOverflowingHeight); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBottom = NS_MAX(contentBottom, childContentBottom); aColData.mLastHeight = childContentBottom; aColData.mSumHeight += childContentBottom; // Build a continuation column if necessary nsIFrame* kidNextInFlow = child->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); child = nsnull; 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 nsresult rv = CreateNextInFlow(PresContext(), child, kidNextInFlow); if (NS_FAILED(rv)) { NS_NOTREACHED("Couldn't create continuation"); child = nsnull; break; } } // Make sure we reflow a next-in-flow when it switches between being // normal or overflow container if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = PR_TRUE; kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = PR_TRUE; kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } if (columnCount >= aConfig.mBalanceColCount) { // No more columns allowed here. Stop. aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); // Move any of our leftover columns to our overflow list. Our // next-in-flow will eventually pick them up. const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); if (continuationColumns.NotEmpty()) { SetOverflowFrames(PresContext(), continuationColumns); } child = nsnull; break; } } if (PresContext()->HasPendingInterrupt()) { // Stop the loop now while |child| still points to the frame that bailed // out. We could keep going here and condition a bunch of the code in // this loop on whether there's an interrupt, or even just keep going and // trying to reflow the blocks (even though we know they'll interrupt // right after their first line), but stopping now is conceptually the // simplest (and probably fastest) thing. 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 (PresContext()->CheckForInterrupt(this) && (GetStateBits() & NS_FRAME_IS_DIRTY)) { // Mark all our kids starting with |child| dirty // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, // because we might have interrupted while reflowing |child|, and since // we're about to add a dirty bit to |child| we need to make sure that // |this| is scheduled to have dirty bits marked on it and its ancestors. // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll // bail out immediately, since it'll already have a dirty bit. for (; child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } // 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) { overflowRects.Clear(); 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(overflowRects, child); contentRect.UnionRect(contentRect, child->GetRect()); } } aColData.mMaxHeight = contentBottom; contentRect.height = NS_MAX(contentRect.height, contentBottom); 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.ComputedHeight() != NS_INTRINSICSIZE) { contentSize.height = aReflowState.ComputedHeight(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) { contentSize.height = NS_MIN(aReflowState.mComputedMaxHeight, contentSize.height); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) { contentSize.height = NS_MAX(aReflowState.mComputedMinHeight, contentSize.height); } } if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { contentSize.width = aReflowState.ComputedWidth(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { contentSize.width = NS_MIN(aReflowState.mComputedMaxWidth, contentSize.width); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { contentSize.width = NS_MAX(aReflowState.mComputedMinWidth, contentSize.width); } } aDesiredSize.height = borderPadding.top + contentSize.height + borderPadding.bottom; aDesiredSize.width = contentSize.width + borderPadding.left + borderPadding.right; aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); }
// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- // technically we're supposed let the current line flow around the // float as well unless it won't fit next to what we already have. // But nobody else implements it that way... PRBool nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, nscoord aAvailableWidth, nsReflowStatus& aReflowStatus) { NS_PRECONDITION(!aLineLayout || mBlock->end_lines() != mCurrentLine, "null ptr"); NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, "aFloat must be an out-of-flow frame"); // Set the geometric parent of the float aFloat->SetParent(mBlock); aReflowStatus = NS_FRAME_COMPLETE; // Because we are in the middle of reflowing a placeholder frame // within a line (and possibly nested in an inline frame or two // that's a child of our block) we need to restore the space // manager's translation to the space that the block resides in // before placing the float. nscoord ox, oy; mFloatManager->GetTranslation(ox, oy); nscoord dx = ox - mFloatManagerX; nscoord dy = oy - mFloatManagerY; mFloatManager->Translate(-dx, -dy); PRBool placed; // Now place the float immediately if possible. Otherwise stash it // away in mPendingFloats and place it later. // If one or more floats has already been pushed to the next line, // don't let this one go on the current line, since that would violate // float ordering. nsRect floatAvailableSpace = GetFloatAvailableSpace().mRect; if (!aLineLayout || (mBelowCurrentLineFloats.IsEmpty() && (aLineLayout->LineIsEmpty() || mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat) <= aAvailableWidth))) { // And then place it // force it to fit if we're at the top of the block and we can't // break before this PRBool forceFit = !aLineLayout || (IsAdjacentWithTop() && !aLineLayout->LineIsBreakable()); placed = FlowAndPlaceFloat(aFloat, aReflowStatus, forceFit); NS_ASSERTION(placed || !forceFit, "If we asked for force-fit, it should have been placed"); if (forceFit || (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus))) { // Pass on updated available space to the current inline reflow engine nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY, forceFit); nsRect availSpace(nsPoint(floatAvailSpace.mRect.x + BorderPadding().left, mY), floatAvailSpace.mRect.Size()); if (aLineLayout) { aLineLayout->UpdateBand(availSpace, aFloat); // Record this float in the current-line list mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); } // If we can't break here, hide the fact that it's truncated // XXX We can probably do this more cleanly aReflowStatus &= ~NS_FRAME_TRUNCATED; } else { if (IsAdjacentWithTop()) { // Pushing the line to the next page won't give us any more space; // therefore, we break. NS_ASSERTION(aLineLayout->LineIsBreakable(), "We can't get here unless forceFit is false"); aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); } else { // Make sure we propagate the truncated status; this signals the // block to push the line to the next page. aReflowStatus |= NS_FRAME_TRUNCATED; } } } else { // Always claim to be placed; we don't know whether we fit yet, so we // deal with this in PlaceBelowCurrentLineFloats placed = PR_TRUE; // This float will be placed after the line is done (it is a // below-current-line float). mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); } // Restore coordinate system mFloatManager->Translate(dx, dy); return placed; }