LCOV - code coverage report
Current view: top level - layout/generic - nsColumnSetFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 566 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 37 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* rendering object for css3 multi-column layout */
       7             : 
       8             : #include "mozilla/Unused.h"
       9             : #include "nsColumnSetFrame.h"
      10             : #include "nsCSSRendering.h"
      11             : 
      12             : using namespace mozilla;
      13             : using namespace mozilla::layout;
      14             : 
      15             : 
      16             : class nsDisplayColumnRule : public nsDisplayItem {
      17             : public:
      18           0 :   nsDisplayColumnRule(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
      19           0 :     : nsDisplayItem(aBuilder, aFrame)
      20             :   {
      21           0 :     MOZ_COUNT_CTOR(nsDisplayColumnRule);
      22           0 :   }
      23             : #ifdef NS_BUILD_REFCNT_LOGGING
      24           0 :   virtual ~nsDisplayColumnRule() {
      25           0 :     MOZ_COUNT_DTOR(nsDisplayColumnRule);
      26           0 :     mBorderRenderers.Clear();
      27           0 :   }
      28             : #endif
      29             : 
      30             :   /**
      31             :    * Returns the frame's visual overflow rect instead of the frame's bounds.
      32             :    */
      33           0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
      34             :                            bool* aSnap) override
      35             :   {
      36           0 :     *aSnap = false;
      37           0 :     return static_cast<nsColumnSetFrame*>(mFrame)->CalculateBounds(ToReferenceFrame());
      38             :   }
      39             : 
      40             :   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
      41             :                                    LayerManager* aManager,
      42             :                                    const ContainerLayerParameters& aParameters) override;
      43             :   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
      44             :                                              LayerManager* aManager,
      45             :                                              const ContainerLayerParameters& aContainerParameters) override;
      46             :   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
      47             :                                        const StackingContextHelper& aSc,
      48             :                                        nsTArray<WebRenderParentCommand>& aParentCommands,
      49             :                                        mozilla::layers::WebRenderLayerManager* aManager,
      50             :                                        nsDisplayListBuilder* aDisplayListBuilder) override;
      51             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
      52             :                      gfxContext* aCtx) override;
      53             : 
      54           0 :   NS_DISPLAY_DECL_NAME("ColumnRule", nsDisplayItem::TYPE_COLUMN_RULE);
      55             : 
      56             : private:
      57             :   nsTArray<nsCSSBorderRenderer> mBorderRenderers;
      58             : };
      59             : 
      60             : void
      61           0 : nsDisplayColumnRule::Paint(nsDisplayListBuilder* aBuilder,
      62             :                            gfxContext* aCtx)
      63             : {
      64           0 :   static_cast<nsColumnSetFrame*>(mFrame)->
      65           0 :     CreateBorderRenderers(mBorderRenderers, aCtx, mVisibleRect, ToReferenceFrame());
      66             : 
      67           0 :   for (auto iter = mBorderRenderers.begin(); iter != mBorderRenderers.end(); iter++) {
      68           0 :     iter->DrawBorders();
      69             :   }
      70           0 : }
      71             : LayerState
      72           0 : nsDisplayColumnRule::GetLayerState(nsDisplayListBuilder* aBuilder,
      73             :                                    LayerManager* aManager,
      74             :                                    const ContainerLayerParameters& aParameters)
      75             : {
      76           0 :   if (!gfxPrefs::LayersAllowColumnRuleLayers()) {
      77           0 :     return LAYER_NONE;
      78             :   }
      79             : 
      80             :   RefPtr<gfxContext> screenRefCtx =
      81           0 :     gfxContext::CreateOrNull(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
      82             : 
      83           0 :   static_cast<nsColumnSetFrame*>(mFrame)->
      84           0 :     CreateBorderRenderers(mBorderRenderers, screenRefCtx, mVisibleRect, ToReferenceFrame());
      85             : 
      86           0 :   if (mBorderRenderers.IsEmpty()) {
      87           0 :     return LAYER_NONE;
      88             :   }
      89             : 
      90           0 :   for (auto iter = mBorderRenderers.begin(); iter != mBorderRenderers.end(); iter++) {
      91           0 :     if (!iter->CanCreateWebRenderCommands()) {
      92           0 :       return LAYER_NONE;
      93             :     }
      94             :   }
      95             : 
      96           0 :   return LAYER_ACTIVE;
      97             : }
      98             : 
      99             : already_AddRefed<Layer>
     100           0 : nsDisplayColumnRule::BuildLayer(nsDisplayListBuilder* aBuilder,
     101             :                                 LayerManager* aManager,
     102             :                                 const ContainerLayerParameters& aContainerParameters)
     103             : {
     104           0 :   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
     105             : }
     106             : 
     107             : bool
     108           0 : nsDisplayColumnRule::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
     109             :                                              const StackingContextHelper& aSc,
     110             :                                              nsTArray<WebRenderParentCommand>& aParentCommands,
     111             :                                              mozilla::layers::WebRenderLayerManager* aManager,
     112             :                                              nsDisplayListBuilder* aDisplayListBuilder)
     113             : {
     114           0 :   if (aManager->IsLayersFreeTransaction()) {
     115             :     RefPtr<gfxContext> screenRefCtx =
     116           0 :       gfxContext::CreateOrNull(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
     117             : 
     118           0 :     static_cast<nsColumnSetFrame*>(mFrame)->
     119           0 :       CreateBorderRenderers(mBorderRenderers, screenRefCtx, mVisibleRect, ToReferenceFrame());
     120             : 
     121           0 :     if (mBorderRenderers.IsEmpty()) {
     122           0 :       return false;
     123             :     }
     124             : 
     125           0 :     for (auto iter = mBorderRenderers.begin(); iter != mBorderRenderers.end(); iter++) {
     126           0 :       if (!iter->CanCreateWebRenderCommands()) {
     127           0 :         return false;
     128             :       }
     129             :     }
     130             :   }
     131             : 
     132           0 :   for (auto iter = mBorderRenderers.begin(); iter != mBorderRenderers.end(); iter++) {
     133           0 :     iter->CreateWebRenderCommands(aBuilder, aSc);
     134             :   }
     135             : 
     136           0 :   return true;
     137             : }
     138             : 
     139             : /**
     140             :  * Tracking issues:
     141             :  *
     142             :  * XXX cursor movement around the top and bottom of colums seems to make the editor
     143             :  * lose the caret.
     144             :  *
     145             :  * XXX should we support CSS columns applied to table elements?
     146             :  */
     147             : nsContainerFrame*
     148           0 : NS_NewColumnSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags)
     149             : {
     150           0 :   nsColumnSetFrame* it = new (aPresShell) nsColumnSetFrame(aContext);
     151           0 :   it->AddStateBits(aStateFlags | NS_BLOCK_MARGIN_ROOT);
     152           0 :   return it;
     153             : }
     154             : 
     155           0 : NS_IMPL_FRAMEARENA_HELPERS(nsColumnSetFrame)
     156             : 
     157           0 : nsColumnSetFrame::nsColumnSetFrame(nsStyleContext* aContext)
     158             :   : nsContainerFrame(aContext, kClassID)
     159           0 :   , mLastBalanceBSize(NS_INTRINSICSIZE)
     160             : {
     161           0 : }
     162             : 
     163             : void
     164           0 : nsColumnSetFrame::ForEachColumn(const std::function<void(const nsRect& lineRect)>& aSetLineRect,
     165             :                                 const nsPoint& aPt)
     166             : {
     167           0 :   nsIFrame* child = mFrames.FirstChild();
     168           0 :   if (!child)
     169           0 :     return;  // no columns
     170             : 
     171           0 :   nsIFrame* nextSibling = child->GetNextSibling();
     172           0 :   if (!nextSibling)
     173           0 :     return;  // 1 column only - this means no gap to draw on
     174             : 
     175           0 :   const nsStyleColumn* colStyle = StyleColumn();
     176           0 :   nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
     177           0 :   if (!ruleWidth)
     178           0 :     return;
     179             : 
     180           0 :   WritingMode wm = GetWritingMode();
     181           0 :   bool isVertical = wm.IsVertical();
     182           0 :   bool isRTL = !wm.IsBidiLTR();
     183             : 
     184             :   // Get our content rect as an absolute coordinate, not relative to
     185             :   // our parent (which is what the X and Y normally is)
     186           0 :   nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt;
     187             :   nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth)
     188           0 :                                : nsSize(ruleWidth, contentRect.height);
     189             : 
     190           0 :   while (nextSibling) {
     191             :     // The frame tree goes RTL in RTL.
     192             :     // The |prevFrame| and |nextFrame| frames here are the visually preceding
     193             :     // (left/above) and following (right/below) frames, not in logical writing-
     194             :     // mode direction.
     195           0 :     nsIFrame* prevFrame = isRTL ? nextSibling : child;
     196           0 :     nsIFrame* nextFrame = isRTL ? child : nextSibling;
     197             : 
     198             :     // Each child frame's position coordinates is actually relative to this
     199             :     // nsColumnSetFrame.
     200             :     // linePt will be at the top-left edge to paint the line.
     201           0 :     nsPoint linePt;
     202           0 :     if (isVertical) {
     203           0 :       nscoord edgeOfPrev = prevFrame->GetRect().YMost() + aPt.y;
     204           0 :       nscoord edgeOfNext = nextFrame->GetRect().Y() + aPt.y;
     205           0 :       linePt = nsPoint(contentRect.x,
     206           0 :                        (edgeOfPrev + edgeOfNext - ruleSize.height) / 2);
     207             :     } else {
     208           0 :       nscoord edgeOfPrev = prevFrame->GetRect().XMost() + aPt.x;
     209           0 :       nscoord edgeOfNext = nextFrame->GetRect().X() + aPt.x;
     210           0 :       linePt = nsPoint((edgeOfPrev + edgeOfNext - ruleSize.width) / 2,
     211             :                        contentRect.y);
     212             :     }
     213             : 
     214           0 :     aSetLineRect(nsRect(linePt, ruleSize));
     215             : 
     216           0 :     child = nextSibling;
     217           0 :     nextSibling = nextSibling->GetNextSibling();
     218             :   }
     219             : }
     220             : 
     221             : nsRect
     222           0 : nsColumnSetFrame::CalculateBounds(const nsPoint& aOffset)
     223             : {
     224           0 :   nsRect combined;
     225           0 :   ForEachColumn([&combined](const nsRect& aLineRect)
     226           0 :                 {
     227           0 :                   combined = combined.Union(aLineRect);
     228           0 :                 }, aOffset);
     229           0 :   return combined;
     230             : }
     231             : 
     232             : void
     233           0 : nsColumnSetFrame::CreateBorderRenderers(nsTArray<nsCSSBorderRenderer>& aBorderRenderers,
     234             :                                         gfxContext* aCtx,
     235             :                                         const nsRect& aDirtyRect,
     236             :                                         const nsPoint& aPt)
     237             : {
     238           0 :   WritingMode wm = GetWritingMode();
     239           0 :   bool isVertical = wm.IsVertical();
     240           0 :   const nsStyleColumn* colStyle = StyleColumn();
     241             :   uint8_t ruleStyle;
     242             : 
     243             :   // Per spec, inset => ridge and outset => groove
     244           0 :   if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET)
     245           0 :     ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE;
     246           0 :   else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET)
     247           0 :     ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE;
     248             :   else
     249           0 :     ruleStyle = colStyle->mColumnRuleStyle;
     250             : 
     251           0 :   nsPresContext* presContext = PresContext();
     252           0 :   nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth();
     253           0 :   if (!ruleWidth)
     254           0 :     return;
     255             : 
     256           0 :   aBorderRenderers.Clear();
     257             :   nscolor ruleColor =
     258           0 :     GetVisitedDependentColor(&nsStyleColumn::mColumnRuleColor);
     259             : 
     260             :   // In order to re-use a large amount of code, we treat the column rule as a border.
     261             :   // We create a new border style object and fill in all the details of the column rule as
     262             :   // the left border. PaintBorder() does all the rendering for us, so we not
     263             :   // only save an enormous amount of code but we'll support all the line styles that
     264             :   // we support on borders!
     265           0 :   nsStyleBorder border(presContext);
     266           0 :   Sides skipSides;
     267           0 :   if (isVertical) {
     268           0 :     border.SetBorderWidth(eSideTop, ruleWidth);
     269           0 :     border.SetBorderStyle(eSideTop, ruleStyle);
     270           0 :     border.mBorderTopColor = StyleComplexColor::FromColor(ruleColor);
     271           0 :     skipSides |= mozilla::eSideBitsLeftRight;
     272           0 :     skipSides |= mozilla::eSideBitsBottom;
     273             :   } else {
     274           0 :     border.SetBorderWidth(eSideLeft, ruleWidth);
     275           0 :     border.SetBorderStyle(eSideLeft, ruleStyle);
     276           0 :     border.mBorderLeftColor = StyleComplexColor::FromColor(ruleColor);
     277           0 :     skipSides |= mozilla::eSideBitsTopBottom;
     278           0 :     skipSides |= mozilla::eSideBitsRight;
     279             :   }
     280             : 
     281           0 :   ForEachColumn([&]
     282           0 :                 (const nsRect& aLineRect)
     283             :                 {
     284             :                   // Assert that we're not drawing a border-image here; if we were, we
     285             :                   // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
     286           0 :                   MOZ_ASSERT(border.mBorderImageSource.GetType() == eStyleImageType_Null);
     287             : 
     288           0 :                   gfx::DrawTarget* dt = aCtx ? aCtx->GetDrawTarget() : nullptr;
     289             :                   Maybe<nsCSSBorderRenderer> br =
     290           0 :                     nsCSSRendering::CreateBorderRendererWithStyleBorder(presContext, dt,
     291           0 :                                                                         this, aDirtyRect,
     292             :                                                                         aLineRect, border,
     293           0 :                                                                         StyleContext(), skipSides);
     294           0 :                   if (br.isSome()) {
     295           0 :                     aBorderRenderers.AppendElement(br.value());
     296             :                   }
     297           0 :                 }, aPt);
     298             : }
     299             : 
     300             : static nscoord
     301           0 : GetAvailableContentISize(const ReflowInput& aReflowInput)
     302             : {
     303           0 :   if (aReflowInput.AvailableISize() == NS_INTRINSICSIZE) {
     304           0 :     return NS_INTRINSICSIZE;
     305             :   }
     306             : 
     307           0 :   WritingMode wm = aReflowInput.GetWritingMode();
     308             :   nscoord borderPaddingISize =
     309           0 :     aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
     310           0 :   return std::max(0, aReflowInput.AvailableISize() - borderPaddingISize);
     311             : }
     312             : 
     313             : nscoord
     314           0 : nsColumnSetFrame::GetAvailableContentBSize(const ReflowInput& aReflowInput)
     315             : {
     316           0 :   if (aReflowInput.AvailableBSize() == NS_INTRINSICSIZE) {
     317           0 :     return NS_INTRINSICSIZE;
     318             :   }
     319             : 
     320           0 :   WritingMode wm = aReflowInput.GetWritingMode();
     321           0 :   LogicalMargin bp = aReflowInput.ComputedLogicalBorderPadding();
     322           0 :   bp.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
     323           0 :   bp.BEnd(wm) = aReflowInput.ComputedLogicalBorderPadding().BEnd(wm);
     324           0 :   return std::max(0, aReflowInput.AvailableBSize() - bp.BStartEnd(wm));
     325             : }
     326             : 
     327             : static nscoord
     328           0 : GetColumnGap(nsColumnSetFrame*    aFrame,
     329             :              const nsStyleColumn* aColStyle)
     330             : {
     331           0 :   if (eStyleUnit_Normal == aColStyle->mColumnGap.GetUnit())
     332           0 :     return aFrame->StyleFont()->mFont.size;
     333           0 :   if (eStyleUnit_Coord == aColStyle->mColumnGap.GetUnit()) {
     334           0 :     nscoord colGap = aColStyle->mColumnGap.GetCoordValue();
     335           0 :     NS_ASSERTION(colGap >= 0, "negative column gap");
     336           0 :     return colGap;
     337             :   }
     338             : 
     339           0 :   NS_NOTREACHED("Unknown gap type");
     340           0 :   return 0;
     341             : }
     342             : 
     343             : nsColumnSetFrame::ReflowConfig
     344           0 : nsColumnSetFrame::ChooseColumnStrategy(const ReflowInput& aReflowInput,
     345             :                                        bool aForceAuto = false,
     346             :                                        nscoord aFeasibleBSize = NS_INTRINSICSIZE,
     347             :                                        nscoord aInfeasibleBSize = 0)
     348             : {
     349           0 :   nscoord knownFeasibleBSize = aFeasibleBSize;
     350           0 :   nscoord knownInfeasibleBSize = aInfeasibleBSize;
     351           0 :   WritingMode wm = aReflowInput.GetWritingMode();
     352             : 
     353           0 :   const nsStyleColumn* colStyle = StyleColumn();
     354           0 :   nscoord availContentISize = GetAvailableContentISize(aReflowInput);
     355           0 :   if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
     356           0 :     availContentISize = aReflowInput.ComputedISize();
     357             :   }
     358             : 
     359           0 :   nscoord consumedBSize = ConsumedBSize(wm);
     360             : 
     361             :   // The effective computed height is the height of the current continuation
     362             :   // of the column set frame. This should be the same as the computed height
     363             :   // if we have an unconstrained available height.
     364           0 :   nscoord computedBSize = GetEffectiveComputedBSize(aReflowInput,
     365           0 :                                                     consumedBSize);
     366           0 :   nscoord colBSize = GetAvailableContentBSize(aReflowInput);
     367             : 
     368           0 :   if (aReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
     369           0 :     colBSize = aReflowInput.ComputedBSize();
     370           0 :   } else if (aReflowInput.ComputedMaxBSize() != NS_INTRINSICSIZE) {
     371           0 :     colBSize = std::min(colBSize, aReflowInput.ComputedMaxBSize());
     372             :   }
     373             : 
     374           0 :   nscoord colGap = GetColumnGap(this, colStyle);
     375           0 :   int32_t numColumns = colStyle->mColumnCount;
     376             : 
     377             :   // If column-fill is set to 'balance', then we want to balance the columns.
     378           0 :   const bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE
     379           0 :                            && !aForceAuto;
     380           0 :   if (isBalancing) {
     381           0 :     const uint32_t MAX_NESTED_COLUMN_BALANCING = 2;
     382           0 :     uint32_t cnt = 0;
     383           0 :     for (const ReflowInput* rs = aReflowInput.mParentReflowInput;
     384           0 :          rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->mParentReflowInput) {
     385           0 :       if (rs->mFlags.mIsColumnBalancing) {
     386           0 :         ++cnt;
     387             :       }
     388             :     }
     389           0 :     if (cnt == MAX_NESTED_COLUMN_BALANCING) {
     390           0 :       numColumns = 1;
     391             :     }
     392             :   }
     393             : 
     394             :   nscoord colISize;
     395             :   // In vertical writing-mode, "column-width" (inline size) will actually be
     396             :   // physical height, but its CSS name is still column-width.
     397           0 :   if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
     398           0 :     colISize = colStyle->mColumnWidth.GetCoordValue();
     399           0 :     NS_ASSERTION(colISize >= 0, "negative column width");
     400             :     // Reduce column count if necessary to make columns fit in the
     401             :     // available width. Compute max number of columns that fit in
     402             :     // availContentISize, satisfying colGap*(maxColumns - 1) +
     403             :     // colISize*maxColumns <= availContentISize
     404           0 :     if (availContentISize != NS_INTRINSICSIZE && colGap + colISize > 0
     405           0 :         && numColumns > 0) {
     406             :       // This expression uses truncated rounding, which is what we
     407             :       // want
     408             :       int32_t maxColumns =
     409           0 :         std::min(nscoord(nsStyleColumn::kMaxColumnCount),
     410           0 :                  (availContentISize + colGap) / (colGap + colISize));
     411           0 :       numColumns = std::max(1, std::min(numColumns, maxColumns));
     412             :     }
     413           0 :   } else if (numColumns > 0 && availContentISize != NS_INTRINSICSIZE) {
     414           0 :     nscoord iSizeMinusGaps = availContentISize - colGap * (numColumns - 1);
     415           0 :     colISize = iSizeMinusGaps / numColumns;
     416             :   } else {
     417           0 :     colISize = NS_INTRINSICSIZE;
     418             :   }
     419             :   // Take care of the situation where there's only one column but it's
     420             :   // still too wide
     421           0 :   colISize = std::max(1, std::min(colISize, availContentISize));
     422             : 
     423           0 :   nscoord expectedISizeLeftOver = 0;
     424             : 
     425           0 :   if (colISize != NS_INTRINSICSIZE && availContentISize != NS_INTRINSICSIZE) {
     426             :     // distribute leftover space
     427             : 
     428             :     // First, determine how many columns will be showing if the column
     429             :     // count is auto
     430           0 :     if (numColumns <= 0) {
     431             :       // choose so that colGap*(nominalColumnCount - 1) +
     432             :       // colISize*nominalColumnCount is nearly availContentISize
     433             :       // make sure to round down
     434           0 :       if (colGap + colISize > 0) {
     435           0 :         numColumns = (availContentISize + colGap) / (colGap + colISize);
     436             :         // The number of columns should never exceed kMaxColumnCount.
     437           0 :         numColumns = std::min(nscoord(nsStyleColumn::kMaxColumnCount),
     438           0 :                               numColumns);
     439             :       }
     440           0 :       if (numColumns <= 0) {
     441           0 :         numColumns = 1;
     442             :       }
     443             :     }
     444             : 
     445             :     // Compute extra space and divide it among the columns
     446             :     nscoord extraSpace =
     447           0 :       std::max(0, availContentISize - (colISize * numColumns +
     448           0 :                                        colGap * (numColumns - 1)));
     449           0 :     nscoord extraToColumns = extraSpace / numColumns;
     450           0 :     colISize += extraToColumns;
     451           0 :     expectedISizeLeftOver = extraSpace - (extraToColumns * numColumns);
     452             :   }
     453             : 
     454           0 :   if (isBalancing) {
     455           0 :     if (numColumns <= 0) {
     456             :       // Hmm, auto column count, column width or available width is unknown,
     457             :       // and balancing is required. Let's just use one column then.
     458           0 :       numColumns = 1;
     459             :     }
     460           0 :     colBSize = std::min(mLastBalanceBSize, colBSize);
     461             :   } else {
     462             :     // This is the case when the column-fill property is set to 'auto'.
     463             :     // No balancing, so don't limit the column count
     464           0 :     numColumns = INT32_MAX;
     465             : 
     466             :     // XXX_jwir3: If a page's height is set to 0, we could continually
     467             :     //            create continuations, resulting in an infinite loop, since
     468             :     //            no progress is ever made. This is an issue with the spec
     469             :     //            (css3-multicol, css3-page, and css3-break) that is
     470             :     //            unresolved as of 27 Feb 2013. For the time being, we set this
     471             :     //            to have a minimum of 1 css px. Once a resolution is made
     472             :     //            on what minimum to have for a page height, we may need to
     473             :     //            change this value to match the appropriate spec(s).
     474           0 :     colBSize = std::max(colBSize, nsPresContext::CSSPixelsToAppUnits(1));
     475             :   }
     476             : 
     477             : #ifdef DEBUG_roc
     478             :   printf("*** nsColumnSetFrame::ChooseColumnStrategy: numColumns=%d, colISize=%d,"
     479             :          " expectedISizeLeftOver=%d, colBSize=%d, colGap=%d\n",
     480             :          numColumns, colISize, expectedISizeLeftOver, colBSize, colGap);
     481             : #endif
     482             :   ReflowConfig config = { numColumns, colISize, expectedISizeLeftOver, colGap,
     483             :                           colBSize, isBalancing, knownFeasibleBSize,
     484           0 :                           knownInfeasibleBSize, computedBSize, consumedBSize };
     485           0 :   return config;
     486             : }
     487             : 
     488             : static void
     489           0 : MarkPrincipalChildrenDirty(nsIFrame* aFrame)
     490             : {
     491           0 :   for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
     492           0 :     childFrame->AddStateBits(NS_FRAME_IS_DIRTY);
     493             :   }
     494           0 : }
     495             : 
     496             : bool
     497           0 : nsColumnSetFrame::ReflowColumns(ReflowOutput& aDesiredSize,
     498             :                                 const ReflowInput& aReflowInput,
     499             :                                 nsReflowStatus& aReflowStatus,
     500             :                                 ReflowConfig& aConfig,
     501             :                                 bool aLastColumnUnbounded,
     502             :                                 nsCollapsingMargin* aCarriedOutBEndMargin,
     503             :                                 ColumnBalanceData& aColData)
     504             : {
     505           0 :   bool feasible = ReflowChildren(aDesiredSize, aReflowInput,
     506             :                                  aReflowStatus, aConfig, aLastColumnUnbounded,
     507           0 :                                  aCarriedOutBEndMargin, aColData);
     508             : 
     509           0 :   if (aColData.mHasExcessBSize) {
     510           0 :     aConfig = ChooseColumnStrategy(aReflowInput, true);
     511             : 
     512             :     // We need to reflow our children again one last time, otherwise we might
     513             :     // end up with a stale column height for some of our columns, since we
     514             :     // bailed out of balancing.
     515           0 :     feasible = ReflowChildren(aDesiredSize, aReflowInput, aReflowStatus,
     516             :                               aConfig, aLastColumnUnbounded,
     517           0 :                               aCarriedOutBEndMargin, aColData);
     518             :   }
     519             : 
     520           0 :   return feasible;
     521             : }
     522             : 
     523           0 : static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin,
     524             :                         WritingMode aWM, const nsSize& aContainerSize)
     525             : {
     526           0 :   if (aChild->GetLogicalPosition(aWM, aContainerSize) == aOrigin) {
     527           0 :     return;
     528             :   }
     529             : 
     530           0 :   aChild->SetPosition(aWM, aOrigin, aContainerSize);
     531           0 :   nsContainerFrame::PlaceFrameView(aChild);
     532             : }
     533             : 
     534             : nscoord
     535           0 : nsColumnSetFrame::GetMinISize(gfxContext *aRenderingContext)
     536             : {
     537           0 :   nscoord iSize = 0;
     538           0 :   DISPLAY_MIN_WIDTH(this, iSize);
     539           0 :   if (mFrames.FirstChild()) {
     540           0 :     iSize = mFrames.FirstChild()->GetMinISize(aRenderingContext);
     541             :   }
     542           0 :   const nsStyleColumn* colStyle = StyleColumn();
     543             :   nscoord colISize;
     544           0 :   if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
     545           0 :     colISize = colStyle->mColumnWidth.GetCoordValue();
     546             :     // As available width reduces to zero, we reduce our number of columns
     547             :     // to one, and don't enforce the column width, so just return the min
     548             :     // of the child's min-width with any specified column width.
     549           0 :     iSize = std::min(iSize, colISize);
     550             :   } else {
     551           0 :     NS_ASSERTION(colStyle->mColumnCount > 0,
     552             :                  "column-count and column-width can't both be auto");
     553             :     // As available width reduces to zero, we still have mColumnCount columns,
     554             :     // so multiply the child's min-width by the number of columns (n) and
     555             :     // include n-1 column gaps.
     556           0 :     colISize = iSize;
     557           0 :     iSize *= colStyle->mColumnCount;
     558           0 :     nscoord colGap = GetColumnGap(this, colStyle);
     559           0 :     iSize += colGap * (colStyle->mColumnCount - 1);
     560             :     // The multiplication above can make 'width' negative (integer overflow),
     561             :     // so use std::max to protect against that.
     562           0 :     iSize = std::max(iSize, colISize);
     563             :   }
     564             :   // XXX count forced column breaks here? Maybe we should return the child's
     565             :   // min-width times the minimum number of columns.
     566           0 :   return iSize;
     567             : }
     568             : 
     569             : nscoord
     570           0 : nsColumnSetFrame::GetPrefISize(gfxContext *aRenderingContext)
     571             : {
     572             :   // Our preferred width is our desired column width, if specified, otherwise
     573             :   // the child's preferred width, times the number of columns, plus the width
     574             :   // of any required column gaps
     575             :   // XXX what about forced column breaks here?
     576           0 :   nscoord result = 0;
     577           0 :   DISPLAY_PREF_WIDTH(this, result);
     578           0 :   const nsStyleColumn* colStyle = StyleColumn();
     579           0 :   nscoord colGap = GetColumnGap(this, colStyle);
     580             : 
     581             :   nscoord colISize;
     582           0 :   if (colStyle->mColumnWidth.GetUnit() == eStyleUnit_Coord) {
     583           0 :     colISize = colStyle->mColumnWidth.GetCoordValue();
     584           0 :   } else if (mFrames.FirstChild()) {
     585           0 :     colISize = mFrames.FirstChild()->GetPrefISize(aRenderingContext);
     586             :   } else {
     587           0 :     colISize = 0;
     588             :   }
     589             : 
     590           0 :   int32_t numColumns = colStyle->mColumnCount;
     591           0 :   if (numColumns <= 0) {
     592             :     // if column-count is auto, assume one column
     593           0 :     numColumns = 1;
     594             :   }
     595             : 
     596           0 :   nscoord iSize = colISize * numColumns + colGap * (numColumns - 1);
     597             :   // The multiplication above can make 'iSize' negative (integer overflow),
     598             :   // so use std::max to protect against that.
     599           0 :   result = std::max(iSize, colISize);
     600           0 :   return result;
     601             : }
     602             : 
     603             : bool
     604           0 : nsColumnSetFrame::ReflowChildren(ReflowOutput&     aDesiredSize,
     605             :                                  const ReflowInput& aReflowInput,
     606             :                                  nsReflowStatus&          aStatus,
     607             :                                  const ReflowConfig&      aConfig,
     608             :                                  bool                     aUnboundedLastColumn,
     609             :                                  nsCollapsingMargin*      aCarriedOutBEndMargin,
     610             :                                  ColumnBalanceData&       aColData)
     611             : {
     612           0 :   aColData.Reset();
     613           0 :   bool allFit = true;
     614           0 :   WritingMode wm = GetWritingMode();
     615           0 :   bool isRTL = !wm.IsBidiLTR();
     616           0 :   bool shrinkingBSize = mLastBalanceBSize > aConfig.mColMaxBSize;
     617           0 :   bool changingBSize = mLastBalanceBSize != aConfig.mColMaxBSize;
     618             : 
     619             : #ifdef DEBUG_roc
     620             :   printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n"
     621             :          "    mBalanceColCount=%d, mColISize=%d, mColGap=%d\n",
     622             :          mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount,
     623             :          aConfig.mColISize, aConfig.mColGap);
     624             : #endif
     625             : 
     626           0 :   DrainOverflowColumns();
     627             : 
     628           0 :   const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize;
     629             : 
     630           0 :   if (colBSizeChanged) {
     631           0 :     mLastBalanceBSize = aConfig.mColMaxBSize;
     632             :     // XXX Seems like this could fire if incremental reflow pushed the column set
     633             :     // down so we reflow incrementally with a different available height.
     634             :     // We need a way to do an incremental reflow and be sure availableHeight
     635             :     // changes are taken account of! Right now I think block frames with absolute
     636             :     // children might exit early.
     637             :     //NS_ASSERTION(aKidReason != eReflowReason_Incremental,
     638             :     //             "incremental reflow should not have changed the balance height");
     639             :   }
     640             : 
     641             :   // get our border and padding
     642           0 :   LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding();
     643           0 :   borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowInput));
     644             : 
     645           0 :   nsRect contentRect(0, 0, 0, 0);
     646           0 :   nsOverflowAreas overflowRects;
     647             : 
     648           0 :   nsIFrame* child = mFrames.FirstChild();
     649           0 :   LogicalPoint childOrigin(wm, borderPadding.IStart(wm),
     650           0 :                            borderPadding.BStart(wm));
     651             :   // In vertical-rl mode, columns will not be correctly placed if the
     652             :   // reflowInput's ComputedWidth() is UNCONSTRAINED (in which case we'll get
     653             :   // a containerSize.width of zero here). In that case, the column positions
     654             :   // will be adjusted later, after our correct contentSize is known.
     655           0 :   nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
     656             : 
     657             :   // For RTL, since the columns might not fill the frame exactly, we
     658             :   // need to account for the slop. Otherwise we'll waste time moving the
     659             :   // columns by some tiny amount
     660             : 
     661             :   // XXX when all of layout is converted to logical coordinates, we
     662             :   //     probably won't need to do this hack any more. For now, we
     663             :   //     confine it to the legacy horizontal-rl case
     664           0 :   if (!wm.IsVertical() && isRTL) {
     665           0 :     nscoord availISize = aReflowInput.AvailableISize();
     666           0 :     if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
     667           0 :       availISize = aReflowInput.ComputedISize();
     668             :     }
     669           0 :     if (availISize != NS_INTRINSICSIZE) {
     670           0 :       childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) -
     671             :                           availISize;
     672             : #ifdef DEBUG_roc
     673             :       printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm));
     674             : #endif
     675             :     }
     676             :   }
     677             : 
     678           0 :   int columnCount = 0;
     679           0 :   int contentBEnd = 0;
     680           0 :   bool reflowNext = false;
     681             : 
     682           0 :   while (child) {
     683             :     // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't
     684             :     // skip if the next column is dirty, because the next column's first line(s)
     685             :     // might be pullable back to this column. We can't skip if it's the last child
     686             :     // because we need to obtain the bottom margin. We can't skip
     687             :     // if this is the last column and we're supposed to assign unbounded
     688             :     // height to it, because that could change the available height from
     689             :     // the last time we reflowed it and we should try to pull all the
     690             :     // content from its next sibling. (Note that it might be the last
     691             :     // column, but not be the last child because the desired number of columns
     692             :     // has changed.)
     693           0 :     bool skipIncremental = !aReflowInput.ShouldReflowAllKids()
     694           0 :       && !NS_SUBTREE_DIRTY(child)
     695           0 :       && child->GetNextSibling()
     696           0 :       && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1)
     697           0 :       && !NS_SUBTREE_DIRTY(child->GetNextSibling());
     698             :     // If column-fill is auto (not the default), then we might need to
     699             :     // move content between columns for any change in column block-size.
     700           0 :     if (skipIncremental && changingBSize &&
     701           0 :         StyleColumn()->mColumnFill == NS_STYLE_COLUMN_FILL_AUTO) {
     702           0 :       skipIncremental = false;
     703             :     }
     704             :     // If we need to pull up content from the prev-in-flow then this is not just
     705             :     // a height shrink. The prev in flow will have set the dirty bit.
     706             :     // Check the overflow rect YMost instead of just the child's content height. The child
     707             :     // may have overflowing content that cares about the available height boundary.
     708             :     // (It may also have overflowing content that doesn't care about the available height
     709             :     // boundary, but if so, too bad, this optimization is defeated.)
     710             :     // We want scrollable overflow here since this is a calculation that
     711             :     // affects layout.
     712           0 :     if (skipIncremental && shrinkingBSize) {
     713           0 :       switch (wm.GetBlockDir()) {
     714             :       case WritingMode::eBlockTB:
     715           0 :         if (child->GetScrollableOverflowRect().YMost() > aConfig.mColMaxBSize) {
     716           0 :           skipIncremental = false;
     717             :         }
     718           0 :         break;
     719             :       case WritingMode::eBlockLR:
     720           0 :         if (child->GetScrollableOverflowRect().XMost() > aConfig.mColMaxBSize) {
     721           0 :           skipIncremental = false;
     722             :         }
     723           0 :         break;
     724             :       case WritingMode::eBlockRL:
     725             :         // XXX not sure how to handle this, so for now just don't attempt
     726             :         // the optimization
     727           0 :         skipIncremental = false;
     728           0 :         break;
     729             :       default:
     730           0 :         NS_NOTREACHED("unknown block direction");
     731           0 :         break;
     732             :       }
     733             :     }
     734             : 
     735           0 :     nscoord childContentBEnd = 0;
     736           0 :     if (!reflowNext && skipIncremental) {
     737             :       // This child does not need to be reflowed, but we may need to move it
     738           0 :       MoveChildTo(child, childOrigin, wm, containerSize);
     739             : 
     740             :       // If this is the last frame then make sure we get the right status
     741           0 :       nsIFrame* kidNext = child->GetNextSibling();
     742           0 :       if (kidNext) {
     743           0 :         aStatus.Reset();
     744           0 :         if (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
     745           0 :           aStatus.SetOverflowIncomplete();
     746             :         } else {
     747           0 :           aStatus.SetIncomplete();
     748             :         }
     749             :       } else {
     750           0 :         aStatus = mLastFrameStatus;
     751             :       }
     752           0 :       childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
     753             : #ifdef DEBUG_roc
     754             :       printf("*** Skipping child #%d %p (incremental %d): status = %d\n",
     755             :              columnCount, (void*)child, skipIncremental, aStatus);
     756             : #endif
     757             :     } else {
     758           0 :       LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize);
     759           0 :       if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) {
     760           0 :         availSize.BSize(wm) = GetAvailableContentBSize(aReflowInput);
     761             :       }
     762             : 
     763           0 :       LogicalSize computedSize = aReflowInput.ComputedSize(wm);
     764             : 
     765           0 :       if (reflowNext)
     766           0 :         child->AddStateBits(NS_FRAME_IS_DIRTY);
     767             : 
     768           0 :       LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm));
     769             :       ReflowInput kidReflowInput(PresContext(), aReflowInput, child,
     770           0 :                                        availSize, &kidCBSize);
     771           0 :       kidReflowInput.mFlags.mIsTopOfPage = true;
     772           0 :       kidReflowInput.mFlags.mTableIsSplittable = false;
     773           0 :       kidReflowInput.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX;
     774             : 
     775             :       // We need to reflow any float placeholders, even if our column height
     776             :       // hasn't changed.
     777           0 :       kidReflowInput.mFlags.mMustReflowPlaceholders = !colBSizeChanged;
     778             : 
     779             : #ifdef DEBUG_roc
     780             :       printf("*** Reflowing child #%d %p: availHeight=%d\n",
     781             :              columnCount, (void*)child,availSize.BSize(wm));
     782             : #endif
     783             : 
     784             :       // Note if the column's next in flow is not being changed by this incremental reflow.
     785             :       // This may allow the current column to avoid trying to pull lines from the next column.
     786           0 :       if (child->GetNextSibling() &&
     787           0 :           !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
     788           0 :         !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) {
     789           0 :         kidReflowInput.mFlags.mNextInFlowUntouched = true;
     790             :       }
     791             : 
     792           0 :       ReflowOutput kidDesiredSize(wm, aDesiredSize.mFlags);
     793             : 
     794             :       // XXX it would be cool to consult the float manager for the
     795             :       // previous block to figure out the region of floats from the
     796             :       // previous column that extend into this column, and subtract
     797             :       // that region from the new float manager.  So you could stick a
     798             :       // really big float in the first column and text in following
     799             :       // columns would flow around it.
     800             : 
     801             :       // Reflow the frame
     802             :       LogicalPoint origin(wm,
     803           0 :                           childOrigin.I(wm) +
     804           0 :                           kidReflowInput.ComputedLogicalMargin().IStart(wm),
     805           0 :                           childOrigin.B(wm) +
     806           0 :                           kidReflowInput.ComputedLogicalMargin().BStart(wm));
     807           0 :       ReflowChild(child, PresContext(), kidDesiredSize, kidReflowInput,
     808           0 :                   wm, origin, containerSize, 0, aStatus);
     809             : 
     810           0 :       reflowNext = aStatus.NextInFlowNeedsReflow();
     811             : 
     812             : #ifdef DEBUG_roc
     813             :       printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n",
     814             :              columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(),
     815             :              kidDesiredSize.mCarriedOutBEndMargin.get());
     816             : #endif
     817             : 
     818           0 :       NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus);
     819             : 
     820           0 :       *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin;
     821             : 
     822           0 :       FinishReflowChild(child, PresContext(), kidDesiredSize,
     823           0 :                         &kidReflowInput, wm, childOrigin, containerSize, 0);
     824             : 
     825           0 :       childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child);
     826           0 :       if (childContentBEnd > aConfig.mColMaxBSize) {
     827           0 :         allFit = false;
     828             :       }
     829           0 :       if (childContentBEnd > availSize.BSize(wm)) {
     830           0 :         aColData.mMaxOverflowingBSize = std::max(childContentBEnd,
     831           0 :             aColData.mMaxOverflowingBSize);
     832             :       }
     833             :     }
     834             : 
     835           0 :     contentRect.UnionRect(contentRect, child->GetRect());
     836             : 
     837           0 :     ConsiderChildOverflow(overflowRects, child);
     838           0 :     contentBEnd = std::max(contentBEnd, childContentBEnd);
     839           0 :     aColData.mLastBSize = childContentBEnd;
     840           0 :     aColData.mSumBSize += childContentBEnd;
     841             : 
     842             :     // Build a continuation column if necessary
     843           0 :     nsIFrame* kidNextInFlow = child->GetNextInFlow();
     844             : 
     845           0 :     if (aStatus.IsFullyComplete() && !aStatus.IsTruncated()) {
     846           0 :       NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
     847           0 :       child = nullptr;
     848           0 :       break;
     849             :     } else {
     850           0 :       ++columnCount;
     851             :       // Make sure that the column has a next-in-flow. If not, we must
     852             :       // create one to hold the overflowing stuff, even if we're just
     853             :       // going to put it on our overflow list and let *our*
     854             :       // next in flow handle it.
     855           0 :       if (!kidNextInFlow) {
     856           0 :         NS_ASSERTION(aStatus.NextInFlowNeedsReflow(),
     857             :                      "We have to create a continuation, but the block doesn't want us to reflow it?");
     858             : 
     859             :         // We need to create a continuing column
     860           0 :         kidNextInFlow = CreateNextInFlow(child);
     861             :       }
     862             : 
     863             :       // Make sure we reflow a next-in-flow when it switches between being
     864             :       // normal or overflow container
     865           0 :       if (aStatus.IsOverflowIncomplete()) {
     866           0 :         if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
     867           0 :           aStatus.SetNextInFlowNeedsReflow();
     868           0 :           reflowNext = true;
     869           0 :           kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
     870             :         }
     871             :       }
     872           0 :       else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
     873           0 :         aStatus.SetNextInFlowNeedsReflow();
     874           0 :         reflowNext = true;
     875           0 :         kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
     876             :       }
     877             : 
     878           0 :       if ((contentBEnd > aReflowInput.ComputedMaxBSize() ||
     879           0 :            contentBEnd > aReflowInput.ComputedBSize()) &&
     880           0 :            aConfig.mBalanceColCount < INT32_MAX) {
     881             :         // We overflowed vertically, but have not exceeded the number of
     882             :         // columns. We're going to go into overflow columns now, so balancing
     883             :         // no longer applies.
     884           0 :         aColData.mHasExcessBSize = true;
     885             :       }
     886             : 
     887           0 :       if (columnCount >= aConfig.mBalanceColCount) {
     888             :         // No more columns allowed here. Stop.
     889           0 :         aStatus.SetNextInFlowNeedsReflow();
     890           0 :         kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY);
     891             :         // Move any of our leftover columns to our overflow list. Our
     892             :         // next-in-flow will eventually pick them up.
     893           0 :         const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child);
     894           0 :         if (continuationColumns.NotEmpty()) {
     895           0 :           SetOverflowFrames(continuationColumns);
     896             :         }
     897           0 :         child = nullptr;
     898           0 :         break;
     899             :       }
     900             :     }
     901             : 
     902           0 :     if (PresContext()->HasPendingInterrupt()) {
     903             :       // Stop the loop now while |child| still points to the frame that bailed
     904             :       // out.  We could keep going here and condition a bunch of the code in
     905             :       // this loop on whether there's an interrupt, or even just keep going and
     906             :       // trying to reflow the blocks (even though we know they'll interrupt
     907             :       // right after their first line), but stopping now is conceptually the
     908             :       // simplest (and probably fastest) thing.
     909           0 :       break;
     910             :     }
     911             : 
     912             :     // Advance to the next column
     913           0 :     child = child->GetNextSibling();
     914             : 
     915           0 :     if (child) {
     916           0 :       childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap;
     917             : 
     918             : #ifdef DEBUG_roc
     919             :       printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm));
     920             : #endif
     921             :     }
     922             :   }
     923             : 
     924           0 :   if (PresContext()->CheckForInterrupt(this) &&
     925           0 :       (GetStateBits() & NS_FRAME_IS_DIRTY)) {
     926             :     // Mark all our kids starting with |child| dirty
     927             : 
     928             :     // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt,
     929             :     // because we might have interrupted while reflowing |child|, and since
     930             :     // we're about to add a dirty bit to |child| we need to make sure that
     931             :     // |this| is scheduled to have dirty bits marked on it and its ancestors.
     932             :     // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll
     933             :     // bail out immediately, since it'll already have a dirty bit.
     934           0 :     for (; child; child = child->GetNextSibling()) {
     935           0 :       child->AddStateBits(NS_FRAME_IS_DIRTY);
     936             :     }
     937             :   }
     938             : 
     939           0 :   aColData.mMaxBSize = contentBEnd;
     940           0 :   LogicalSize contentSize = LogicalSize(wm, contentRect.Size());
     941           0 :   contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd);
     942           0 :   mLastFrameStatus = aStatus;
     943             : 
     944             :   // Apply computed and min/max values
     945           0 :   if (aConfig.mComputedBSize != NS_INTRINSICSIZE) {
     946           0 :     if (aReflowInput.AvailableBSize() != NS_INTRINSICSIZE) {
     947           0 :       contentSize.BSize(wm) = std::min(contentSize.BSize(wm),
     948           0 :                                        aConfig.mComputedBSize);
     949             :     } else {
     950           0 :       contentSize.BSize(wm) = aConfig.mComputedBSize;
     951             :     }
     952             :   } else {
     953             :     // We add the "consumed" block-size back in so that we're applying
     954             :     // constraints to the correct bSize value, then subtract it again
     955             :     // after we've finished with the min/max calculation. This prevents us from
     956             :     // having a last continuation that is smaller than the min bSize. but which
     957             :     // has prev-in-flows, trigger a larger bSize than actually required.
     958           0 :     contentSize.BSize(wm) =
     959           0 :       aReflowInput.ApplyMinMaxBSize(contentSize.BSize(wm),
     960           0 :                                     aConfig.mConsumedBSize);
     961             :   }
     962           0 :   if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
     963           0 :     contentSize.ISize(wm) = aReflowInput.ComputedISize();
     964             :   } else {
     965           0 :     contentSize.ISize(wm) =
     966           0 :       aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm));
     967             :   }
     968             : 
     969           0 :   contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
     970           0 :   contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
     971           0 :   aDesiredSize.SetSize(wm, contentSize);
     972           0 :   aDesiredSize.mOverflowAreas = overflowRects;
     973           0 :   aDesiredSize.UnionOverflowAreasWithDesiredBounds();
     974             : 
     975             :   // In vertical-rl mode, make a second pass if necessary to reposition the
     976             :   // columns with the correct container width. (In other writing modes,
     977             :   // correct containerSize was not required for column positioning so we don't
     978             :   // need this fixup.)
     979           0 :   if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) {
     980           0 :     const nsSize finalContainerSize = aDesiredSize.PhysicalSize();
     981           0 :     for (nsIFrame* child : mFrames) {
     982             :       // Get the logical position as set previously using a provisional or
     983             :       // dummy containerSize, and reset with the correct container size.
     984           0 :       child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize),
     985           0 :                          finalContainerSize);
     986             :     }
     987             :   }
     988             : 
     989             : #ifdef DEBUG_roc
     990             :   printf("*** DONE PASS feasible=%d\n", allFit && aStatus.IsFullyComplete()
     991             :          && !aStatus.IsTruncated());
     992             : #endif
     993           0 :   return allFit && aStatus.IsFullyComplete()
     994           0 :     && !aStatus.IsTruncated();
     995             : }
     996             : 
     997             : void
     998           0 : nsColumnSetFrame::DrainOverflowColumns()
     999             : {
    1000             :   // First grab the prev-in-flows overflows and reparent them to this
    1001             :   // frame.
    1002           0 :   nsPresContext* presContext = PresContext();
    1003           0 :   nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
    1004           0 :   if (prev) {
    1005           0 :     AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
    1006           0 :     if (overflows) {
    1007           0 :       nsContainerFrame::ReparentFrameViewList(*overflows, prev, this);
    1008             : 
    1009           0 :       mFrames.InsertFrames(this, nullptr, *overflows);
    1010             :     }
    1011             :   }
    1012             : 
    1013             :   // Now pull back our own overflows and append them to our children.
    1014             :   // We don't need to reparent them since we're already their parent.
    1015           0 :   AutoFrameListPtr overflows(presContext, StealOverflowFrames());
    1016           0 :   if (overflows) {
    1017             :     // We're already the parent for these frames, so no need to set
    1018             :     // their parent again.
    1019           0 :     mFrames.AppendFrames(nullptr, *overflows);
    1020             :   }
    1021           0 : }
    1022             : 
    1023             : void
    1024           0 : nsColumnSetFrame::FindBestBalanceBSize(const ReflowInput& aReflowInput,
    1025             :                                        nsPresContext* aPresContext,
    1026             :                                        ReflowConfig& aConfig,
    1027             :                                        ColumnBalanceData& aColData,
    1028             :                                        ReflowOutput& aDesiredSize,
    1029             :                                        nsCollapsingMargin& aOutMargin,
    1030             :                                        bool& aUnboundedLastColumn,
    1031             :                                        bool& aRunWasFeasible,
    1032             :                                        nsReflowStatus& aStatus)
    1033             : {
    1034           0 :   bool feasible = aRunWasFeasible;
    1035             : 
    1036           0 :   nsMargin bp = aReflowInput.ComputedPhysicalBorderPadding();
    1037           0 :   bp.ApplySkipSides(GetSkipSides());
    1038           0 :   bp.bottom = aReflowInput.ComputedPhysicalBorderPadding().bottom;
    1039             : 
    1040             :   nscoord availableContentBSize =
    1041           0 :     GetAvailableContentBSize(aReflowInput);
    1042             : 
    1043             :   // Termination of the algorithm below is guaranteed because
    1044             :   // aConfig.knownFeasibleBSize - aConfig.knownInfeasibleBSize decreases in every
    1045             :   // iteration.
    1046             : 
    1047             :   // We set this flag when we detect that we may contain a frame
    1048             :   // that can break anywhere (thus foiling the linear decrease-by-one
    1049             :   // search)
    1050           0 :   bool maybeContinuousBreakingDetected = false;
    1051             : 
    1052           0 :   while (!aPresContext->HasPendingInterrupt()) {
    1053           0 :     nscoord lastKnownFeasibleBSize = aConfig.mKnownFeasibleBSize;
    1054             : 
    1055             :     // Record what we learned from the last reflow
    1056           0 :     if (feasible) {
    1057             :       // maxBSize is feasible. Also, mLastBalanceBSize is feasible.
    1058           0 :       aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
    1059           0 :                                               aColData.mMaxBSize);
    1060           0 :       aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
    1061           0 :                                               mLastBalanceBSize);
    1062             : 
    1063             :       // Furthermore, no height less than the height of the last
    1064             :       // column can ever be feasible. (We might be able to reduce the
    1065             :       // height of a non-last column by moving content to a later column,
    1066             :       // but we can't do that with the last column.)
    1067           0 :       if (mFrames.GetLength() == aConfig.mBalanceColCount) {
    1068           0 :         aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
    1069           0 :                                        aColData.mLastBSize - 1);
    1070             :       }
    1071             :     } else {
    1072           0 :       aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
    1073           0 :                                                 mLastBalanceBSize);
    1074             :       // If a column didn't fit in its available height, then its current
    1075             :       // height must be the minimum height for unbreakable content in
    1076             :       // the column, and therefore no smaller height can be feasible.
    1077           0 :       aConfig.mKnownInfeasibleBSize = std::max(aConfig.mKnownInfeasibleBSize,
    1078           0 :                                          aColData.mMaxOverflowingBSize - 1);
    1079             : 
    1080           0 :       if (aUnboundedLastColumn) {
    1081             :         // The last column is unbounded, so all content got reflowed, so the
    1082             :         // mColMaxBSize is feasible.
    1083           0 :         aConfig.mKnownFeasibleBSize = std::min(aConfig.mKnownFeasibleBSize,
    1084           0 :                                                 aColData.mMaxBSize);
    1085             :       }
    1086             :     }
    1087             : 
    1088             : #ifdef DEBUG_roc
    1089             :     printf("*** nsColumnSetFrame::Reflow balancing knownInfeasible=%d knownFeasible=%d\n",
    1090             :            aConfig.mKnownInfeasibleBSize, aConfig.mKnownFeasibleBSize);
    1091             : #endif
    1092             : 
    1093             : 
    1094           0 :     if (aConfig.mKnownInfeasibleBSize >= aConfig.mKnownFeasibleBSize - 1) {
    1095             :       // aConfig.mKnownFeasibleBSize is where we want to be
    1096           0 :       break;
    1097             :     }
    1098             : 
    1099           0 :     if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
    1100           0 :       break;
    1101             :     }
    1102             : 
    1103           0 :     if (lastKnownFeasibleBSize - aConfig.mKnownFeasibleBSize == 1) {
    1104             :       // We decreased the feasible height by one twip only. This could
    1105             :       // indicate that there is a continuously breakable child frame
    1106             :       // that we are crawling through.
    1107           0 :       maybeContinuousBreakingDetected = true;
    1108             :     }
    1109             : 
    1110           0 :     nscoord nextGuess = (aConfig.mKnownFeasibleBSize + aConfig.mKnownInfeasibleBSize)/2;
    1111             :     // The constant of 600 twips is arbitrary. It's about two line-heights.
    1112           0 :     if (aConfig.mKnownFeasibleBSize - nextGuess < 600 &&
    1113           0 :         !maybeContinuousBreakingDetected) {
    1114             :       // We're close to our target, so just try shrinking just the
    1115             :       // minimum amount that will cause one of our columns to break
    1116             :       // differently.
    1117           0 :       nextGuess = aConfig.mKnownFeasibleBSize - 1;
    1118           0 :     } else if (aUnboundedLastColumn) {
    1119             :       // Make a guess by dividing that into N columns. Add some slop
    1120             :       // to try to make it on the feasible side.  The constant of
    1121             :       // 600 twips is arbitrary. It's about two line-heights.
    1122           0 :       nextGuess = aColData.mSumBSize/aConfig.mBalanceColCount + 600;
    1123             :       // Sanitize it
    1124           0 :       nextGuess = clamped(nextGuess, aConfig.mKnownInfeasibleBSize + 1,
    1125           0 :                                      aConfig.mKnownFeasibleBSize - 1);
    1126           0 :     } else if (aConfig.mKnownFeasibleBSize == NS_INTRINSICSIZE) {
    1127             :       // This can happen when we had a next-in-flow so we didn't
    1128             :       // want to do an unbounded height measuring step. Let's just increase
    1129             :       // from the infeasible height by some reasonable amount.
    1130           0 :       nextGuess = aConfig.mKnownInfeasibleBSize*2 + 600;
    1131             :     }
    1132             :     // Don't bother guessing more than our height constraint.
    1133           0 :     nextGuess = std::min(availableContentBSize, nextGuess);
    1134             : 
    1135             : #ifdef DEBUG_roc
    1136             :     printf("*** nsColumnSetFrame::Reflow balancing choosing next guess=%d\n", nextGuess);
    1137             : #endif
    1138             : 
    1139           0 :     aConfig.mColMaxBSize = nextGuess;
    1140             : 
    1141           0 :     aUnboundedLastColumn = false;
    1142           0 :     MarkPrincipalChildrenDirty(this);
    1143             :     feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig, false,
    1144           0 :                              &aOutMargin, aColData);
    1145             : 
    1146           0 :     if (!aConfig.mIsBalancing) {
    1147             :       // Looks like we had excess height when balancing, so we gave up on
    1148             :       // trying to balance.
    1149           0 :       break;
    1150             :     }
    1151             :   }
    1152             : 
    1153           0 :   if (aConfig.mIsBalancing && !feasible &&
    1154           0 :       !aPresContext->HasPendingInterrupt()) {
    1155             :     // We may need to reflow one more time at the feasible height to
    1156             :     // get a valid layout.
    1157           0 :     bool skip = false;
    1158           0 :     if (aConfig.mKnownInfeasibleBSize >= availableContentBSize) {
    1159           0 :       aConfig.mColMaxBSize = availableContentBSize;
    1160           0 :       if (mLastBalanceBSize == availableContentBSize) {
    1161           0 :         skip = true;
    1162             :       }
    1163             :     } else {
    1164           0 :       aConfig.mColMaxBSize = aConfig.mKnownFeasibleBSize;
    1165             :     }
    1166           0 :     if (!skip) {
    1167             :       // If our height is unconstrained, make sure that the last column is
    1168             :       // allowed to have arbitrary height here, even though we were balancing.
    1169             :       // Otherwise we'd have to split, and it's not clear what we'd do with
    1170             :       // that.
    1171           0 :       MarkPrincipalChildrenDirty(this);
    1172           0 :       feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, aConfig,
    1173             :                                availableContentBSize == NS_UNCONSTRAINEDSIZE,
    1174           0 :                                &aOutMargin, aColData);
    1175             :     }
    1176             :   }
    1177             : 
    1178           0 :   aRunWasFeasible = feasible;
    1179           0 : }
    1180             : 
    1181             : void
    1182           0 : nsColumnSetFrame::Reflow(nsPresContext*           aPresContext,
    1183             :                          ReflowOutput&     aDesiredSize,
    1184             :                          const ReflowInput& aReflowInput,
    1185             :                          nsReflowStatus&          aStatus)
    1186             : {
    1187           0 :   MarkInReflow();
    1188             :   // Don't support interruption in columns
    1189           0 :   nsPresContext::InterruptPreventer noInterrupts(aPresContext);
    1190             : 
    1191           0 :   DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame");
    1192           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
    1193             : 
    1194             :   // Initialize OUT parameter
    1195           0 :   aStatus.Reset();
    1196             : 
    1197             :   // Our children depend on our block-size if we have a fixed block-size.
    1198           0 :   if (aReflowInput.ComputedBSize() != NS_AUTOHEIGHT) {
    1199           0 :     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    1200             :   } else {
    1201           0 :     RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    1202             :   }
    1203             : 
    1204             : #ifdef DEBUG
    1205           0 :   nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList));
    1206           0 :   for (; !oc.AtEnd(); oc.Next()) {
    1207           0 :     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get()));
    1208             :   }
    1209           0 :   nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList));
    1210           0 :   for (; !eoc.AtEnd(); eoc.Next()) {
    1211           0 :     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get()));
    1212             :   }
    1213             : #endif
    1214             : 
    1215           0 :   nsOverflowAreas ocBounds;
    1216           0 :   nsReflowStatus ocStatus;
    1217           0 :   if (GetPrevInFlow()) {
    1218           0 :     ReflowOverflowContainerChildren(aPresContext, aReflowInput, ocBounds, 0,
    1219           0 :                                     ocStatus);
    1220             :   }
    1221             : 
    1222             :   //------------ Handle Incremental Reflow -----------------
    1223             : 
    1224             :   // If inline size is unconstrained, set aForceAuto to true to allow
    1225             :   // the columns to expand in the inline direction. (This typically
    1226             :   // happens in orthogonal flows where the inline direction is the
    1227             :   // container's block direction).
    1228             :   ReflowConfig config =
    1229             :     ChooseColumnStrategy(aReflowInput,
    1230           0 :                          aReflowInput.ComputedISize() == NS_UNCONSTRAINEDSIZE);
    1231             : 
    1232             :   // If balancing, then we allow the last column to grow to unbounded
    1233             :   // height during the first reflow. This gives us a way to estimate
    1234             :   // what the average column height should be, because we can measure
    1235             :   // the heights of all the columns and sum them up. But don't do this
    1236             :   // if we have a next in flow because we don't want to suck all its
    1237             :   // content back here and then have to push it out again!
    1238           0 :   nsIFrame* nextInFlow = GetNextInFlow();
    1239           0 :   bool unboundedLastColumn = config.mIsBalancing && !nextInFlow;
    1240           0 :   nsCollapsingMargin carriedOutBottomMargin;
    1241             :   ColumnBalanceData colData;
    1242           0 :   colData.mHasExcessBSize = false;
    1243             : 
    1244           0 :   bool feasible = ReflowColumns(aDesiredSize, aReflowInput, aStatus, config,
    1245             :                                 unboundedLastColumn, &carriedOutBottomMargin,
    1246           0 :                                 colData);
    1247             : 
    1248             :   // If we're not balancing, then we're already done, since we should have
    1249             :   // reflown all of our children, and there is no need for a binary search to
    1250             :   // determine proper column height.
    1251           0 :   if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) {
    1252             :     FindBestBalanceBSize(aReflowInput, aPresContext, config, colData,
    1253             :                           aDesiredSize, carriedOutBottomMargin,
    1254           0 :                           unboundedLastColumn, feasible, aStatus);
    1255             :   }
    1256             : 
    1257           0 :   if (aPresContext->HasPendingInterrupt() &&
    1258           0 :       aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) {
    1259             :     // In this situation, we might be lying about our reflow status, because
    1260             :     // our last kid (the one that got interrupted) was incomplete.  Fix that.
    1261           0 :     aStatus.Reset();
    1262             :   }
    1263             : 
    1264           0 :   NS_ASSERTION(aStatus.IsFullyComplete() ||
    1265             :                aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
    1266             :                "Column set should be complete if the available block-size is unconstrained");
    1267             : 
    1268             :   // Merge overflow container bounds and status.
    1269           0 :   aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
    1270           0 :   aStatus.MergeCompletionStatusFrom(ocStatus);
    1271             : 
    1272           0 :   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus, false);
    1273             : 
    1274           0 :   aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin;
    1275             : 
    1276           0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
    1277           0 : }
    1278             : 
    1279             : void
    1280           0 : nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    1281             :                                    const nsRect&           aDirtyRect,
    1282             :                                    const nsDisplayListSet& aLists)
    1283             : {
    1284           0 :   DisplayBorderBackgroundOutline(aBuilder, aLists);
    1285             : 
    1286           0 :   if (IsVisibleForPainting(aBuilder)) {
    1287             :     aLists.BorderBackground()->
    1288           0 :       AppendNewToTop(new (aBuilder)nsDisplayColumnRule(aBuilder, this));
    1289             :   }
    1290             : 
    1291             :   // Our children won't have backgrounds so it doesn't matter where we put them.
    1292           0 :   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
    1293           0 :     BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, aLists);
    1294             :   }
    1295           0 : }
    1296             : 
    1297             : void
    1298           0 : nsColumnSetFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
    1299             : {
    1300             :   // Everything in mFrames is continuations of the first thing in mFrames.
    1301           0 :   nsIFrame* column = mFrames.FirstChild();
    1302             : 
    1303             :   // We might not have any columns, apparently?
    1304           0 :   if (!column) {
    1305           0 :     return;
    1306             :   }
    1307             : 
    1308           0 :   MOZ_ASSERT(column->StyleContext()->GetPseudo() ==
    1309             :                nsCSSAnonBoxes::columnContent,
    1310             :              "What sort of child is this?");
    1311           0 :   aResult.AppendElement(OwnedAnonBox(column));
    1312             : }
    1313             : 
    1314             : #ifdef DEBUG
    1315             : void
    1316           0 : nsColumnSetFrame::SetInitialChildList(ChildListID     aListID,
    1317             :                                       nsFrameList&    aChildList)
    1318             : {
    1319           0 :   MOZ_ASSERT(aListID != kPrincipalList || aChildList.OnlyChild(),
    1320             :              "initial principal child list must have exactly one child");
    1321           0 :   nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList);
    1322           0 : }
    1323             : 
    1324             : void
    1325           0 : nsColumnSetFrame::AppendFrames(ChildListID     aListID,
    1326             :                                nsFrameList&    aFrameList)
    1327             : {
    1328           0 :   MOZ_CRASH("unsupported operation");
    1329             : }
    1330             : 
    1331             : void
    1332           0 : nsColumnSetFrame::InsertFrames(ChildListID     aListID,
    1333             :                                nsIFrame*       aPrevFrame,
    1334             :                                nsFrameList&    aFrameList)
    1335             : {
    1336           0 :   MOZ_CRASH("unsupported operation");
    1337             : }
    1338             : 
    1339             : void
    1340           0 : nsColumnSetFrame::RemoveFrame(ChildListID     aListID,
    1341             :                               nsIFrame*       aOldFrame)
    1342             : {
    1343           0 :   MOZ_CRASH("unsupported operation");
    1344             : }
    1345             : #endif

Generated by: LCOV version 1.13