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