summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsColumnSetFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic/nsColumnSetFrame.cpp')
-rw-r--r--layout/generic/nsColumnSetFrame.cpp1169
1 files changed, 1169 insertions, 0 deletions
diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp
new file mode 100644
index 000000000..ad36ba1a8
--- /dev/null
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -0,0 +1,1169 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* rendering object for css3 multi-column layout */
+
+#include "mozilla/Unused.h"
+#include "nsColumnSetFrame.h"
+#include "nsCSSRendering.h"
+#include "nsDisplayList.h"
+
+using namespace mozilla;
+using namespace mozilla::layout;
+
+/**
+ * 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?
+ */
+nsContainerFrame*
+NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags)
+{
+ nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame(aContext);
+ it->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT);
+ return it;
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetFrame)
+
+nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext)
+ : nsContainerFrame(aContext), mLastBalanceBSize(NS_INTRINSICSIZE),
+ mLastFrameStatus(NS_FRAME_COMPLETE)
+{
+}
+
+nsIAtom*
+nsColumnSetFrame::GetType() const
+{
+ return nsGkAtoms::columnSetFrame;
+}
+
+static void
+PaintColumnRule(nsIFrame* aFrame, nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect, nsPoint aPt)
+{
+ static_cast<nsColumnSetFrame*>(aFrame)->PaintColumnRule(aCtx, aDirtyRect, aPt);
+}
+
+void
+nsColumnSetFrame::PaintColumnRule(nsRenderingContext* aCtx,
+ const nsRect& aDirtyRect,
+ const nsPoint& aPt)
+{
+ nsIFrame* child = mFrames.FirstChild();
+ if (!child)
+ return; // no columns
+
+ nsIFrame* nextSibling = child->GetNextSibling();
+ if (!nextSibling)
+ return; // 1 column only - this means no gap to draw on
+
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+ bool isRTL = !wm.IsBidiLTR();
+ const nsStyleColumn* colStyle = StyleColumn();
+
+ uint8_t ruleStyle;
+ // Per spec, inset => ridge and outset => groove
+ if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET)
+ ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE;
+ else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET)
+ ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE;
+ else
+ ruleStyle = colStyle->mColumnRuleStyle;
+
+ nsPresContext* presContext = PresContext();
+ nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
+ if (!ruleWidth)
+ return;
+
+ nscolor ruleColor =
+ GetVisitedDependentColor(eCSSProperty_column_rule_color);
+
+ // In order to re-use a large amount of code, we treat the column rule as a border.
+ // We create a new border style object and fill in all the details of the column rule as
+ // the left border. PaintBorder() does all the rendering for us, so we not
+ // only save an enormous amount of code but we'll support all the line styles that
+ // we support on borders!
+ nsStyleBorder border(presContext);
+ Sides skipSides;
+ if (isVertical) {
+ border.SetBorderWidth(NS_SIDE_TOP, ruleWidth);
+ border.SetBorderStyle(NS_SIDE_TOP, ruleStyle);
+ border.mBorderTopColor = StyleComplexColor::FromColor(ruleColor);
+ skipSides |= mozilla::eSideBitsLeftRight;
+ skipSides |= mozilla::eSideBitsBottom;
+ } else {
+ border.SetBorderWidth(NS_SIDE_LEFT, ruleWidth);
+ border.SetBorderStyle(NS_SIDE_LEFT, ruleStyle);
+ border.mBorderLeftColor = StyleComplexColor::FromColor(ruleColor);
+ skipSides |= mozilla::eSideBitsTopBottom;
+ skipSides |= mozilla::eSideBitsRight;
+ }
+
+ // Get our content rect as an absolute coordinate, not relative to
+ // our parent (which is what the X and Y normally is)
+ nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt;
+ nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth)
+ : nsSize(ruleWidth, contentRect.height);
+
+ while (nextSibling) {
+ // The frame tree goes RTL in RTL.
+ // The |prevFrame| and |nextFrame| frames here are the visually preceding
+ // (left/above) and following (right/below) frames, not in logical writing-
+ // mode direction.
+ nsIFrame* prevFrame = isRTL ? nextSibling : child;
+ nsIFrame* nextFrame = isRTL ? child : nextSibling;
+
+ // Each child frame's position coordinates is actually relative to this
+ // nsColumnSetFrame.
+ // linePt will be at the top-left edge to paint the line.
+ nsPoint linePt;
+ if (isVertical) {
+ nscoord edgeOfPrev = prevFrame->GetRect().YMost() + aPt.y;
+ nscoord edgeOfNext = nextFrame->GetRect().Y() + aPt.y;
+ linePt = nsPoint(contentRect.x,
+ (edgeOfPrev + edgeOfNext - ruleSize.height) / 2);
+ } else {
+ nscoord edgeOfPrev = prevFrame->GetRect().XMost() + aPt.x;
+ nscoord edgeOfNext = nextFrame->GetRect().X() + aPt.x;
+ linePt = nsPoint((edgeOfPrev + edgeOfNext - ruleSize.width) / 2,
+ contentRect.y);
+ }
+
+ nsRect lineRect(linePt, ruleSize);
+
+ // Assert that we're not drawing a border-image here; if we were, we
+ // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
+ MOZ_ASSERT(border.mBorderImageSource.GetType() == eStyleImageType_Null);
+
+ Unused <<
+ nsCSSRendering::PaintBorderWithStyleBorder(presContext, *aCtx, this,
+ aDirtyRect, lineRect, border,
+ StyleContext(),
+ PaintBorderFlags::SYNC_DECODE_IMAGES,
+ skipSides);
+
+ child = nextSibling;
+ nextSibling = nextSibling->GetNextSibling();
+ }
+}
+
+static nscoord
+GetAvailableContentISize(const ReflowInput& aReflowInput)
+{
+ if (aReflowInput.AvailableISize() == NS_INTRINSICSIZE) {
+ return NS_INTRINSICSIZE;
+ }
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ nscoord borderPaddingISize =
+ aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
+ return std::max(0, aReflowInput.AvailableISize() - borderPaddingISize);
+}
+
+nscoord
+nsColumnSetFrame::GetAvailableContentBSize(const ReflowInput& aReflowInput)
+{
+ if (aReflowInput.AvailableBSize() == NS_INTRINSICSIZE) {
+ return NS_INTRINSICSIZE;
+ }
+
+ WritingMode wm = aReflowInput.GetWritingMode();
+ LogicalMargin bp = aReflowInput.ComputedLogicalBorderPadding();
+ bp.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
+ bp.BEnd(wm) = aReflowInput.ComputedLogicalBorderPadding().BEnd(wm);
+ return std::max(0, aReflowInput.AvailableBSize() - bp.BStartEnd(wm));
+}
+
+static nscoord
+GetColumnGap(nsColumnSetFrame* aFrame,
+ const nsStyleColumn* aColStyle)
+{
+ if (eStyleUnit_Normal == aColStyle->mColumnGap.GetUnit())
+ return aFrame->StyleFont()->mFont.size;
+ if (eStyleUnit_Coord == aColStyle->mColumnGap.GetUnit()) {
+ nscoord colGap = aColStyle->mColumnGap.GetCoordValue();
+ NS_ASSERTION(colGap >= 0, "negative column gap");
+ return colGap;
+ }
+
+ NS_NOTREACHED("Unknown gap type");
+ return 0;
+}
+
+nsColumnSetFrame::ReflowConfig
+nsColumnSetFrame::ChooseColumnStrategy(const ReflowInput& aReflowInput,
+ bool aForceAuto = false,
+ nscoord aFeasibleBSize = NS_INTRINSICSIZE,
+ nscoord aInfeasibleBSize = 0)
+{
+ nscoord knownFeasibleBSize = aFeasibleBSize;
+ nscoord knownInfeasibleBSize = aInfeasibleBSize;
+
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord availContentISize = GetAvailableContentISize(aReflowInput);
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ availContentISize = aReflowInput.ComputedISize();
+ }
+
+ nscoord consumedBSize = GetConsumedBSize();
+
+ // The effective computed height is the height of the current continuation
+ // of the column set frame. This should be the same as the computed height
+ // if we have an unconstrained available height.
+ nscoord computedBSize = GetEffectiveComputedBSize(aReflowInput,
+ consumedBSize);
+ nscoord colBSize = GetAvailableContentBSize(aReflowInput);
+
+ if (aReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
+ colBSize = aReflowInput.ComputedBSize();
+ } else if (aReflowInput.ComputedMaxBSize() != NS_INTRINSICSIZE) {
+ colBSize = std::min(colBSize, aReflowInput.ComputedMaxBSize());
+ }
+
+ nscoord colGap = GetColumnGap(this, colStyle);
+ int32_t numColumns = colStyle->mColumnCount;
+
+ // If column-fill is set to 'balance', then we want to balance the columns.
+ const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE
+ && !aForceAuto;
+ if (isBalancing) {
+ const uint32_t MAX_NESTED_COLUMN_BALANCING = 2;
+ uint32_t cnt = 0;
+ for (const ReflowInput* rs = aReflowInput.mParentReflowInput;
+ rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->mParentReflowInput) {
+ if (rs->mFlags.mIsColumnBalancing) {
+ ++cnt;
+ }
+ }
+ if (cnt == MAX_NESTED_COLUMN_BALANCING) {
+ numColumns = 1;
+ }
+ }
+
+ nscoord colISize;
+ // In vertical writing-mode, "column-width" (inline size) will actually be
+ // physical height, but its CSS name is still column-width.
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ NS_ASSERTION(colISize >= 0, "negative column width");
+ // Reduce column count if necessary to make columns fit in the
+ // available width. Compute max number of columns that fit in
+ // availContentISize, satisfying colGap*(maxColumns - 1) +
+ // colISize*maxColumns <= availContentISize
+ if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0
+ && numColumns > 0) {
+ // This expression uses truncated rounding, which is what we
+ // want
+ int32_t maxColumns =
+ std::min(nscoord(nsStyleColumn::kMaxColumnCount),
+ (availContentISize + colGap) / (colGap + colISize));
+ numColumns = std::max(1, std::min(numColumns, maxColumns));
+ }
+ } else if (numColumns > 0 && availContentISize != NS_INTRINSICSIZE) {
+ nscoord iSizeMinusGaps = availContentISize - colGap * (numColumns - 1);
+ colISize = iSizeMinusGaps / numColumns;
+ } else {
+ colISize = NS_INTRINSICSIZE;
+ }
+ // Take care of the situation where there's only one column but it's
+ // still too wide
+ colISize = std::max(1, std::min(colISize, availContentISize));
+
+ nscoord expectedISizeLeftOver = 0;
+
+ if (colISize != NS_INTRINSICSIZE && availContentISize != 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) +
+ // colISize*nominalColumnCount is nearly availContentISize
+ // make sure to round down
+ if (colGap + colISize > 0) {
+ numColumns = (availContentISize + colGap) / (colGap + colISize);
+ // The number of columns should never exceed kMaxColumnCount.
+ numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount),
+ numColumns);
+ }
+ if (numColumns <= 0) {
+ numColumns = 1;
+ }
+ }
+
+ // Compute extra space and divide it among the columns
+ nscoord extraSpace =
+ std::max(0, availContentISize - (colISize * numColumns +
+ colGap * (numColumns - 1)));
+ nscoord extraToColumns = extraSpace / numColumns;
+ colISize += extraToColumns;
+ expectedISizeLeftOver = extraSpace - (extraToColumns * numColumns);
+ }
+
+ if (isBalancing) {
+ 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;
+ }
+ colBSize = std::min(mLastBalanceBSize, colBSize);
+ } else {
+ // This is the case when the column-fill property is set to 'auto'.
+ // No balancing, so don't limit the column count
+ numColumns = INT32_MAX;
+
+ // XXX_jwir3: If a page's height is set to 0, we could continually
+ // create continuations, resulting in an infinite loop, since
+ // no progress is ever made. This is an issue with the spec
+ // (css3-multicol, css3-page, and css3-break) that is
+ // unresolved as of 27 Feb 2013. For the time being, we set this
+ // to have a minimum of 1 css px. Once a resolution is made
+ // on what minimum to have for a page height, we may need to
+ // change this value to match the appropriate spec(s).
+ colBSize = std::max(colBSize, nsPresContext::CSSPixelsToAppUnits(1));
+ }
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colISize=%d,"
+ " expectedISizeLeftOver=%d, colBSize=%d, colGap=%d\n",
+ numColumns, colISize, expectedISizeLeftOver, colBSize, colGap);
+#endif
+ ReflowConfig config = { numColumns, colISize, expectedISizeLeftOver, colGap,
+ colBSize, isBalancing, knownFeasibleBSize,
+ knownInfeasibleBSize, computedBSize, consumedBSize };
+ return config;
+}
+
+bool
+nsColumnSetFrame::ReflowColumns(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aReflowStatus,
+ ReflowConfig& aConfig,
+ bool aLastColumnUnbounded,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData)
+{
+ bool feasible = ReflowChildren(aDesiredSize, aReflowInput,
+ aReflowStatus, aConfig, aLastColumnUnbounded,
+ aCarriedOutBEndMargin, aColData);
+
+ if (aColData.mHasExcessBSize) {
+ aConfig = ChooseColumnStrategy(aReflowInput, true);
+
+ // We need to reflow our children again one last time, otherwise we might
+ // end up with a stale column height for some of our columns, since we
+ // bailed out of balancing.
+ feasible = ReflowChildren(aDesiredSize, aReflowInput, aReflowStatus,
+ aConfig, aLastColumnUnbounded,
+ aCarriedOutBEndMargin, aColData);
+ }
+
+ return feasible;
+}
+
+static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin,
+ WritingMode aWM, const nsSize& aContainerSize)
+{
+ if (aChild->GetLogicalPosition(aWM, aContainerSize) == aOrigin) {
+ return;
+ }
+
+ aChild->SetPosition(aWM, aOrigin, aContainerSize);
+ nsContainerFrame::PlaceFrameView(aChild);
+}
+
+nscoord
+nsColumnSetFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+ nscoord iSize = 0;
+ DISPLAY_MIN_WIDTH(this, iSize);
+ if (mFrames.FirstChild()) {
+ iSize = mFrames.FirstChild()->GetMinISize(aRenderingContext);
+ }
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord colISize;
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ // As available width reduces to zero, we reduce our number of columns
+ // to one, and don't enforce the column width, so just return the min
+ // of the child's min-width with any specified column width.
+ iSize = std::min(iSize, colISize);
+ } else {
+ NS_ASSERTION(colStyle->mColumnCount > 0,
+ "column-count and column-width can't both be auto");
+ // As available width reduces to zero, we still have mColumnCount columns,
+ // so multiply the child's min-width by the number of columns (n) and
+ // include n-1 column gaps.
+ colISize = iSize;
+ iSize *= colStyle->mColumnCount;
+ nscoord colGap = GetColumnGap(this, colStyle);
+ iSize += colGap * (colStyle->mColumnCount - 1);
+ // The multiplication above can make 'width' negative (integer overflow),
+ // so use std::max to protect against that.
+ iSize = std::max(iSize, colISize);
+ }
+ // XXX count forced column breaks here? Maybe we should return the child's
+ // min-width times the minimum number of columns.
+ return iSize;
+}
+
+nscoord
+nsColumnSetFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+ // Our preferred width is our desired column width, if specified, otherwise
+ // the child's preferred width, times the number of columns, plus the width
+ // of any required column gaps
+ // XXX what about forced column breaks here?
+ nscoord result = 0;
+ DISPLAY_PREF_WIDTH(this, result);
+ const nsStyleColumn* colStyle = StyleColumn();
+ nscoord colGap = GetColumnGap(this, colStyle);
+
+ nscoord colISize;
+ if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
+ colISize = colStyle->mColumnWidth.GetCoordValue();
+ } else if (mFrames.FirstChild()) {
+ colISize = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
+ } else {
+ colISize = 0;
+ }
+
+ int32_t numColumns = colStyle->mColumnCount;
+ if (numColumns <= 0) {
+ // if column-count is auto, assume one column
+ numColumns = 1;
+ }
+
+ nscoord iSize = colISize * numColumns + colGap * (numColumns - 1);
+ // The multiplication above can make 'iSize' negative (integer overflow),
+ // so use std::max to protect against that.
+ result = std::max(iSize, colISize);
+ return result;
+}
+
+bool
+nsColumnSetFrame::ReflowChildren(ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus,
+ const ReflowConfig& aConfig,
+ bool aUnboundedLastColumn,
+ nsCollapsingMargin* aCarriedOutBEndMargin,
+ ColumnBalanceData& aColData)
+{
+ aColData.Reset();
+ bool allFit = true;
+ WritingMode wm = GetWritingMode();
+ bool isVertical = wm.IsVertical();
+ bool isRTL = !wm.IsBidiLTR();
+ bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) &&
+ mLastBalanceBSize > aConfig.mColMaxBSize;
+
+#ifdef DEBUG_roc
+ printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n"
+ " mBalanceColCount=%d, mColISize=%d, mColGap=%d\n",
+ mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount,
+ aConfig.mColISize, aConfig.mColGap);
+#endif
+
+ DrainOverflowColumns();
+
+ const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize;
+
+ if (colBSizeChanged) {
+ mLastBalanceBSize = aConfig.mColMaxBSize;
+ // 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
+ LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
+ borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
+
+ nsRect contentRect(0, 0, 0, 0);
+ nsOverflowAreas overflowRects;
+
+ nsIFrame* child = mFrames.FirstChild();
+ LogicalPoint childOrigin(wm, borderPadding.IStart(wm),
+ borderPadding.BStart(wm));
+ // In vertical-rl mode, columns will not be correctly placed if the
+ // reflowInput's ComputedWidth() is UNCONSTRAINED (in which case we'll get
+ // a containerSize.width of zero here). In that case, the column positions
+ // will be adjusted later, after our correct contentSize is known.
+ nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
+
+ // For RTL, 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
+
+ // XXX when all of layout is converted to logical coordinates, we
+ // probably won't need to do this hack any more. For now, we
+ // confine it to the legacy horizontal-rl case
+ if (!isVertical && isRTL) {
+ nscoord availISize = aReflowInput.AvailableISize();
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ availISize = aReflowInput.ComputedISize();
+ }
+ if (availISize != NS_INTRINSICSIZE) {
+ childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) -
+ availISize;
+#ifdef DEBUG_roc
+ printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm));
+#endif
+ }
+ }
+
+ int columnCount = 0;
+ int contentBEnd = 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 = !aReflowInput.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 skipResizeBSizeShrink = false;
+ if (shrinkingBSizeOnly) {
+ switch (wm.GetBlockDir()) {
+ case WritingMode::eBlockTB:
+ if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) {
+ skipResizeBSizeShrink = true;
+ }
+ break;
+ case WritingMode::eBlockLR:
+ if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) {
+ skipResizeBSizeShrink = true;
+ }
+ break;
+ case WritingMode::eBlockRL:
+ // XXX not sure how to handle this, so for now just don't attempt
+ // the optimization
+ break;
+ default:
+ NS_NOTREACHED("unknown block direction");
+ break;
+ }
+ }
+
+ nscoord childContentBEnd = 0;
+ if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) {
+ // This child does not need to be reflowed, but we may need to move it
+ MoveChildTo(child, childOrigin, wm, containerSize);
+
+ // 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;
+ }
+ childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
+#ifdef DEBUG_roc
+ printf("*** Skipping child #%d %p (incremental %d, resize block-size shrink %d): status = %d\n",
+ columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus);
+#endif
+ } else {
+ LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize);
+ if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
+ availSize.BSize(wm) = GetAvailableContentBSize(aReflowInput);
+ }
+
+ LogicalSize computedSize = aReflowInput.ComputedSize(wm);
+
+ if (reflowNext)
+ child->AddStateBits(NS_FRAME_IS_DIRTY);
+
+ LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm));
+ ReflowInput kidReflowInput(PresContext(), aReflowInput, child,
+ availSize, &kidCBSize);
+ kidReflowInput.mFlags.mIsTopOfPage = true;
+ kidReflowInput.mFlags.mTableIsSplittable = false;
+ kidReflowInput.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX;
+
+ // We need to reflow any float placeholders, even if our column height
+ // hasn't changed.
+ kidReflowInput.mFlags.mMustReflowPlaceholders = !colBSizeChanged;
+
+#ifdef DEBUG_roc
+ printf("*** Reflowing child #%d %p: availHeight=%d\n",
+ columnCount, (void*)child,availSize.BSize(wm));
+#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)) {
+ kidReflowInput.mFlags.mNextInFlowUntouched = true;
+ }
+
+ ReflowOutput kidDesiredSize(wm, 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
+ LogicalPoint origin(wm,
+ childOrigin.I(wm) +
+ kidReflowInput.ComputedLogicalMargin().IStart(wm),
+ childOrigin.B(wm) +
+ kidReflowInput.ComputedLogicalMargin().BStart(wm));
+ ReflowChild(child, PresContext(), kidDesiredSize, kidReflowInput,
+ wm, origin, containerSize, 0, aStatus);
+
+ reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0;
+
+#ifdef DEBUG_roc
+ printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n",
+ columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(),
+ kidDesiredSize.mCarriedOutBEndMargin.get());
+#endif
+
+ NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
+
+ *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin;
+
+ FinishReflowChild(child, PresContext(), kidDesiredSize,
+ &kidReflowInput, wm, childOrigin, containerSize, 0);
+
+ childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
+ if (childContentBEnd > aConfig.mColMaxBSize) {
+ allFit = false;
+ }
+ if (childContentBEnd > availSize.BSize(wm)) {
+ aColData.mMaxOverflowingBSize = std::max(childContentBEnd,
+ aColData.mMaxOverflowingBSize);
+ }
+ }
+
+ contentRect.UnionRect(contentRect, child->GetRect());
+
+ ConsiderChildOverflow(overflowRects, child);
+ contentBEnd = std::max(contentBEnd, childContentBEnd);
+ aColData.mLastBSize = childContentBEnd;
+ aColData.mSumBSize += childContentBEnd;
+
+ // 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 = nullptr;
+ 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
+ kidNextInFlow = CreateNextInFlow(child);
+ }
+
+ // 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 = true;
+ kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+ }
+ else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
+ aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+ reflowNext = true;
+ kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
+ }
+
+ if ((contentBEnd > aReflowInput.ComputedMaxBSize() ||
+ contentBEnd > aReflowInput.ComputedBSize()) &&
+ aConfig.mBalanceColCount < INT32_MAX) {
+ // We overflowed vertically, but have not exceeded the number of
+ // columns. We're going to go into overflow columns now, so balancing
+ // no longer applies.
+ aColData.mHasExcessBSize = true;
+ }
+
+ 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(continuationColumns);
+ }
+ child = nullptr;
+ 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) {
+ childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap;
+
+#ifdef DEBUG_roc
+ printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm));
+#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);
+ }
+ }
+
+ aColData.mMaxBSize = contentBEnd;
+ LogicalSize contentSize = LogicalSize(wm, contentRect.Size());
+ contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd);
+ mLastFrameStatus = aStatus;
+
+ // Apply computed and min/max values
+ if (aConfig.mComputedBSize != NS_INTRINSICSIZE) {
+ if (aReflowInput.AvailableBSize() != NS_INTRINSICSIZE) {
+ contentSize.BSize(wm) = std::min(contentSize.BSize(wm),
+ aConfig.mComputedBSize);
+ } else {
+ contentSize.BSize(wm) = aConfig.mComputedBSize;
+ }
+ } else {
+ // We add the "consumed" block-size back in so that we're applying
+ // constraints to the correct bSize value, then subtract it again
+ // after we've finished with the min/max calculation. This prevents us from
+ // having a last continuation that is smaller than the min bSize. but which
+ // has prev-in-flows, trigger a larger bSize than actually required.
+ contentSize.BSize(wm) =
+ aReflowInput.ApplyMinMaxBSize(contentSize.BSize(wm),
+ aConfig.mConsumedBSize);
+ }
+ if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
+ contentSize.ISize(wm) = aReflowInput.ComputedISize();
+ } else {
+ contentSize.ISize(wm) =
+ aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm));
+ }
+
+ contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
+ contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
+ aDesiredSize.SetSize(wm, contentSize);
+ aDesiredSize.mOverflowAreas = overflowRects;
+ aDesiredSize.UnionOverflowAreasWithDesiredBounds();
+
+ // In vertical-rl mode, make a second pass if necessary to reposition the
+ // columns with the correct container width. (In other writing modes,
+ // correct containerSize was not required for column positioning so we don't
+ // need this fixup.)
+ if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) {
+ const nsSize finalContainerSize = aDesiredSize.PhysicalSize();
+ for (nsIFrame* child : mFrames) {
+ // Get the logical position as set previously using a provisional or
+ // dummy containerSize, and reset with the correct container size.
+ child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize),
+ finalContainerSize);
+ }
+ }
+
+#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);
+}
+
+void
+nsColumnSetFrame::DrainOverflowColumns()
+{
+ // First grab the prev-in-flows overflows and reparent them to this
+ // frame.
+ nsPresContext* presContext = PresContext();
+ nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
+ if (prev) {
+ AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
+ if (overflows) {
+ nsContainerFrame::ReparentFrameViewList(*overflows, prev, this);
+
+ mFrames.InsertFrames(this, nullptr, *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.
+ AutoFrameListPtr overflows(presContext, StealOverflowFrames());
+ if (overflows) {
+ // We're already the parent for these frames, so no need to set
+ // their parent again.
+ mFrames.AppendFrames(nullptr, *overflows);
+ }
+}
+
+void
+nsColumnSetFrame::FindBestBalanceBSize(const ReflowInput& aReflowInput,
+ nsPresContext* aPresContext,
+ ReflowConfig& aConfig,
+ ColumnBalanceData& aColData,
+ ReflowOutput& aDesiredSize,
+ nsCollapsingMargin& aOutMargin,
+ bool& aUnboundedLastColumn,
+ bool& aRunWasFeasible,
+ nsReflowStatus& aStatus)
+{
+ bool feasible = aRunWasFeasible;
+
+ nsMargin bp = aReflowInput.ComputedPhysicalBorderPadding();
+ bp.ApplySkipSides(GetSkipSides());
+ bp.bottom = aReflowInput.ComputedPhysicalBorderPadding().bottom;
+
+ nscoord availableContentBSize =
+ GetAvailableContentBSize(aReflowInput);
+
+ // Termination of the algorithm below is guaranteed because
+ // aConfig.knownFeasibleBSize - aConfig.knownInfeasibleBSize decreases in every
+ // iteration.
+
+ // We set this flag when we detect that we may contain a frame
+ // that can break anywhere (thus foiling the linear decrease-by-one
+ // search)
+ bool maybeContinuousBreakingDetected = false;
+
+ while (!aPresContext->HasPendingInterrupt()) {
+ nscoord lastKnownFeasibleBSize = aConfig.mKnownFeasibleBSize;
+
+ // Record what we learned from the last reflow
+ if (feasible) {
+ // maxBSize is feasible. Also, mLastBalanceBSize is feasible.
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ aColData.mMaxBSize);
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ mLastBalanceBSize);
+
+ // Furthermore, no height less than the height of the last
+ // column can ever be feasible. (We might be able to reduce the
+ // height of a non-last column by moving content to a later column,
+ // but we can't do that with the last column.)
+ if (mFrames.GetLength() == aConfig.mBalanceColCount) {
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ aColData.mLastBSize - 1);
+ }
+ } else {
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ mLastBalanceBSize);
+ // If a column didn't fit in its available height, then its current
+ // height must be the minimum height for unbreakable content in
+ // the column, and therefore no smaller height can be feasible.
+ aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
+ aColData.mMaxOverflowingBSize - 1);
+
+ if (aUnboundedLastColumn) {
+ // The last column is unbounded, so all content got reflowed, so the
+ // mColMaxBSize is feasible.
+ aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
+ aColData.mMaxBSize);
+ }
+ }
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
+ aConfig.mKnownInfeasibleBSize, aConfig.mKnownFeasibleBSize);
+#endif
+
+
+ if (aConfig.mKnownInfeasibleBSize >= aConfig.mKnownFeasibleBSize - 1) {
+ // aConfig.mKnownFeasibleBSize is where we want to be
+ break;
+ }
+
+ if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
+ break;
+ }
+
+ if (lastKnownFeasibleBSize - aConfig.mKnownFeasibleBSize == 1) {
+ // We decreased the feasible height by one twip only. This could
+ // indicate that there is a continuously breakable child frame
+ // that we are crawling through.
+ maybeContinuousBreakingDetected = true;
+ }
+
+ nscoord nextGuess = (aConfig.mKnownFeasibleBSize + aConfig.mKnownInfeasibleBSize)/2;
+ // The constant of 600 twips is arbitrary. It's about two line-heights.
+ if (aConfig.mKnownFeasibleBSize - nextGuess < 600 &&
+ !maybeContinuousBreakingDetected) {
+ // 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 = aConfig.mKnownFeasibleBSize - 1;
+ } else if (aUnboundedLastColumn) {
+ // 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 = aColData.mSumBSize/aConfig.mBalanceColCount + 600;
+ // Sanitize it
+ nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleBSize + 1,
+ aConfig.mKnownFeasibleBSize - 1);
+ } else if (aConfig.mKnownFeasibleBSize == 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 = aConfig.mKnownInfeasibleBSize*2 + 600;
+ }
+ // Don't bother guessing more than our height constraint.
+ nextGuess = std::min(availableContentBSize, nextGuess);
+
+#ifdef DEBUG_roc
+ printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
+#endif
+
+ aConfig.mColMaxBSize = nextGuess;
+
+ aUnboundedLastColumn = false;
+ AddStateBits(NS_FRAME_IS_DIRTY);
+ feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig, false,
+ &aOutMargin, aColData);
+
+ if (!aConfig.mIsBalancing) {
+ // Looks like we had excess height when balancing, so we gave up on
+ // trying to balance.
+ break;
+ }
+ }
+
+ if (aConfig.mIsBalancing && !feasible &&
+ !aPresContext->HasPendingInterrupt()) {
+ // We may need to reflow one more time at the feasible height to
+ // get a valid layout.
+ bool skip = false;
+ if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
+ aConfig.mColMaxBSize = availableContentBSize;
+ if (mLastBalanceBSize == availableContentBSize) {
+ skip = true;
+ }
+ } else {
+ aConfig.mColMaxBSize = aConfig.mKnownFeasibleBSize;
+ }
+ if (!skip) {
+ // If our height is unconstrained, make sure that the last column is
+ // allowed to have arbitrary height here, even though we were balancing.
+ // Otherwise we'd have to split, and it's not clear what we'd do with
+ // that.
+ AddStateBits(NS_FRAME_IS_DIRTY);
+ feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig,
+ availableContentBSize == NS_UNCONSTRAINEDSIZE,
+ &aOutMargin, aColData);
+ }
+ }
+
+ aRunWasFeasible = feasible;
+}
+
+void
+nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ // Don't support interruption in columns
+ nsPresContext::InterruptPreventer noInterrupts(aPresContext);
+
+ DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame");
+ DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
+
+ // Initialize OUT parameter
+ aStatus = NS_FRAME_COMPLETE;
+
+ // Our children depend on our block-size if we have a fixed block-size.
+ if (aReflowInput.ComputedBSize() != NS_AUTOHEIGHT) {
+ AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ } else {
+ RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
+ }
+
+#ifdef DEBUG
+ nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList));
+ for (; !oc.AtEnd(); oc.Next()) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get()));
+ }
+ nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList));
+ for (; !eoc.AtEnd(); eoc.Next()) {
+ MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get()));
+ }
+#endif
+
+ nsOverflowAreas ocBounds;
+ nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
+ if (GetPrevInFlow()) {
+ ReflowOverflowContainerChildren(aPresContext, aReflowInput, ocBounds, 0,
+ ocStatus);
+ }
+
+ //------------ Handle Incremental Reflow -----------------
+
+ // If inline size is unconstrained, set aForceAuto to true to allow
+ // the columns to expand in the inline direction. (This typically
+ // happens in orthogonal flows where the inline direction is the
+ // container's block direction).
+ ReflowConfig config =
+ ChooseColumnStrategy(aReflowInput,
+ aReflowInput.ComputedISize() == NS_UNCONSTRAINEDSIZE);
+
+ // 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();
+ bool unboundedLastColumn = config.mIsBalancing && !nextInFlow;
+ nsCollapsingMargin carriedOutBottomMargin;
+ ColumnBalanceData colData;
+ colData.mHasExcessBSize = false;
+
+ bool feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, config,
+ unboundedLastColumn, &carriedOutBottomMargin,
+ colData);
+
+ // If we're not balancing, then we're already done, since we should have
+ // reflown all of our children, and there is no need for a binary search to
+ // determine proper column height.
+ if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) {
+ FindBestBalanceBSize(aReflowInput, aPresContext, config, colData,
+ aDesiredSize, carriedOutBottomMargin,
+ unboundedLastColumn, feasible, aStatus);
+ }
+
+ if (aPresContext->HasPendingInterrupt() &&
+ aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
+ // In this situation, we might be lying about our reflow status, because
+ // our last kid (the one that got interrupted) was incomplete. Fix that.
+ aStatus = NS_FRAME_COMPLETE;
+ }
+
+ NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
+ aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
+ "Column set should be complete if the available block-size is unconstrained");
+
+ // Merge overflow container bounds and status.
+ aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
+ NS_MergeReflowStatusInto(&aStatus, ocStatus);
+
+ FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus, false);
+
+ aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin;
+
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+void
+nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ DisplayBorderBackgroundOutline(aBuilder, aLists);
+
+ if (IsVisibleForPainting(aBuilder)) {
+ aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+ nsDisplayGenericOverflow(aBuilder, this, ::PaintColumnRule, "ColumnRule",
+ nsDisplayItem::TYPE_COLUMN_RULE));
+ }
+
+ // Our children won't have backgrounds so it doesn't matter where we put them.
+ for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+ BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, aLists);
+ }
+}
+
+#ifdef DEBUG
+void
+nsColumnSetFrame::SetInitialChildList(ChildListID aListID,
+ nsFrameList& aChildList)
+{
+ MOZ_ASSERT(aListID != kPrincipalList || aChildList.OnlyChild(),
+ "initial principal child list must have exactly one child");
+ nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList);
+}
+
+void
+nsColumnSetFrame::AppendFrames(ChildListID aListID,
+ nsFrameList& aFrameList)
+{
+ MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetFrame::InsertFrames(ChildListID aListID,
+ nsIFrame* aPrevFrame,
+ nsFrameList& aFrameList)
+{
+ MOZ_CRASH("unsupported operation");
+}
+
+void
+nsColumnSetFrame::RemoveFrame(ChildListID aListID,
+ nsIFrame* aOldFrame)
+{
+ MOZ_CRASH("unsupported operation");
+}
+#endif