1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsColumnSetFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1079 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* rendering object for css3 multi-column layout */ 1.10 + 1.11 +#include "nsColumnSetFrame.h" 1.12 +#include "nsCSSRendering.h" 1.13 +#include "nsDisplayList.h" 1.14 + 1.15 +using namespace mozilla; 1.16 +using namespace mozilla::layout; 1.17 + 1.18 +/** 1.19 + * Tracking issues: 1.20 + * 1.21 + * XXX cursor movement around the top and bottom of colums seems to make the editor 1.22 + * lose the caret. 1.23 + * 1.24 + * XXX should we support CSS columns applied to table elements? 1.25 + */ 1.26 +nsIFrame* 1.27 +NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags) 1.28 +{ 1.29 + nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame(aContext); 1.30 + it->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT); 1.31 + return it; 1.32 +} 1.33 + 1.34 +NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetFrame) 1.35 + 1.36 +nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext) 1.37 + : nsContainerFrame(aContext), mLastBalanceHeight(NS_INTRINSICSIZE), 1.38 + mLastFrameStatus(NS_FRAME_COMPLETE) 1.39 +{ 1.40 +} 1.41 + 1.42 +nsIAtom* 1.43 +nsColumnSetFrame::GetType() const 1.44 +{ 1.45 + return nsGkAtoms::columnSetFrame; 1.46 +} 1.47 + 1.48 +static void 1.49 +PaintColumnRule(nsIFrame* aFrame, nsRenderingContext* aCtx, 1.50 + const nsRect& aDirtyRect, nsPoint aPt) 1.51 +{ 1.52 + static_cast<nsColumnSetFrame*>(aFrame)->PaintColumnRule(aCtx, aDirtyRect, aPt); 1.53 +} 1.54 + 1.55 +void 1.56 +nsColumnSetFrame::PaintColumnRule(nsRenderingContext* aCtx, 1.57 + const nsRect& aDirtyRect, 1.58 + const nsPoint& aPt) 1.59 +{ 1.60 + nsIFrame* child = mFrames.FirstChild(); 1.61 + if (!child) 1.62 + return; // no columns 1.63 + 1.64 + nsIFrame* nextSibling = child->GetNextSibling(); 1.65 + if (!nextSibling) 1.66 + return; // 1 column only - this means no gap to draw on 1.67 + 1.68 + bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; 1.69 + const nsStyleColumn* colStyle = StyleColumn(); 1.70 + 1.71 + uint8_t ruleStyle; 1.72 + // Per spec, inset => ridge and outset => groove 1.73 + if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET) 1.74 + ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE; 1.75 + else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET) 1.76 + ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE; 1.77 + else 1.78 + ruleStyle = colStyle->mColumnRuleStyle; 1.79 + 1.80 + nsPresContext* presContext = PresContext(); 1.81 + nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth(); 1.82 + if (!ruleWidth) 1.83 + return; 1.84 + 1.85 + nscolor ruleColor = 1.86 + GetVisitedDependentColor(eCSSProperty__moz_column_rule_color); 1.87 + 1.88 + // In order to re-use a large amount of code, we treat the column rule as a border. 1.89 + // We create a new border style object and fill in all the details of the column rule as 1.90 + // the left border. PaintBorder() does all the rendering for us, so we not 1.91 + // only save an enormous amount of code but we'll support all the line styles that 1.92 + // we support on borders! 1.93 + nsStyleBorder border(presContext); 1.94 + border.SetBorderWidth(NS_SIDE_LEFT, ruleWidth); 1.95 + border.SetBorderStyle(NS_SIDE_LEFT, ruleStyle); 1.96 + border.SetBorderColor(NS_SIDE_LEFT, ruleColor); 1.97 + 1.98 + // Get our content rect as an absolute coordinate, not relative to 1.99 + // our parent (which is what the X and Y normally is) 1.100 + nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt; 1.101 + nsSize ruleSize(ruleWidth, contentRect.height); 1.102 + 1.103 + while (nextSibling) { 1.104 + // The frame tree goes RTL in RTL 1.105 + nsIFrame* leftSibling = isRTL ? nextSibling : child; 1.106 + nsIFrame* rightSibling = isRTL ? child : nextSibling; 1.107 + 1.108 + // Each child frame's position coordinates is actually relative to this nsColumnSetFrame. 1.109 + // linePt will be at the top-left edge to paint the line. 1.110 + nsPoint edgeOfLeftSibling = leftSibling->GetRect().TopRight() + aPt; 1.111 + nsPoint edgeOfRightSibling = rightSibling->GetRect().TopLeft() + aPt; 1.112 + nsPoint linePt((edgeOfLeftSibling.x + edgeOfRightSibling.x - ruleWidth) / 2, 1.113 + contentRect.y); 1.114 + 1.115 + nsRect lineRect(linePt, ruleSize); 1.116 + nsCSSRendering::PaintBorderWithStyleBorder(presContext, *aCtx, this, 1.117 + aDirtyRect, lineRect, border, StyleContext(), 1.118 + // Remember, we only have the "left" "border". Skip everything else 1.119 + (1 << NS_SIDE_TOP | 1 << NS_SIDE_RIGHT | 1 << NS_SIDE_BOTTOM)); 1.120 + 1.121 + child = nextSibling; 1.122 + nextSibling = nextSibling->GetNextSibling(); 1.123 + } 1.124 +} 1.125 + 1.126 +nsresult 1.127 +nsColumnSetFrame::SetInitialChildList(ChildListID aListID, 1.128 + nsFrameList& aChildList) 1.129 +{ 1.130 + if (aListID == kAbsoluteList) { 1.131 + return nsContainerFrame::SetInitialChildList(aListID, aChildList); 1.132 + } 1.133 + 1.134 + NS_ASSERTION(aListID == kPrincipalList, 1.135 + "Only default child list supported"); 1.136 + NS_ASSERTION(aChildList.OnlyChild(), 1.137 + "initial child list must have exaisRevertingctly one child"); 1.138 + // Queue up the frames for the content frame 1.139 + return nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList); 1.140 +} 1.141 + 1.142 +static nscoord 1.143 +GetAvailableContentWidth(const nsHTMLReflowState& aReflowState) 1.144 +{ 1.145 + if (aReflowState.AvailableWidth() == NS_INTRINSICSIZE) { 1.146 + return NS_INTRINSICSIZE; 1.147 + } 1.148 + nscoord borderPaddingWidth = 1.149 + aReflowState.ComputedPhysicalBorderPadding().left + 1.150 + aReflowState.ComputedPhysicalBorderPadding().right; 1.151 + return std::max(0, aReflowState.AvailableWidth() - borderPaddingWidth); 1.152 +} 1.153 + 1.154 +nscoord 1.155 +nsColumnSetFrame::GetAvailableContentHeight(const nsHTMLReflowState& aReflowState) 1.156 +{ 1.157 + if (aReflowState.AvailableHeight() == NS_INTRINSICSIZE) { 1.158 + return NS_INTRINSICSIZE; 1.159 + } 1.160 + 1.161 + nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); 1.162 + ApplySkipSides(bp, &aReflowState); 1.163 + bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom; 1.164 + return std::max(0, aReflowState.AvailableHeight() - bp.TopBottom()); 1.165 +} 1.166 + 1.167 +static nscoord 1.168 +GetColumnGap(nsColumnSetFrame* aFrame, 1.169 + const nsStyleColumn* aColStyle) 1.170 +{ 1.171 + if (eStyleUnit_Normal == aColStyle->mColumnGap.GetUnit()) 1.172 + return aFrame->StyleFont()->mFont.size; 1.173 + if (eStyleUnit_Coord == aColStyle->mColumnGap.GetUnit()) { 1.174 + nscoord colGap = aColStyle->mColumnGap.GetCoordValue(); 1.175 + NS_ASSERTION(colGap >= 0, "negative column gap"); 1.176 + return colGap; 1.177 + } 1.178 + 1.179 + NS_NOTREACHED("Unknown gap type"); 1.180 + return 0; 1.181 +} 1.182 + 1.183 +nsColumnSetFrame::ReflowConfig 1.184 +nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, 1.185 + bool aForceAuto = false, 1.186 + nscoord aFeasibleHeight = NS_INTRINSICSIZE, 1.187 + nscoord aInfeasibleHeight = 0) 1.188 + 1.189 +{ 1.190 + nscoord knownFeasibleHeight = aFeasibleHeight; 1.191 + nscoord knownInfeasibleHeight = aInfeasibleHeight; 1.192 + 1.193 + const nsStyleColumn* colStyle = StyleColumn(); 1.194 + nscoord availContentWidth = GetAvailableContentWidth(aReflowState); 1.195 + if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { 1.196 + availContentWidth = aReflowState.ComputedWidth(); 1.197 + } 1.198 + 1.199 + nscoord consumedHeight = GetConsumedHeight(); 1.200 + 1.201 + // The effective computed height is the height of the current continuation 1.202 + // of the column set frame. This should be the same as the computed height 1.203 + // if we have an unconstrained available height. 1.204 + nscoord computedHeight = GetEffectiveComputedHeight(aReflowState, 1.205 + consumedHeight); 1.206 + nscoord colHeight = GetAvailableContentHeight(aReflowState); 1.207 + 1.208 + if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) { 1.209 + colHeight = aReflowState.ComputedHeight(); 1.210 + } else if (aReflowState.ComputedMaxHeight() != NS_INTRINSICSIZE) { 1.211 + colHeight = std::min(colHeight, aReflowState.ComputedMaxHeight()); 1.212 + } 1.213 + 1.214 + nscoord colGap = GetColumnGap(this, colStyle); 1.215 + int32_t numColumns = colStyle->mColumnCount; 1.216 + 1.217 + // If column-fill is set to 'balance', then we want to balance the columns. 1.218 + const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE 1.219 + && !aForceAuto; 1.220 + if (isBalancing) { 1.221 + const uint32_t MAX_NESTED_COLUMN_BALANCING = 2; 1.222 + uint32_t cnt = 0; 1.223 + for (const nsHTMLReflowState* rs = aReflowState.parentReflowState; 1.224 + rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->parentReflowState) { 1.225 + if (rs->mFlags.mIsColumnBalancing) { 1.226 + ++cnt; 1.227 + } 1.228 + } 1.229 + if (cnt == MAX_NESTED_COLUMN_BALANCING) { 1.230 + numColumns = 1; 1.231 + } 1.232 + } 1.233 + 1.234 + nscoord colWidth; 1.235 + if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) { 1.236 + colWidth = colStyle->mColumnWidth.GetCoordValue(); 1.237 + NS_ASSERTION(colWidth >= 0, "negative column width"); 1.238 + // Reduce column count if necessary to make columns fit in the 1.239 + // available width. Compute max number of columns that fit in 1.240 + // availContentWidth, satisfying colGap*(maxColumns - 1) + 1.241 + // colWidth*maxColumns <= availContentWidth 1.242 + if (availContentWidth != NS_INTRINSICSIZE && colGap + colWidth > 0 1.243 + && numColumns > 0) { 1.244 + // This expression uses truncated rounding, which is what we 1.245 + // want 1.246 + int32_t maxColumns = 1.247 + std::min(nscoord(nsStyleColumn::kMaxColumnCount), 1.248 + (availContentWidth + colGap)/(colGap + colWidth)); 1.249 + numColumns = std::max(1, std::min(numColumns, maxColumns)); 1.250 + } 1.251 + } else if (numColumns > 0 && availContentWidth != NS_INTRINSICSIZE) { 1.252 + nscoord widthMinusGaps = availContentWidth - colGap*(numColumns - 1); 1.253 + colWidth = widthMinusGaps/numColumns; 1.254 + } else { 1.255 + colWidth = NS_INTRINSICSIZE; 1.256 + } 1.257 + // Take care of the situation where there's only one column but it's 1.258 + // still too wide 1.259 + colWidth = std::max(1, std::min(colWidth, availContentWidth)); 1.260 + 1.261 + nscoord expectedWidthLeftOver = 0; 1.262 + 1.263 + if (colWidth != NS_INTRINSICSIZE && availContentWidth != NS_INTRINSICSIZE) { 1.264 + // distribute leftover space 1.265 + 1.266 + // First, determine how many columns will be showing if the column 1.267 + // count is auto 1.268 + if (numColumns <= 0) { 1.269 + // choose so that colGap*(nominalColumnCount - 1) + 1.270 + // colWidth*nominalColumnCount is nearly availContentWidth 1.271 + // make sure to round down 1.272 + if (colGap + colWidth > 0) { 1.273 + numColumns = (availContentWidth + colGap)/(colGap + colWidth); 1.274 + // The number of columns should never exceed kMaxColumnCount. 1.275 + numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount), 1.276 + numColumns); 1.277 + } 1.278 + if (numColumns <= 0) { 1.279 + numColumns = 1; 1.280 + } 1.281 + } 1.282 + 1.283 + // Compute extra space and divide it among the columns 1.284 + nscoord extraSpace = 1.285 + std::max(0, availContentWidth - (colWidth*numColumns + colGap*(numColumns - 1))); 1.286 + nscoord extraToColumns = extraSpace/numColumns; 1.287 + colWidth += extraToColumns; 1.288 + expectedWidthLeftOver = extraSpace - (extraToColumns*numColumns); 1.289 + } 1.290 + 1.291 + if (isBalancing) { 1.292 + if (numColumns <= 0) { 1.293 + // Hmm, auto column count, column width or available width is unknown, 1.294 + // and balancing is required. Let's just use one column then. 1.295 + numColumns = 1; 1.296 + } 1.297 + colHeight = std::min(mLastBalanceHeight, colHeight); 1.298 + } else { 1.299 + // This is the case when the column-fill property is set to 'auto'. 1.300 + // No balancing, so don't limit the column count 1.301 + numColumns = INT32_MAX; 1.302 + 1.303 + // XXX_jwir3: If a page's height is set to 0, we could continually 1.304 + // create continuations, resulting in an infinite loop, since 1.305 + // no progress is ever made. This is an issue with the spec 1.306 + // (css3-multicol, css3-page, and css3-break) that is 1.307 + // unresolved as of 27 Feb 2013. For the time being, we set this 1.308 + // to have a minimum of 1 css px. Once a resolution is made 1.309 + // on what minimum to have for a page height, we may need to 1.310 + // change this value to match the appropriate spec(s). 1.311 + colHeight = std::max(colHeight, nsPresContext::CSSPixelsToAppUnits(1)); 1.312 + } 1.313 + 1.314 +#ifdef DEBUG_roc 1.315 + printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colWidth=%d, expectedWidthLeftOver=%d, colHeight=%d, colGap=%d\n", 1.316 + numColumns, colWidth, expectedWidthLeftOver, colHeight, colGap); 1.317 +#endif 1.318 + ReflowConfig config = { numColumns, colWidth, expectedWidthLeftOver, colGap, 1.319 + colHeight, isBalancing, knownFeasibleHeight, 1.320 + knownInfeasibleHeight, computedHeight, consumedHeight }; 1.321 + return config; 1.322 +} 1.323 + 1.324 +bool 1.325 +nsColumnSetFrame::ReflowColumns(nsHTMLReflowMetrics& aDesiredSize, 1.326 + const nsHTMLReflowState& aReflowState, 1.327 + nsReflowStatus& aReflowStatus, 1.328 + ReflowConfig& aConfig, 1.329 + bool aLastColumnUnbounded, 1.330 + nsCollapsingMargin* aCarriedOutBottomMargin, 1.331 + ColumnBalanceData& aColData) 1.332 +{ 1.333 + bool feasible = ReflowChildren(aDesiredSize, aReflowState, 1.334 + aReflowStatus, aConfig, aLastColumnUnbounded, 1.335 + aCarriedOutBottomMargin, aColData); 1.336 + 1.337 + if (aColData.mHasExcessHeight) { 1.338 + aConfig = ChooseColumnStrategy(aReflowState, true); 1.339 + 1.340 + // We need to reflow our children again one last time, otherwise we might 1.341 + // end up with a stale column height for some of our columns, since we 1.342 + // bailed out of balancing. 1.343 + feasible = ReflowChildren(aDesiredSize, aReflowState, aReflowStatus, 1.344 + aConfig, aLastColumnUnbounded, 1.345 + aCarriedOutBottomMargin, aColData); 1.346 + } 1.347 + 1.348 + return feasible; 1.349 +} 1.350 + 1.351 +static void MoveChildTo(nsIFrame* aParent, nsIFrame* aChild, nsPoint aOrigin) { 1.352 + if (aChild->GetPosition() == aOrigin) { 1.353 + return; 1.354 + } 1.355 + 1.356 + aChild->SetPosition(aOrigin); 1.357 + nsContainerFrame::PlaceFrameView(aChild); 1.358 +} 1.359 + 1.360 +nscoord 1.361 +nsColumnSetFrame::GetMinWidth(nsRenderingContext *aRenderingContext) { 1.362 + nscoord width = 0; 1.363 + DISPLAY_MIN_WIDTH(this, width); 1.364 + if (mFrames.FirstChild()) { 1.365 + width = mFrames.FirstChild()->GetMinWidth(aRenderingContext); 1.366 + } 1.367 + const nsStyleColumn* colStyle = StyleColumn(); 1.368 + nscoord colWidth; 1.369 + if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) { 1.370 + colWidth = colStyle->mColumnWidth.GetCoordValue(); 1.371 + // As available width reduces to zero, we reduce our number of columns 1.372 + // to one, and don't enforce the column width, so just return the min 1.373 + // of the child's min-width with any specified column width. 1.374 + width = std::min(width, colWidth); 1.375 + } else { 1.376 + NS_ASSERTION(colStyle->mColumnCount > 0, 1.377 + "column-count and column-width can't both be auto"); 1.378 + // As available width reduces to zero, we still have mColumnCount columns, 1.379 + // so multiply the child's min-width by the number of columns. 1.380 + colWidth = width; 1.381 + width *= colStyle->mColumnCount; 1.382 + // The multiplication above can make 'width' negative (integer overflow), 1.383 + // so use std::max to protect against that. 1.384 + width = std::max(width, colWidth); 1.385 + } 1.386 + // XXX count forced column breaks here? Maybe we should return the child's 1.387 + // min-width times the minimum number of columns. 1.388 + return width; 1.389 +} 1.390 + 1.391 +nscoord 1.392 +nsColumnSetFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) { 1.393 + // Our preferred width is our desired column width, if specified, otherwise 1.394 + // the child's preferred width, times the number of columns, plus the width 1.395 + // of any required column gaps 1.396 + // XXX what about forced column breaks here? 1.397 + nscoord result = 0; 1.398 + DISPLAY_PREF_WIDTH(this, result); 1.399 + const nsStyleColumn* colStyle = StyleColumn(); 1.400 + nscoord colGap = GetColumnGap(this, colStyle); 1.401 + 1.402 + nscoord colWidth; 1.403 + if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) { 1.404 + colWidth = colStyle->mColumnWidth.GetCoordValue(); 1.405 + } else if (mFrames.FirstChild()) { 1.406 + colWidth = mFrames.FirstChild()->GetPrefWidth(aRenderingContext); 1.407 + } else { 1.408 + colWidth = 0; 1.409 + } 1.410 + 1.411 + int32_t numColumns = colStyle->mColumnCount; 1.412 + if (numColumns <= 0) { 1.413 + // if column-count is auto, assume one column 1.414 + numColumns = 1; 1.415 + } 1.416 + 1.417 + nscoord width = colWidth*numColumns + colGap*(numColumns - 1); 1.418 + // The multiplication above can make 'width' negative (integer overflow), 1.419 + // so use std::max to protect against that. 1.420 + result = std::max(width, colWidth); 1.421 + return result; 1.422 +} 1.423 + 1.424 +bool 1.425 +nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, 1.426 + const nsHTMLReflowState& aReflowState, 1.427 + nsReflowStatus& aStatus, 1.428 + const ReflowConfig& aConfig, 1.429 + bool aUnboundedLastColumn, 1.430 + nsCollapsingMargin* aBottomMarginCarriedOut, 1.431 + ColumnBalanceData& aColData) 1.432 +{ 1.433 + aColData.Reset(); 1.434 + bool allFit = true; 1.435 + bool RTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; 1.436 + bool shrinkingHeightOnly = !NS_SUBTREE_DIRTY(this) && 1.437 + mLastBalanceHeight > aConfig.mColMaxHeight; 1.438 + 1.439 +#ifdef DEBUG_roc 1.440 + printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n", 1.441 + mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount, 1.442 + aConfig.mColWidth, aConfig.mColGap); 1.443 +#endif 1.444 + 1.445 + DrainOverflowColumns(); 1.446 + 1.447 + const bool colHeightChanged = mLastBalanceHeight != aConfig.mColMaxHeight; 1.448 + 1.449 + if (colHeightChanged) { 1.450 + mLastBalanceHeight = aConfig.mColMaxHeight; 1.451 + // XXX Seems like this could fire if incremental reflow pushed the column set 1.452 + // down so we reflow incrementally with a different available height. 1.453 + // We need a way to do an incremental reflow and be sure availableHeight 1.454 + // changes are taken account of! Right now I think block frames with absolute 1.455 + // children might exit early. 1.456 + //NS_ASSERTION(aKidReason != eReflowReason_Incremental, 1.457 + // "incremental reflow should not have changed the balance height"); 1.458 + } 1.459 + 1.460 + // get our border and padding 1.461 + nsMargin borderPadding = aReflowState.ComputedPhysicalBorderPadding(); 1.462 + ApplySkipSides(borderPadding, &aReflowState); 1.463 + 1.464 + nsRect contentRect(0, 0, 0, 0); 1.465 + nsOverflowAreas overflowRects; 1.466 + 1.467 + nsIFrame* child = mFrames.FirstChild(); 1.468 + nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top); 1.469 + // For RTL, figure out where the last column's left edge should be. Since the 1.470 + // columns might not fill the frame exactly, we need to account for the 1.471 + // slop. Otherwise we'll waste time moving the columns by some tiny 1.472 + // amount unnecessarily. 1.473 + if (RTL) { 1.474 + nscoord availWidth = aReflowState.AvailableWidth(); 1.475 + if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { 1.476 + availWidth = aReflowState.ComputedWidth(); 1.477 + } 1.478 + if (availWidth != NS_INTRINSICSIZE) { 1.479 + childOrigin.x += availWidth - aConfig.mColWidth; 1.480 +#ifdef DEBUG_roc 1.481 + printf("*** childOrigin.x = %d\n", childOrigin.x); 1.482 +#endif 1.483 + } 1.484 + } 1.485 + int columnCount = 0; 1.486 + int contentBottom = 0; 1.487 + bool reflowNext = false; 1.488 + 1.489 + while (child) { 1.490 + // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't 1.491 + // skip if the next column is dirty, because the next column's first line(s) 1.492 + // might be pullable back to this column. We can't skip if it's the last child 1.493 + // because we need to obtain the bottom margin. We can't skip 1.494 + // if this is the last column and we're supposed to assign unbounded 1.495 + // height to it, because that could change the available height from 1.496 + // the last time we reflowed it and we should try to pull all the 1.497 + // content from its next sibling. (Note that it might be the last 1.498 + // column, but not be the last child because the desired number of columns 1.499 + // has changed.) 1.500 + bool skipIncremental = !aReflowState.ShouldReflowAllKids() 1.501 + && !NS_SUBTREE_DIRTY(child) 1.502 + && child->GetNextSibling() 1.503 + && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) 1.504 + && !NS_SUBTREE_DIRTY(child->GetNextSibling()); 1.505 + // If we need to pull up content from the prev-in-flow then this is not just 1.506 + // a height shrink. The prev in flow will have set the dirty bit. 1.507 + // Check the overflow rect YMost instead of just the child's content height. The child 1.508 + // may have overflowing content that cares about the available height boundary. 1.509 + // (It may also have overflowing content that doesn't care about the available height 1.510 + // boundary, but if so, too bad, this optimization is defeated.) 1.511 + // We want scrollable overflow here since this is a calculation that 1.512 + // affects layout. 1.513 + bool skipResizeHeightShrink = shrinkingHeightOnly 1.514 + && child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight; 1.515 + 1.516 + nscoord childContentBottom = 0; 1.517 + if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) { 1.518 + // This child does not need to be reflowed, but we may need to move it 1.519 + MoveChildTo(this, child, childOrigin); 1.520 + 1.521 + // If this is the last frame then make sure we get the right status 1.522 + nsIFrame* kidNext = child->GetNextSibling(); 1.523 + if (kidNext) { 1.524 + aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) 1.525 + ? NS_FRAME_OVERFLOW_INCOMPLETE 1.526 + : NS_FRAME_NOT_COMPLETE; 1.527 + } else { 1.528 + aStatus = mLastFrameStatus; 1.529 + } 1.530 + childContentBottom = nsLayoutUtils::CalculateContentBottom(child); 1.531 +#ifdef DEBUG_roc 1.532 + printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n", 1.533 + columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus); 1.534 +#endif 1.535 + } else { 1.536 + nsSize availSize(aConfig.mColWidth, aConfig.mColMaxHeight); 1.537 + 1.538 + if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { 1.539 + availSize.height = GetAvailableContentHeight(aReflowState); 1.540 + } 1.541 + 1.542 + if (reflowNext) 1.543 + child->AddStateBits(NS_FRAME_IS_DIRTY); 1.544 + 1.545 + nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, 1.546 + availSize, availSize.width, 1.547 + aReflowState.ComputedHeight()); 1.548 + kidReflowState.mFlags.mIsTopOfPage = true; 1.549 + kidReflowState.mFlags.mTableIsSplittable = false; 1.550 + kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; 1.551 + 1.552 + // We need to reflow any float placeholders, even if our column height 1.553 + // hasn't changed. 1.554 + kidReflowState.mFlags.mMustReflowPlaceholders = !colHeightChanged; 1.555 + 1.556 +#ifdef DEBUG_roc 1.557 + printf("*** Reflowing child #%d %p: availHeight=%d\n", 1.558 + columnCount, (void*)child,availSize.height); 1.559 +#endif 1.560 + 1.561 + // Note if the column's next in flow is not being changed by this incremental reflow. 1.562 + // This may allow the current column to avoid trying to pull lines from the next column. 1.563 + if (child->GetNextSibling() && 1.564 + !(GetStateBits() & NS_FRAME_IS_DIRTY) && 1.565 + !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { 1.566 + kidReflowState.mFlags.mNextInFlowUntouched = true; 1.567 + } 1.568 + 1.569 + nsHTMLReflowMetrics kidDesiredSize(aReflowState.GetWritingMode(), 1.570 + aDesiredSize.mFlags); 1.571 + 1.572 + // XXX it would be cool to consult the float manager for the 1.573 + // previous block to figure out the region of floats from the 1.574 + // previous column that extend into this column, and subtract 1.575 + // that region from the new float manager. So you could stick a 1.576 + // really big float in the first column and text in following 1.577 + // columns would flow around it. 1.578 + 1.579 + // Reflow the frame 1.580 + ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, 1.581 + childOrigin.x + kidReflowState.ComputedPhysicalMargin().left, 1.582 + childOrigin.y + kidReflowState.ComputedPhysicalMargin().top, 1.583 + 0, aStatus); 1.584 + 1.585 + reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; 1.586 + 1.587 +#ifdef DEBUG_roc 1.588 + printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBottomMargin=%d\n", 1.589 + columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), 1.590 + kidDesiredSize.mCarriedOutBottomMargin.get()); 1.591 +#endif 1.592 + 1.593 + NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); 1.594 + 1.595 + *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBottomMargin; 1.596 + 1.597 + FinishReflowChild(child, PresContext(), kidDesiredSize, 1.598 + &kidReflowState, childOrigin.x, childOrigin.y, 0); 1.599 + 1.600 + childContentBottom = nsLayoutUtils::CalculateContentBottom(child); 1.601 + if (childContentBottom > aConfig.mColMaxHeight) { 1.602 + allFit = false; 1.603 + } 1.604 + if (childContentBottom > availSize.height) { 1.605 + aColData.mMaxOverflowingHeight = std::max(childContentBottom, 1.606 + aColData.mMaxOverflowingHeight); 1.607 + } 1.608 + } 1.609 + 1.610 + contentRect.UnionRect(contentRect, child->GetRect()); 1.611 + 1.612 + ConsiderChildOverflow(overflowRects, child); 1.613 + contentBottom = std::max(contentBottom, childContentBottom); 1.614 + aColData.mLastHeight = childContentBottom; 1.615 + aColData.mSumHeight += childContentBottom; 1.616 + 1.617 + // Build a continuation column if necessary 1.618 + nsIFrame* kidNextInFlow = child->GetNextInFlow(); 1.619 + 1.620 + if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { 1.621 + NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); 1.622 + child = nullptr; 1.623 + break; 1.624 + } else { 1.625 + ++columnCount; 1.626 + // Make sure that the column has a next-in-flow. If not, we must 1.627 + // create one to hold the overflowing stuff, even if we're just 1.628 + // going to put it on our overflow list and let *our* 1.629 + // next in flow handle it. 1.630 + if (!kidNextInFlow) { 1.631 + NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW, 1.632 + "We have to create a continuation, but the block doesn't want us to reflow it?"); 1.633 + 1.634 + // We need to create a continuing column 1.635 + nsresult rv = CreateNextInFlow(child, kidNextInFlow); 1.636 + 1.637 + if (NS_FAILED(rv)) { 1.638 + NS_NOTREACHED("Couldn't create continuation"); 1.639 + child = nullptr; 1.640 + break; 1.641 + } 1.642 + } 1.643 + 1.644 + // Make sure we reflow a next-in-flow when it switches between being 1.645 + // normal or overflow container 1.646 + if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { 1.647 + if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.648 + aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.649 + reflowNext = true; 1.650 + kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1.651 + } 1.652 + } 1.653 + else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { 1.654 + aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.655 + reflowNext = true; 1.656 + kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1.657 + } 1.658 + 1.659 + if ((contentBottom > aReflowState.ComputedMaxHeight() || 1.660 + contentBottom > aReflowState.ComputedHeight()) && 1.661 + aConfig.mBalanceColCount < INT32_MAX) { 1.662 + // We overflowed vertically, but have not exceeded the number of 1.663 + // columns. We're going to go into overflow columns now, so balancing 1.664 + // no longer applies. 1.665 + aColData.mHasExcessHeight = true; 1.666 + } 1.667 + 1.668 + if (columnCount >= aConfig.mBalanceColCount) { 1.669 + // No more columns allowed here. Stop. 1.670 + aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.671 + kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); 1.672 + // Move any of our leftover columns to our overflow list. Our 1.673 + // next-in-flow will eventually pick them up. 1.674 + const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); 1.675 + if (continuationColumns.NotEmpty()) { 1.676 + SetOverflowFrames(continuationColumns); 1.677 + } 1.678 + child = nullptr; 1.679 + break; 1.680 + } 1.681 + } 1.682 + 1.683 + if (PresContext()->HasPendingInterrupt()) { 1.684 + // Stop the loop now while |child| still points to the frame that bailed 1.685 + // out. We could keep going here and condition a bunch of the code in 1.686 + // this loop on whether there's an interrupt, or even just keep going and 1.687 + // trying to reflow the blocks (even though we know they'll interrupt 1.688 + // right after their first line), but stopping now is conceptually the 1.689 + // simplest (and probably fastest) thing. 1.690 + break; 1.691 + } 1.692 + 1.693 + // Advance to the next column 1.694 + child = child->GetNextSibling(); 1.695 + 1.696 + if (child) { 1.697 + if (!RTL) { 1.698 + childOrigin.x += aConfig.mColWidth + aConfig.mColGap; 1.699 + } else { 1.700 + childOrigin.x -= aConfig.mColWidth + aConfig.mColGap; 1.701 + } 1.702 + 1.703 +#ifdef DEBUG_roc 1.704 + printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x); 1.705 +#endif 1.706 + } 1.707 + } 1.708 + 1.709 + if (PresContext()->CheckForInterrupt(this) && 1.710 + (GetStateBits() & NS_FRAME_IS_DIRTY)) { 1.711 + // Mark all our kids starting with |child| dirty 1.712 + 1.713 + // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, 1.714 + // because we might have interrupted while reflowing |child|, and since 1.715 + // we're about to add a dirty bit to |child| we need to make sure that 1.716 + // |this| is scheduled to have dirty bits marked on it and its ancestors. 1.717 + // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll 1.718 + // bail out immediately, since it'll already have a dirty bit. 1.719 + for (; child; child = child->GetNextSibling()) { 1.720 + child->AddStateBits(NS_FRAME_IS_DIRTY); 1.721 + } 1.722 + } 1.723 + 1.724 + aColData.mMaxHeight = contentBottom; 1.725 + contentRect.height = std::max(contentRect.height, contentBottom); 1.726 + mLastFrameStatus = aStatus; 1.727 + 1.728 + // contentRect included the borderPadding.left,borderPadding.top of the child rects 1.729 + contentRect -= nsPoint(borderPadding.left, borderPadding.top); 1.730 + 1.731 + nsSize contentSize = nsSize(contentRect.XMost(), contentRect.YMost()); 1.732 + 1.733 + // Apply computed and min/max values 1.734 + if (aConfig.mComputedHeight != NS_INTRINSICSIZE) { 1.735 + if (aReflowState.AvailableHeight() != NS_INTRINSICSIZE) { 1.736 + contentSize.height = std::min(contentSize.height, 1.737 + aConfig.mComputedHeight); 1.738 + } else { 1.739 + contentSize.height = aConfig.mComputedHeight; 1.740 + } 1.741 + } else { 1.742 + // We add the "consumed" height back in so that we're applying 1.743 + // constraints to the correct height value, then subtract it again 1.744 + // after we've finished with the min/max calculation. This prevents us from 1.745 + // having a last continuation that is smaller than the min height. but which 1.746 + // has prev-in-flows, trigger a larger height than actually required. 1.747 + contentSize.height = aReflowState.ApplyMinMaxHeight(contentSize.height, 1.748 + aConfig.mConsumedHeight); 1.749 + } 1.750 + if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { 1.751 + contentSize.width = aReflowState.ComputedWidth(); 1.752 + } else { 1.753 + contentSize.width = aReflowState.ApplyMinMaxWidth(contentSize.width); 1.754 + } 1.755 + 1.756 + aDesiredSize.Height() = contentSize.height + 1.757 + borderPadding.TopBottom(); 1.758 + aDesiredSize.Width() = contentSize.width + 1.759 + borderPadding.LeftRight(); 1.760 + aDesiredSize.mOverflowAreas = overflowRects; 1.761 + aDesiredSize.UnionOverflowAreasWithDesiredBounds(); 1.762 + 1.763 +#ifdef DEBUG_roc 1.764 + printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) 1.765 + && !NS_FRAME_IS_TRUNCATED(aStatus)); 1.766 +#endif 1.767 + return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) 1.768 + && !NS_FRAME_IS_TRUNCATED(aStatus); 1.769 +} 1.770 + 1.771 +void 1.772 +nsColumnSetFrame::DrainOverflowColumns() 1.773 +{ 1.774 + // First grab the prev-in-flows overflows and reparent them to this 1.775 + // frame. 1.776 + nsPresContext* presContext = PresContext(); 1.777 + nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow()); 1.778 + if (prev) { 1.779 + AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames()); 1.780 + if (overflows) { 1.781 + nsContainerFrame::ReparentFrameViewList(*overflows, prev, this); 1.782 + 1.783 + mFrames.InsertFrames(this, nullptr, *overflows); 1.784 + } 1.785 + } 1.786 + 1.787 + // Now pull back our own overflows and append them to our children. 1.788 + // We don't need to reparent them since we're already their parent. 1.789 + AutoFrameListPtr overflows(presContext, StealOverflowFrames()); 1.790 + if (overflows) { 1.791 + // We're already the parent for these frames, so no need to set 1.792 + // their parent again. 1.793 + mFrames.AppendFrames(nullptr, *overflows); 1.794 + } 1.795 +} 1.796 + 1.797 +void 1.798 +nsColumnSetFrame::FindBestBalanceHeight(const nsHTMLReflowState& aReflowState, 1.799 + nsPresContext* aPresContext, 1.800 + ReflowConfig& aConfig, 1.801 + ColumnBalanceData& aColData, 1.802 + nsHTMLReflowMetrics& aDesiredSize, 1.803 + nsCollapsingMargin& aOutMargin, 1.804 + bool& aUnboundedLastColumn, 1.805 + bool& aRunWasFeasible, 1.806 + nsReflowStatus& aStatus) 1.807 +{ 1.808 + bool feasible = aRunWasFeasible; 1.809 + 1.810 + nsMargin bp = aReflowState.ComputedPhysicalBorderPadding(); 1.811 + ApplySkipSides(bp); 1.812 + bp.bottom = aReflowState.ComputedPhysicalBorderPadding().bottom; 1.813 + 1.814 + nscoord availableContentHeight = 1.815 + GetAvailableContentHeight(aReflowState); 1.816 + 1.817 + // Termination of the algorithm below is guaranteed because 1.818 + // aConfig.knownFeasibleHeight - aConfig.knownInfeasibleHeight decreases in every 1.819 + // iteration. 1.820 + 1.821 + // We set this flag when we detect that we may contain a frame 1.822 + // that can break anywhere (thus foiling the linear decrease-by-one 1.823 + // search) 1.824 + bool maybeContinuousBreakingDetected = false; 1.825 + 1.826 + while (!aPresContext->HasPendingInterrupt()) { 1.827 + nscoord lastKnownFeasibleHeight = aConfig.mKnownFeasibleHeight; 1.828 + 1.829 + // Record what we learned from the last reflow 1.830 + if (feasible) { 1.831 + // maxHeight is feasible. Also, mLastBalanceHeight is feasible. 1.832 + aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, 1.833 + aColData.mMaxHeight); 1.834 + aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, 1.835 + mLastBalanceHeight); 1.836 + 1.837 + // Furthermore, no height less than the height of the last 1.838 + // column can ever be feasible. (We might be able to reduce the 1.839 + // height of a non-last column by moving content to a later column, 1.840 + // but we can't do that with the last column.) 1.841 + if (mFrames.GetLength() == aConfig.mBalanceColCount) { 1.842 + aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, 1.843 + aColData.mLastHeight - 1); 1.844 + } 1.845 + } else { 1.846 + aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, 1.847 + mLastBalanceHeight); 1.848 + // If a column didn't fit in its available height, then its current 1.849 + // height must be the minimum height for unbreakable content in 1.850 + // the column, and therefore no smaller height can be feasible. 1.851 + aConfig.mKnownInfeasibleHeight = std::max(aConfig.mKnownInfeasibleHeight, 1.852 + aColData.mMaxOverflowingHeight - 1); 1.853 + 1.854 + if (aUnboundedLastColumn) { 1.855 + // The last column is unbounded, so all content got reflowed, so the 1.856 + // mColMaxHeight is feasible. 1.857 + aConfig.mKnownFeasibleHeight = std::min(aConfig.mKnownFeasibleHeight, 1.858 + aColData.mMaxHeight); 1.859 + } 1.860 + } 1.861 + 1.862 +#ifdef DEBUG_roc 1.863 + printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n", 1.864 + aConfig.mKnownInfeasibleHeight, aConfig.mKnownFeasibleHeight); 1.865 +#endif 1.866 + 1.867 + 1.868 + if (aConfig.mKnownInfeasibleHeight >= aConfig.mKnownFeasibleHeight - 1) { 1.869 + // aConfig.mKnownFeasibleHeight is where we want to be 1.870 + break; 1.871 + } 1.872 + 1.873 + if (aConfig.mKnownInfeasibleHeight >= availableContentHeight) { 1.874 + break; 1.875 + } 1.876 + 1.877 + if (lastKnownFeasibleHeight - aConfig.mKnownFeasibleHeight == 1) { 1.878 + // We decreased the feasible height by one twip only. This could 1.879 + // indicate that there is a continuously breakable child frame 1.880 + // that we are crawling through. 1.881 + maybeContinuousBreakingDetected = true; 1.882 + } 1.883 + 1.884 + nscoord nextGuess = (aConfig.mKnownFeasibleHeight + aConfig.mKnownInfeasibleHeight)/2; 1.885 + // The constant of 600 twips is arbitrary. It's about two line-heights. 1.886 + if (aConfig.mKnownFeasibleHeight - nextGuess < 600 && 1.887 + !maybeContinuousBreakingDetected) { 1.888 + // We're close to our target, so just try shrinking just the 1.889 + // minimum amount that will cause one of our columns to break 1.890 + // differently. 1.891 + nextGuess = aConfig.mKnownFeasibleHeight - 1; 1.892 + } else if (aUnboundedLastColumn) { 1.893 + // Make a guess by dividing that into N columns. Add some slop 1.894 + // to try to make it on the feasible side. The constant of 1.895 + // 600 twips is arbitrary. It's about two line-heights. 1.896 + nextGuess = aColData.mSumHeight/aConfig.mBalanceColCount + 600; 1.897 + // Sanitize it 1.898 + nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleHeight + 1, 1.899 + aConfig.mKnownFeasibleHeight - 1); 1.900 + } else if (aConfig.mKnownFeasibleHeight == NS_INTRINSICSIZE) { 1.901 + // This can happen when we had a next-in-flow so we didn't 1.902 + // want to do an unbounded height measuring step. Let's just increase 1.903 + // from the infeasible height by some reasonable amount. 1.904 + nextGuess = aConfig.mKnownInfeasibleHeight*2 + 600; 1.905 + } 1.906 + // Don't bother guessing more than our height constraint. 1.907 + nextGuess = std::min(availableContentHeight, nextGuess); 1.908 + 1.909 +#ifdef DEBUG_roc 1.910 + printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess); 1.911 +#endif 1.912 + 1.913 + aConfig.mColMaxHeight = nextGuess; 1.914 + 1.915 + aUnboundedLastColumn = false; 1.916 + AddStateBits(NS_FRAME_IS_DIRTY); 1.917 + feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, false, 1.918 + &aOutMargin, aColData); 1.919 + 1.920 + if (!aConfig.mIsBalancing) { 1.921 + // Looks like we had excess height when balancing, so we gave up on 1.922 + // trying to balance. 1.923 + break; 1.924 + } 1.925 + } 1.926 + 1.927 + if (aConfig.mIsBalancing && !feasible && 1.928 + !aPresContext->HasPendingInterrupt()) { 1.929 + // We may need to reflow one more time at the feasible height to 1.930 + // get a valid layout. 1.931 + bool skip = false; 1.932 + if (aConfig.mKnownInfeasibleHeight >= availableContentHeight) { 1.933 + aConfig.mColMaxHeight = availableContentHeight; 1.934 + if (mLastBalanceHeight == availableContentHeight) { 1.935 + skip = true; 1.936 + } 1.937 + } else { 1.938 + aConfig.mColMaxHeight = aConfig.mKnownFeasibleHeight; 1.939 + } 1.940 + if (!skip) { 1.941 + // If our height is unconstrained, make sure that the last column is 1.942 + // allowed to have arbitrary height here, even though we were balancing. 1.943 + // Otherwise we'd have to split, and it's not clear what we'd do with 1.944 + // that. 1.945 + AddStateBits(NS_FRAME_IS_DIRTY); 1.946 + feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, aConfig, 1.947 + availableContentHeight == NS_UNCONSTRAINEDSIZE, 1.948 + &aOutMargin, aColData); 1.949 + } 1.950 + } 1.951 + 1.952 + aRunWasFeasible = feasible; 1.953 +} 1.954 + 1.955 +nsresult 1.956 +nsColumnSetFrame::Reflow(nsPresContext* aPresContext, 1.957 + nsHTMLReflowMetrics& aDesiredSize, 1.958 + const nsHTMLReflowState& aReflowState, 1.959 + nsReflowStatus& aStatus) 1.960 +{ 1.961 + // Don't support interruption in columns 1.962 + nsPresContext::InterruptPreventer noInterrupts(aPresContext); 1.963 + 1.964 + DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame"); 1.965 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.966 + 1.967 + // Initialize OUT parameter 1.968 + aStatus = NS_FRAME_COMPLETE; 1.969 + 1.970 + // Our children depend on our height if we have a fixed height. 1.971 + if (aReflowState.ComputedHeight() != NS_AUTOHEIGHT) { 1.972 + NS_ASSERTION(aReflowState.ComputedHeight() != NS_INTRINSICSIZE, 1.973 + "Unexpected computed height"); 1.974 + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.975 + } 1.976 + else { 1.977 + RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.978 + } 1.979 + 1.980 + //------------ Handle Incremental Reflow ----------------- 1.981 + 1.982 + ReflowConfig config = ChooseColumnStrategy(aReflowState); 1.983 + 1.984 + // If balancing, then we allow the last column to grow to unbounded 1.985 + // height during the first reflow. This gives us a way to estimate 1.986 + // what the average column height should be, because we can measure 1.987 + // the heights of all the columns and sum them up. But don't do this 1.988 + // if we have a next in flow because we don't want to suck all its 1.989 + // content back here and then have to push it out again! 1.990 + nsIFrame* nextInFlow = GetNextInFlow(); 1.991 + bool unboundedLastColumn = config.mIsBalancing && !nextInFlow; 1.992 + nsCollapsingMargin carriedOutBottomMargin; 1.993 + ColumnBalanceData colData; 1.994 + colData.mHasExcessHeight = false; 1.995 + 1.996 + bool feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, config, 1.997 + unboundedLastColumn, &carriedOutBottomMargin, 1.998 + colData); 1.999 + 1.1000 + // If we're not balancing, then we're already done, since we should have 1.1001 + // reflown all of our children, and there is no need for a binary search to 1.1002 + // determine proper column height. 1.1003 + if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) { 1.1004 + FindBestBalanceHeight(aReflowState, aPresContext, config, colData, 1.1005 + aDesiredSize, carriedOutBottomMargin, 1.1006 + unboundedLastColumn, feasible, aStatus); 1.1007 + } 1.1008 + 1.1009 + if (aPresContext->HasPendingInterrupt() && 1.1010 + aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE) { 1.1011 + // In this situation, we might be lying about our reflow status, because 1.1012 + // our last kid (the one that got interrupted) was incomplete. Fix that. 1.1013 + aStatus = NS_FRAME_COMPLETE; 1.1014 + } 1.1015 + 1.1016 + FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false); 1.1017 + 1.1018 + aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin; 1.1019 + 1.1020 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.1021 + 1.1022 + NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) || 1.1023 + aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE, 1.1024 + "Column set should be complete if the available height is unconstrained"); 1.1025 + 1.1026 + return NS_OK; 1.1027 +} 1.1028 + 1.1029 +void 1.1030 +nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1031 + const nsRect& aDirtyRect, 1.1032 + const nsDisplayListSet& aLists) { 1.1033 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.1034 + 1.1035 + if (IsVisibleForPainting(aBuilder)) { 1.1036 + aLists.BorderBackground()->AppendNewToTop(new (aBuilder) 1.1037 + nsDisplayGenericOverflow(aBuilder, this, ::PaintColumnRule, "ColumnRule", 1.1038 + nsDisplayItem::TYPE_COLUMN_RULE)); 1.1039 + } 1.1040 + 1.1041 + // Our children won't have backgrounds so it doesn't matter where we put them. 1.1042 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.1043 + BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, aLists); 1.1044 + } 1.1045 +} 1.1046 + 1.1047 +nsresult 1.1048 +nsColumnSetFrame::AppendFrames(ChildListID aListID, 1.1049 + nsFrameList& aFrameList) 1.1050 +{ 1.1051 + if (aListID == kAbsoluteList) { 1.1052 + return nsContainerFrame::AppendFrames(aListID, aFrameList); 1.1053 + } 1.1054 + 1.1055 + NS_ERROR("unexpected child list"); 1.1056 + return NS_ERROR_INVALID_ARG; 1.1057 +} 1.1058 + 1.1059 +nsresult 1.1060 +nsColumnSetFrame::InsertFrames(ChildListID aListID, 1.1061 + nsIFrame* aPrevFrame, 1.1062 + nsFrameList& aFrameList) 1.1063 +{ 1.1064 + if (aListID == kAbsoluteList) { 1.1065 + return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); 1.1066 + } 1.1067 + 1.1068 + NS_ERROR("unexpected child list"); 1.1069 + return NS_ERROR_INVALID_ARG; 1.1070 +} 1.1071 + 1.1072 +nsresult 1.1073 +nsColumnSetFrame::RemoveFrame(ChildListID aListID, 1.1074 + nsIFrame* aOldFrame) 1.1075 +{ 1.1076 + if (aListID == kAbsoluteList) { 1.1077 + return nsContainerFrame::RemoveFrame(aListID, aOldFrame); 1.1078 + } 1.1079 + 1.1080 + NS_ERROR("unexpected child list"); 1.1081 + return NS_ERROR_INVALID_ARG; 1.1082 +}