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 BlockReflowState_h
10 #define BlockReflowState_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 // BlockReflowState 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 BlockReflowState {
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 BlockReflowState 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 BlockReflowState 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 state, 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 BlockReflowState 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   BlockReflowState(const ReflowInput& aReflowInput, nsPresContext* aPresContext,
98                    nsBlockFrame* aFrame, bool aBStartMarginRoot,
99                    bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
100                    const nscoord aConsumedBSize,
101                    const nscoord aEffectiveContentBoxBSize);
102 
103   /**
104    * Get the available reflow space (the area not occupied by floats)
105    * for the current y coordinate. The available space is relative to
106    * our coordinate system, which is the content box, with (0, 0) in the
107    * upper left.
108    *
109    * Returns whether there are floats present at the given block-direction
110    * coordinate and within the inline size of the content rect.
111    */
GetFloatAvailableSpace()112   nsFlowAreaRect GetFloatAvailableSpace() const {
113     return GetFloatAvailableSpace(mBCoord);
114   }
GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord)115   nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord) const {
116     return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::Margin, nullptr);
117   }
GetFloatAvailableSpace(nscoord aBCoord)118   nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const {
119     return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::ShapeOutside,
120                                            nullptr);
121   }
122   nsFlowAreaRect GetFloatAvailableSpaceWithState(
123       nscoord aBCoord, ShapeType aShapeType,
124       nsFloatManager::SavedState* aState) const;
125   nsFlowAreaRect GetFloatAvailableSpaceForBSize(
126       nscoord aBCoord, nscoord aBSize,
127       nsFloatManager::SavedState* aState) const;
128 
129   /*
130    * The following functions all return true if they were able to place the
131    * float, false if the float did not fit in available space.
132    *
133    * Note: if these functions return false, then the float's position and size
134    * should be considered stale/invalid (until the float is successfully
135    * placed).
136    */
137   bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat,
138                 nscoord aAvailableISize);
139 
140   bool FlowAndPlaceFloat(nsIFrame* aFloat);
141 
142   void PlaceBelowCurrentLineFloats(nsLineBox* aLine);
143 
144   // Returns the first coordinate >= aBCoord that clears the
145   // floats indicated by aBreakType and has enough inline size between floats
146   // (or no floats remaining) to accomodate aReplacedBlock.
147   enum class ClearFloatsResult : uint8_t {
148     BCoordNoChange,
149     BCoordAdvanced,
150     FloatsPushedOrSplit,
151   };
152   std::tuple<nscoord, ClearFloatsResult> ClearFloats(
153       nscoord aBCoord, mozilla::StyleClear aBreakType,
154       nsIFrame* aReplacedBlock = nullptr);
155 
FloatManager()156   nsFloatManager* FloatManager() const {
157     MOZ_ASSERT(mReflowInput.mFloatManager,
158                "Float manager should be valid during the lifetime of "
159                "BlockReflowState!");
160     return mReflowInput.mFloatManager;
161   }
162 
163   // Advances to the next band, i.e., the next horizontal stripe in
164   // which there is a different set of floats.
165   // Return false if it did not advance, which only happens for
166   // constrained heights (and means that we should get pushed to the
167   // next column/page).
AdvanceToNextBand(const mozilla::LogicalRect & aFloatAvailableSpace,nscoord * aBCoord)168   bool AdvanceToNextBand(const mozilla::LogicalRect& aFloatAvailableSpace,
169                          nscoord* aBCoord) const {
170     mozilla::WritingMode wm = mReflowInput.GetWritingMode();
171     if (aFloatAvailableSpace.BSize(wm) > 0) {
172       // See if there's room in the next band.
173       *aBCoord += aFloatAvailableSpace.BSize(wm);
174     } else {
175       if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
176         // Stop trying to clear here; we'll just get pushed to the
177         // next column or page and try again there.
178         return false;
179       }
180       MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!");
181       *aBCoord += 1;
182     }
183     return true;
184   }
185 
186   bool ReplacedBlockFitsInAvailSpace(
187       nsIFrame* aReplacedBlock,
188       const nsFlowAreaRect& aFloatAvailableSpace) const;
189 
IsAdjacentWithTop()190   bool IsAdjacentWithTop() const {
191     return mBCoord == mBorderPadding.BStart(mReflowInput.GetWritingMode());
192   }
193 
194   /**
195    * Return mBlock's computed physical border+padding with GetSkipSides applied.
196    */
BorderPadding()197   const mozilla::LogicalMargin& BorderPadding() const { return mBorderPadding; }
198 
199   // Reconstruct the previous block-end margin that goes before |aLine|.
200   void ReconstructMarginBefore(nsLineList::iterator aLine);
201 
202   // Caller must have called GetFloatAvailableSpace for the correct position
203   // (which need not be the current mBCoord).
204   void ComputeReplacedBlockOffsetsForFloats(
205       nsIFrame* aFrame, const mozilla::LogicalRect& aFloatAvailableSpace,
206       nscoord& aIStartResult, nscoord& aIEndResult) const;
207 
208   // Compute the amount of available space for reflowing a block frame at the
209   // current block-direction coordinate mBCoord. Caller must have called
210   // GetFloatAvailableSpace for the current mBCoord.
211   mozilla::LogicalRect ComputeBlockAvailSpace(
212       nsIFrame* aFrame, const nsFlowAreaRect& aFloatAvailableSpace,
213       bool aBlockAvoidsFloats);
214 
215   void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord);
216 
AdvanceToNextLine()217   void AdvanceToNextLine() {
218     if (mFlags.mIsLineLayoutEmpty) {
219       mFlags.mIsLineLayoutEmpty = false;
220     } else {
221       mLineNumber++;
222     }
223   }
224 
225   //----------------------------------------
226 
227   // This state is the "global" state computed once for the reflow of
228   // the block.
229 
230   // The block frame that is using this object
231   nsBlockFrame* mBlock;
232 
233   nsPresContext* mPresContext;
234 
235   const ReflowInput& mReflowInput;
236 
237   // The coordinates within the float manager where the block is being
238   // placed <b>after</b> taking into account the blocks border and
239   // padding. This, therefore, represents the inner "content area" (in
240   // float manager coordinates) where child frames will be placed,
241   // including child blocks and floats.
242   nscoord mFloatManagerI, mFloatManagerB;
243 
244   // XXX get rid of this
245   nsReflowStatus mReflowStatus;
246 
247   // The float manager state as it was before the contents of this
248   // block.  This is needed for positioning bullets, since we only want
249   // to move the bullet to flow around floats that were before this
250   // block, not floats inside of it.
251   nsFloatManager::SavedState mFloatManagerStateBefore;
252 
253   // The content area to reflow child frames within.  This is within
254   // this frame's coordinate system and writing mode, which means
255   // mContentArea.IStart == BorderPadding().IStart and
256   // mContentArea.BStart == BorderPadding().BStart.
257   // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there
258   // is no page/column boundary below (the common case).
259   // mContentArea.BEnd() should only be called after checking that
260   // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise
261   // coordinate overflow may occur.
262   mozilla::LogicalRect mContentArea;
ContentIStart()263   nscoord ContentIStart() const {
264     return mContentArea.IStart(mReflowInput.GetWritingMode());
265   }
ContentISize()266   nscoord ContentISize() const {
267     return mContentArea.ISize(mReflowInput.GetWritingMode());
268   }
ContentIEnd()269   nscoord ContentIEnd() const {
270     return mContentArea.IEnd(mReflowInput.GetWritingMode());
271   }
ContentBStart()272   nscoord ContentBStart() const {
273     return mContentArea.BStart(mReflowInput.GetWritingMode());
274   }
ContentBSize()275   nscoord ContentBSize() const {
276     return mContentArea.BSize(mReflowInput.GetWritingMode());
277   }
ContentBEnd()278   nscoord ContentBEnd() const {
279     NS_ASSERTION(
280         ContentBSize() != NS_UNCONSTRAINEDSIZE,
281         "ContentBSize() is unconstrained, so ContentBEnd() may overflow.");
282     return mContentArea.BEnd(mReflowInput.GetWritingMode());
283   }
ContentSize(mozilla::WritingMode aWM)284   mozilla::LogicalSize ContentSize(mozilla::WritingMode aWM) const {
285     mozilla::WritingMode wm = mReflowInput.GetWritingMode();
286     return mContentArea.Size(wm).ConvertTo(aWM, wm);
287   }
288 
289   // Physical size. Use only for physical <-> logical coordinate conversion.
290   nsSize mContainerSize;
ContainerSize()291   const nsSize& ContainerSize() const { return mContainerSize; }
292 
293   // Continuation out-of-flow float frames that need to move to our
294   // next in flow are placed here during reflow.  It's a pointer to
295   // a frame list stored in the block's property table.
296   nsFrameList* mPushedFloats;
297   // This method makes sure pushed floats are accessible to
298   // StealFrame. Call it before adding any frames to mPushedFloats.
299   void SetupPushedFloatList();
300   /**
301    * Append aFloatCont and its next-in-flows within the same block to
302    * mPushedFloats.  aFloatCont should not be on any child list when
303    * making this call.  Its next-in-flows will be removed from
304    * mBlock using StealFrame() before being added to mPushedFloats.
305    * All appended frames will be marked NS_FRAME_IS_PUSHED_FLOAT.
306    */
307   void AppendPushedFloatChain(nsIFrame* aFloatCont);
308 
309   // Track child overflow continuations.
310   nsOverflowContinuationTracker* mOverflowTracker;
311 
312   //----------------------------------------
313 
314   // This state is "running" state updated by the reflow of each line
315   // in the block. This same state is "recovered" when a line is not
316   // dirty and is passed over during incremental reflow.
317 
318   // The current line being reflowed
319   // If it is mBlock->end_lines(), then it is invalid.
320   nsLineList::iterator mCurrentLine;
321 
322   // When mHasLineAdjacentToTop is set, this refers to a line
323   // which we know is adjacent to the top of the block (in other words,
324   // all lines before it are empty and do not have clearance. This line is
325   // always before the current line.
326   nsLineList::iterator mLineAdjacentToTop;
327 
328   // The current block-direction coordinate in the block
329   nscoord mBCoord;
330 
331   // mBlock's computed physical border+padding with GetSkipSides applied.
332   mozilla::LogicalMargin mBorderPadding;
333 
334   // The overflow areas of all floats placed so far
335   mozilla::OverflowAreas mFloatOverflowAreas;
336 
337   nsFloatCacheFreeList mFloatCacheFreeList;
338 
339   // Previous child. This is used when pulling up a frame to update
340   // the sibling list.
341   nsIFrame* mPrevChild;
342 
343   // The previous child frames collapsed bottom margin value.
344   nsCollapsingMargin mPrevBEndMargin;
345 
346   // The current next-in-flow for the block. When lines are pulled
347   // from a next-in-flow, this is used to know which next-in-flow to
348   // pull from. When a next-in-flow is emptied of lines, we advance
349   // this to the next next-in-flow.
350   nsBlockFrame* mNextInFlow;
351 
352   //----------------------------------------
353 
354   // Temporary state, for line-reflow. This state is used during the reflow
355   // of a given line, but doesn't have meaning before or after.
356 
357   // The list of floats that are "current-line" floats. These are
358   // added to the line after the line has been reflowed, to keep the
359   // list fiddling from being N^2.
360   nsFloatCacheFreeList mCurrentLineFloats;
361 
362   // The list of floats which are "below current-line"
363   // floats. These are reflowed/placed after the line is reflowed
364   // and placed. Again, this is done to keep the list fiddling from
365   // being N^2.
366   nsFloatCacheFreeList mBelowCurrentLineFloats;
367 
368   // The list of floats that are waiting on a break opportunity in order to be
369   // placed, since we're on a nowrap context.
370   nsTArray<nsIFrame*> mNoWrapFloats;
371 
372   nscoord mMinLineHeight;
373 
374   int32_t mLineNumber;
375 
376   Flags mFlags;
377 
378   StyleClear mFloatBreakType;
379 
380   // The amount of computed content block-size "consumed" by our previous
381   // continuations.
382   const nscoord mConsumedBSize;
383 
384   // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to
385   // place the line. When redoing the line, it will be used to query the
386   // accurate float available space in AddFloat() and
387   // nsBlockFrame::PlaceLine().
388   mozilla::Maybe<nscoord> mLineBSize;
389 
390  private:
391   bool CanPlaceFloat(nscoord aFloatISize,
392                      const nsFlowAreaRect& aFloatAvailableSpace);
393 
394   void PushFloatPastBreak(nsIFrame* aFloat);
395 
396   void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord);
397 };
398 
399 };  // namespace mozilla
400 
401 #endif  // BlockReflowState_h
402