1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* state used in reflow of block frames */
8 
9 #ifndef BlockReflowInput_h
10 #define BlockReflowInput_h
11 
12 #include <tuple>
13 
14 #include "mozilla/ReflowInput.h"
15 #include "nsFloatManager.h"
16 #include "nsLineBox.h"
17 
18 class nsBlockFrame;
19 class nsFrameList;
20 class nsOverflowContinuationTracker;
21 
22 namespace mozilla {
23 
24 // BlockReflowInput contains additional reflow input information that the
25 // block frame uses along with ReflowInput. Like ReflowInput, this
26 // is read-only data that is passed down from a parent frame to its children.
27 class BlockReflowInput {
28   using BandInfoType = nsFloatManager::BandInfoType;
29   using ShapeType = nsFloatManager::ShapeType;
30 
31   // Block reflow input flags.
32   struct Flags {
FlagsFlags33     Flags()
34         : mIsBStartMarginRoot(false),
35           mIsBEndMarginRoot(false),
36           mShouldApplyBStartMargin(false),
37           mHasLineAdjacentToTop(false),
38           mBlockNeedsFloatManager(false),
39           mIsLineLayoutEmpty(false),
40           mIsFloatListInBlockPropertyTable(false),
41           mCanHaveOverflowMarkers(false) {}
42 
43     // Set in the BlockReflowInput constructor when reflowing a "block margin
44     // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for
45     // which margins apply by default).
46     //
47     // The flag is also set when reflowing a frame whose computed BStart border
48     // padding is non-zero.
49     bool mIsBStartMarginRoot : 1;
50 
51     // Set in the BlockReflowInput constructor when reflowing a "block margin
52     // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for
53     // which margins apply by default).
54     //
55     // The flag is also set when reflowing a frame whose computed BEnd border
56     // padding is non-zero.
57     bool mIsBEndMarginRoot : 1;
58 
59     // Set if the BStart margin should be considered when placing a linebox that
60     // contains a block frame. It may be set as a side-effect of calling
61     // nsBlockFrame::ShouldApplyBStartMargin(); once set,
62     // ShouldApplyBStartMargin() uses it as a fast-path way to return whether
63     // the BStart margin should apply.
64     //
65     // If the flag hasn't been set in the block reflow input, then
66     // ShouldApplyBStartMargin() will crawl the line list to see if a block
67     // frame precedes the specified frame. If so, the BStart margin should be
68     // applied, and the flag is set to cache the result. (If not, the BStart
69     // margin will be applied as a result of the generational margin collapsing
70     // logic in nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this
71     // case, the flag won't be set, so subsequent calls to
72     // ShouldApplyBStartMargin() will continue crawl the line list.)
73     //
74     // This flag is also set in the BlockReflowInput constructor if
75     // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin
76     // root by default.
77     bool mShouldApplyBStartMargin : 1;
78 
79     // Set when mLineAdjacentToTop is valid.
80     bool mHasLineAdjacentToTop : 1;
81 
82     // Set when the block has the equivalent of NS_BLOCK_FLOAT_MGR.
83     bool mBlockNeedsFloatManager : 1;
84 
85     // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing
86     // the current line.
87     bool mIsLineLayoutEmpty : 1;
88 
89     // Set when our mPushedFloats list is stored on the block's property table.
90     bool mIsFloatListInBlockPropertyTable : 1;
91 
92     // Set when we need text-overflow or -webkit-line-clamp processing.
93     bool mCanHaveOverflowMarkers : 1;
94   };
95 
96  public:
97   BlockReflowInput(const ReflowInput& aReflowInput, nsPresContext* aPresContext,
98                    nsBlockFrame* aFrame, bool aBStartMarginRoot,
99                    bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
100                    nscoord aConsumedBSize = NS_UNCONSTRAINEDSIZE);
101 
102   /**
103    * Get the available reflow space (the area not occupied by floats)
104    * for the current y coordinate. The available space is relative to
105    * our coordinate system, which is the content box, with (0, 0) in the
106    * upper left.
107    *
108    * Returns whether there are floats present at the given block-direction
109    * coordinate and within the inline size of the content rect.
110    */
GetFloatAvailableSpace()111   nsFlowAreaRect GetFloatAvailableSpace() const {
112     return GetFloatAvailableSpace(mBCoord);
113   }
GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord)114   nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord) const {
115     return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::Margin, nullptr);
116   }
GetFloatAvailableSpace(nscoord aBCoord)117   nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const {
118     return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::ShapeOutside,
119                                            nullptr);
120   }
121   nsFlowAreaRect GetFloatAvailableSpaceWithState(
122       nscoord aBCoord, ShapeType aShapeType,
123       nsFloatManager::SavedState* aState) const;
124   nsFlowAreaRect GetFloatAvailableSpaceForBSize(
125       nscoord aBCoord, nscoord aBSize,
126       nsFloatManager::SavedState* aState) const;
127 
128   /*
129    * The following functions all return true if they were able to place the
130    * float, false if the float did not fit in available space.
131    *
132    * Note: if these functions return false, then the float's position and size
133    * should be considered stale/invalid (until the float is successfully
134    * placed).
135    */
136   bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat,
137                 nscoord aAvailableISize);
138 
139   bool FlowAndPlaceFloat(nsIFrame* aFloat);
140 
141   void PlaceBelowCurrentLineFloats(nsLineBox* aLine);
142 
143   // Returns the first coordinate >= aBCoord that clears the
144   // floats indicated by aBreakType and has enough inline size between floats
145   // (or no floats remaining) to accomodate aReplacedBlock.
146   enum class ClearFloatsResult : uint8_t {
147     BCoordNoChange,
148     BCoordAdvanced,
149     FloatsPushedOrSplit,
150   };
151   std::tuple<nscoord, ClearFloatsResult> ClearFloats(
152       nscoord aBCoord, mozilla::StyleClear aBreakType,
153       nsIFrame* aReplacedBlock = nullptr);
154 
FloatManager()155   nsFloatManager* FloatManager() const {
156     MOZ_ASSERT(mReflowInput.mFloatManager,
157                "Float manager should be valid during the lifetime of "
158                "BlockReflowInput!");
159     return mReflowInput.mFloatManager;
160   }
161 
162   // Advances to the next band, i.e., the next horizontal stripe in
163   // which there is a different set of floats.
164   // Return false if it did not advance, which only happens for
165   // constrained heights (and means that we should get pushed to the
166   // next column/page).
AdvanceToNextBand(const mozilla::LogicalRect & aFloatAvailableSpace,nscoord * aBCoord)167   bool AdvanceToNextBand(const mozilla::LogicalRect& aFloatAvailableSpace,
168                          nscoord* aBCoord) const {
169     mozilla::WritingMode wm = mReflowInput.GetWritingMode();
170     if (aFloatAvailableSpace.BSize(wm) > 0) {
171       // See if there's room in the next band.
172       *aBCoord += aFloatAvailableSpace.BSize(wm);
173     } else {
174       if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
175         // Stop trying to clear here; we'll just get pushed to the
176         // next column or page and try again there.
177         return false;
178       }
179       MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!");
180       *aBCoord += 1;
181     }
182     return true;
183   }
184 
185   bool ReplacedBlockFitsInAvailSpace(
186       nsIFrame* aReplacedBlock,
187       const nsFlowAreaRect& aFloatAvailableSpace) const;
188 
IsAdjacentWithTop()189   bool IsAdjacentWithTop() const {
190     return mBCoord == mBorderPadding.BStart(mReflowInput.GetWritingMode());
191   }
192 
193   /**
194    * Return mBlock's computed physical border+padding with GetSkipSides applied.
195    */
BorderPadding()196   const mozilla::LogicalMargin& BorderPadding() const { return mBorderPadding; }
197 
198   // Reconstruct the previous block-end margin that goes before |aLine|.
199   void ReconstructMarginBefore(nsLineList::iterator aLine);
200 
201   // Caller must have called GetFloatAvailableSpace for the correct position
202   // (which need not be the current mBCoord).
203   void ComputeReplacedBlockOffsetsForFloats(
204       nsIFrame* aFrame, const mozilla::LogicalRect& aFloatAvailableSpace,
205       nscoord& aIStartResult, nscoord& aIEndResult) const;
206 
207   // Caller must have called GetFloatAvailableSpace for the current mBCoord
208   void ComputeBlockAvailSpace(nsIFrame* aFrame,
209                               const nsFlowAreaRect& aFloatAvailableSpace,
210                               bool aBlockAvoidsFloats,
211                               mozilla::LogicalRect& aResult);
212 
213   void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord);
214 
AdvanceToNextLine()215   void AdvanceToNextLine() {
216     if (mFlags.mIsLineLayoutEmpty) {
217       mFlags.mIsLineLayoutEmpty = false;
218     } else {
219       mLineNumber++;
220     }
221   }
222 
223   //----------------------------------------
224 
225   // This state is the "global" state computed once for the reflow of
226   // the block.
227 
228   // The block frame that is using this object
229   nsBlockFrame* mBlock;
230 
231   nsPresContext* mPresContext;
232 
233   const ReflowInput& mReflowInput;
234 
235   // The coordinates within the float manager where the block is being
236   // placed <b>after</b> taking into account the blocks border and
237   // padding. This, therefore, represents the inner "content area" (in
238   // float manager coordinates) where child frames will be placed,
239   // including child blocks and floats.
240   nscoord mFloatManagerI, mFloatManagerB;
241 
242   // XXX get rid of this
243   nsReflowStatus mReflowStatus;
244 
245   // The float manager state as it was before the contents of this
246   // block.  This is needed for positioning bullets, since we only want
247   // to move the bullet to flow around floats that were before this
248   // block, not floats inside of it.
249   nsFloatManager::SavedState mFloatManagerStateBefore;
250 
251   // The content area to reflow child frames within.  This is within
252   // this frame's coordinate system and writing mode, which means
253   // mContentArea.IStart == BorderPadding().IStart and
254   // mContentArea.BStart == BorderPadding().BStart.
255   // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there
256   // is no page/column boundary below (the common case).
257   // mContentArea.BEnd() should only be called after checking that
258   // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise
259   // coordinate overflow may occur.
260   mozilla::LogicalRect mContentArea;
ContentIStart()261   nscoord ContentIStart() const {
262     return mContentArea.IStart(mReflowInput.GetWritingMode());
263   }
ContentISize()264   nscoord ContentISize() const {
265     return mContentArea.ISize(mReflowInput.GetWritingMode());
266   }
ContentIEnd()267   nscoord ContentIEnd() const {
268     return mContentArea.IEnd(mReflowInput.GetWritingMode());
269   }
ContentBStart()270   nscoord ContentBStart() const {
271     return mContentArea.BStart(mReflowInput.GetWritingMode());
272   }
ContentBSize()273   nscoord ContentBSize() const {
274     return mContentArea.BSize(mReflowInput.GetWritingMode());
275   }
ContentBEnd()276   nscoord ContentBEnd() const {
277     NS_ASSERTION(
278         ContentBSize() != NS_UNCONSTRAINEDSIZE,
279         "ContentBSize() is unconstrained, so ContentBEnd() may overflow.");
280     return mContentArea.BEnd(mReflowInput.GetWritingMode());
281   }
ContentSize(mozilla::WritingMode aWM)282   mozilla::LogicalSize ContentSize(mozilla::WritingMode aWM) const {
283     mozilla::WritingMode wm = mReflowInput.GetWritingMode();
284     return mContentArea.Size(wm).ConvertTo(aWM, wm);
285   }
286 
287   // Physical size. Use only for physical <-> logical coordinate conversion.
288   nsSize mContainerSize;
ContainerSize()289   const nsSize& ContainerSize() const { return mContainerSize; }
290 
291   // Continuation out-of-flow float frames that need to move to our
292   // next in flow are placed here during reflow.  It's a pointer to
293   // a frame list stored in the block's property table.
294   nsFrameList* mPushedFloats;
295   // This method makes sure pushed floats are accessible to
296   // StealFrame. Call it before adding any frames to mPushedFloats.
297   void SetupPushedFloatList();
298   /**
299    * Append aFloatCont and its next-in-flows within the same block to
300    * mPushedFloats.  aFloatCont should not be on any child list when
301    * making this call.  Its next-in-flows will be removed from
302    * mBlock using StealFrame() before being added to mPushedFloats.
303    * All appended frames will be marked NS_FRAME_IS_PUSHED_FLOAT.
304    */
305   void AppendPushedFloatChain(nsIFrame* aFloatCont);
306 
307   // Track child overflow continuations.
308   nsOverflowContinuationTracker* mOverflowTracker;
309 
310   //----------------------------------------
311 
312   // This state is "running" state updated by the reflow of each line
313   // in the block. This same state is "recovered" when a line is not
314   // dirty and is passed over during incremental reflow.
315 
316   // The current line being reflowed
317   // If it is mBlock->end_lines(), then it is invalid.
318   nsLineList::iterator mCurrentLine;
319 
320   // When mHasLineAdjacentToTop is set, this refers to a line
321   // which we know is adjacent to the top of the block (in other words,
322   // all lines before it are empty and do not have clearance. This line is
323   // always before the current line.
324   nsLineList::iterator mLineAdjacentToTop;
325 
326   // The current block-direction coordinate in the block
327   nscoord mBCoord;
328 
329   // mBlock's computed physical border+padding with GetSkipSides applied.
330   mozilla::LogicalMargin mBorderPadding;
331 
332   // The overflow areas of all floats placed so far
333   mozilla::OverflowAreas mFloatOverflowAreas;
334 
335   nsFloatCacheFreeList mFloatCacheFreeList;
336 
337   // Previous child. This is used when pulling up a frame to update
338   // the sibling list.
339   nsIFrame* mPrevChild;
340 
341   // The previous child frames collapsed bottom margin value.
342   nsCollapsingMargin mPrevBEndMargin;
343 
344   // The current next-in-flow for the block. When lines are pulled
345   // from a next-in-flow, this is used to know which next-in-flow to
346   // pull from. When a next-in-flow is emptied of lines, we advance
347   // this to the next next-in-flow.
348   nsBlockFrame* mNextInFlow;
349 
350   //----------------------------------------
351 
352   // Temporary state, for line-reflow. This state is used during the reflow
353   // of a given line, but doesn't have meaning before or after.
354 
355   // The list of floats that are "current-line" floats. These are
356   // added to the line after the line has been reflowed, to keep the
357   // list fiddling from being N^2.
358   nsFloatCacheFreeList mCurrentLineFloats;
359 
360   // The list of floats which are "below current-line"
361   // floats. These are reflowed/placed after the line is reflowed
362   // and placed. Again, this is done to keep the list fiddling from
363   // being N^2.
364   nsFloatCacheFreeList mBelowCurrentLineFloats;
365 
366   // The list of floats that are waiting on a break opportunity in order to be
367   // placed, since we're on a nowrap context.
368   nsTArray<nsIFrame*> mNoWrapFloats;
369 
370   nscoord mMinLineHeight;
371 
372   int32_t mLineNumber;
373 
374   Flags mFlags;
375 
376   StyleClear mFloatBreakType;
377 
378   // The amount of computed content block-size "consumed" by our previous
379   // continuations.
380   const nscoord mConsumedBSize;
381 
382   // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to
383   // place the line. When redoing the line, it will be used to query the
384   // accurate float available space in AddFloat() and
385   // nsBlockFrame::PlaceLine().
386   mozilla::Maybe<nscoord> mLineBSize;
387 
388  private:
389   bool CanPlaceFloat(nscoord aFloatISize,
390                      const nsFlowAreaRect& aFloatAvailableSpace);
391 
392   void PushFloatPastBreak(nsIFrame* aFloat);
393 
394   void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord);
395 };
396 
397 };  // namespace mozilla
398 
399 #endif  // BlockReflowInput_h
400