BlockReflowState.h (17527B)
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 { 33 Flags() 34 : mIsBStartMarginRoot(false), 35 mIsBEndMarginRoot(false), 36 mShouldApplyBStartMargin(false), 37 mHasLineAdjacentToTop(false), 38 mBlockNeedsFloatManager(false), 39 mIsLineLayoutEmpty(false), 40 mCanHaveOverflowMarkers(false) {} 41 42 // Set in the BlockReflowState constructor when reflowing a "block margin 43 // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for 44 // which margins apply by default). 45 // 46 // The flag is also set when reflowing a frame whose computed BStart border 47 // padding is non-zero. 48 bool mIsBStartMarginRoot : 1; 49 50 // Set in the BlockReflowState constructor when reflowing a "block margin 51 // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for 52 // which margins apply by default). 53 // 54 // The flag is also set when reflowing a frame whose computed BEnd border 55 // padding is non-zero. 56 bool mIsBEndMarginRoot : 1; 57 58 // Set if the BStart margin should be considered when placing a linebox that 59 // contains a block frame. It may be set as a side-effect of calling 60 // nsBlockFrame::ShouldApplyBStartMargin(); once set, 61 // ShouldApplyBStartMargin() uses it as a fast-path way to return whether 62 // the BStart margin should apply. 63 // 64 // If the flag hasn't been set in the block reflow state, then 65 // ShouldApplyBStartMargin() will crawl the line list to see if a block 66 // frame precedes the specified frame. If so, the BStart margin should be 67 // applied, and the flag is set to cache the result. (If not, the BStart 68 // margin will be applied as a result of the generational margin collapsing 69 // logic in nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this 70 // case, the flag won't be set, so subsequent calls to 71 // ShouldApplyBStartMargin() will continue crawl the line list.) 72 // 73 // This flag is also set in the BlockReflowState constructor if 74 // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin 75 // root by default. 76 bool mShouldApplyBStartMargin : 1; 77 78 // Set when mLineAdjacentToTop is valid. 79 bool mHasLineAdjacentToTop : 1; 80 81 // Set when the block has the equivalent of NS_BLOCK_*_BFC. 82 bool mBlockNeedsFloatManager : 1; 83 84 // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing 85 // the current line. 86 bool mIsLineLayoutEmpty : 1; 87 88 // Set when we need text-overflow or -webkit-line-clamp processing. 89 bool mCanHaveOverflowMarkers : 1; 90 }; 91 92 public: 93 BlockReflowState(const ReflowInput& aReflowInput, nsPresContext* aPresContext, 94 nsBlockFrame* aFrame, bool aBStartMarginRoot, 95 bool aBEndMarginRoot, bool aBlockNeedsFloatManager, 96 const nscoord aConsumedBSize, 97 const nscoord aEffectiveContentBoxBSize, 98 const nscoord aInset = 0); 99 100 /** 101 * Unshifts coords, restores availableBSize to reality. 102 * (Constructor applies any cached shift before reflow 103 * so that frames are reflowed with cached shift) 104 */ 105 void UndoAlignContentShift(); 106 107 /** 108 * Get the available reflow space (the area not occupied by floats) 109 * for the current y coordinate. The available space is relative to 110 * our coordinate system, which is the content box, with (0, 0) in the 111 * upper left. 112 * 113 * The parameter aCBWM is the containing block's writing mode, which is 114 * NOT necessarily the mode currently being used by the float manager. 115 * 116 * Returns whether there are floats present at the given block-direction 117 * coordinate and within the inline size of the content rect. 118 * 119 * Note: some codepaths clamp this structure's inline-size to be >=0 "for 120 * compatibility with nsSpaceManager". So if you encounter a nsFlowAreaRect 121 * which appears to have an ISize of 0, you can't necessarily assume that a 122 * 0-ISize float-avoiding block would actually fit; you need to check the 123 * InitialISizeIsNegative flag to see whether that 0 is actually a clamped 124 * negative value (in which case a 0-ISize float-avoiding block *should not* 125 * be considered as fitting, because it would intersect some float). 126 */ 127 nsFlowAreaRect GetFloatAvailableSpace(WritingMode aCBWM) const { 128 return GetFloatAvailableSpace(aCBWM, mBCoord); 129 } 130 nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(WritingMode aCBWM, 131 nscoord aBCoord) const { 132 return GetFloatAvailableSpaceWithState(aCBWM, aBCoord, ShapeType::Margin, 133 nullptr); 134 } 135 nsFlowAreaRect GetFloatAvailableSpace(WritingMode aCBWM, 136 nscoord aBCoord) const { 137 return GetFloatAvailableSpaceWithState(aCBWM, aBCoord, 138 ShapeType::ShapeOutside, nullptr); 139 } 140 nsFlowAreaRect GetFloatAvailableSpaceWithState( 141 WritingMode aCBWM, nscoord aBCoord, ShapeType aShapeType, 142 nsFloatManager::SavedState* aState) const; 143 nsFlowAreaRect GetFloatAvailableSpaceForBSize( 144 WritingMode aCBWM, nscoord aBCoord, nscoord aBSize, 145 nsFloatManager::SavedState* aState) const; 146 147 // @return true if AddFloat was able to place the float; false if the float 148 // did not fit in available space. 149 // 150 // Note: if it returns false, then the float's position and size should be 151 // considered stale/invalid (until the float is successfully placed). 152 bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, 153 nscoord aAvailableISize); 154 155 enum class PlaceFloatResult : uint8_t { 156 Placed, 157 ShouldPlaceBelowCurrentLine, 158 ShouldPlaceInNextContinuation, 159 }; 160 // @param aAvailableISizeInCurrentLine the available inline-size of the 161 // current line if current line is not empty. 162 PlaceFloatResult FlowAndPlaceFloat( 163 nsIFrame* aFloat, mozilla::Maybe<nscoord> aAvailableISizeInCurrentLine = 164 mozilla::Nothing()); 165 166 void PlaceBelowCurrentLineFloats(nsLineBox* aLine); 167 168 // Returns the first coordinate >= aBCoord that clears the 169 // floats indicated by aClearType and has enough inline size between floats 170 // (or no floats remaining) to accomodate aFloatAvoidingBlock. 171 enum class ClearFloatsResult : uint8_t { 172 BCoordNoChange, 173 BCoordAdvanced, 174 FloatsPushedOrSplit, 175 }; 176 std::tuple<nscoord, ClearFloatsResult> ClearFloats( 177 nscoord aBCoord, UsedClear aClearType, 178 nsIFrame* aFloatAvoidingBlock = nullptr); 179 180 nsFloatManager* FloatManager() const { 181 MOZ_ASSERT(mReflowInput.mFloatManager, 182 "Float manager should be valid during the lifetime of " 183 "BlockReflowState!"); 184 return mReflowInput.mFloatManager; 185 } 186 187 // Advances to the next band, i.e., the next horizontal stripe in 188 // which there is a different set of floats. 189 // Return false if it did not advance, which only happens for 190 // constrained heights (and means that we should get pushed to the 191 // next column/page). 192 bool AdvanceToNextBand(const LogicalRect& aFloatAvailableSpace, 193 nscoord* aBCoord) const { 194 WritingMode wm = mReflowInput.GetWritingMode(); 195 if (aFloatAvailableSpace.BSize(wm) > 0) { 196 // See if there's room in the next band. 197 *aBCoord += aFloatAvailableSpace.BSize(wm); 198 } else { 199 if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { 200 // Stop trying to clear here; we'll just get pushed to the 201 // next column or page and try again there. 202 return false; 203 } 204 MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!"); 205 *aBCoord += 1; 206 } 207 return true; 208 } 209 210 bool FloatAvoidingBlockFitsInAvailSpace( 211 nsIFrame* aFloatAvoidingBlock, 212 const nsFlowAreaRect& aFloatAvailableSpace) const; 213 214 // True if the current block-direction coordinate, for placing the children 215 // within the content area, is still adjacent with the block-start of the 216 // content area. 217 bool IsAdjacentWithBStart() const { return mBCoord == ContentBStart(); } 218 219 const LogicalMargin& BorderPadding() const { return mBorderPadding; } 220 221 // Reconstruct the previous block-end margin that goes before |aLine|. 222 void ReconstructMarginBefore(nsLineList::iterator aLine); 223 224 // Caller must have called GetFloatAvailableSpace for the correct position 225 // (which need not be the current mBCoord). 226 void ComputeFloatAvoidingOffsets(nsIFrame* aFloatAvoidingBlock, 227 const LogicalRect& aFloatAvailableSpace, 228 nscoord& aIStartResult, 229 nscoord& aIEndResult) const; 230 231 // Compute the amount of available space for reflowing a block frame at the 232 // current block-direction coordinate mBCoord. Caller must have called 233 // GetFloatAvailableSpace for the current mBCoord. 234 LogicalRect ComputeBlockAvailSpace(nsIFrame* aFrame, 235 const nsFlowAreaRect& aFloatAvailableSpace, 236 bool aBlockAvoidsFloats); 237 238 LogicalSize ComputeAvailableSizeForFloat() const; 239 240 void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord); 241 242 void AdvanceToNextLine() { 243 if (mFlags.mIsLineLayoutEmpty) { 244 mFlags.mIsLineLayoutEmpty = false; 245 } else { 246 mLineNumber++; 247 } 248 } 249 250 //---------------------------------------- 251 252 // This state is the "global" state computed once for the reflow of 253 // the block. 254 255 // The block frame that is using this object 256 nsBlockFrame* const mBlock; 257 258 nsPresContext* const mPresContext; 259 260 const ReflowInput& mReflowInput; 261 262 // The coordinates within the float manager where the block is being 263 // placed <b>after</b> taking into account the blocks border and 264 // padding. This, therefore, represents the inner "content area" (in 265 // float manager coordinates) where child frames will be placed, 266 // including child blocks and floats. 267 nscoord mFloatManagerI, mFloatManagerB; 268 269 // XXX get rid of this 270 nsReflowStatus mReflowStatus; 271 272 // The float manager state as it was before the contents of this 273 // block. This is needed for positioning bullets, since we only want 274 // to move the bullet to flow around floats that were before this 275 // block, not floats inside of it. 276 nsFloatManager::SavedState mFloatManagerStateBefore; 277 278 // The content area to reflow child frames within. This is within 279 // this frame's coordinate system and writing mode, which means 280 // mContentArea.IStart == BorderPadding().IStart and 281 // mContentArea.BStart == BorderPadding().BStart. 282 // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there 283 // is no page/column boundary below (the common case). 284 // mContentArea.BEnd() should only be called after checking that 285 // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise 286 // coordinate overflow may occur. 287 LogicalRect mContentArea; 288 nscoord ContentIStart() const { 289 return mContentArea.IStart(mReflowInput.GetWritingMode()); 290 } 291 nscoord ContentISize() const { 292 return mContentArea.ISize(mReflowInput.GetWritingMode()); 293 } 294 nscoord ContentIEnd() const { 295 return mContentArea.IEnd(mReflowInput.GetWritingMode()); 296 } 297 nscoord ContentBStart() const { 298 return mContentArea.BStart(mReflowInput.GetWritingMode()); 299 } 300 nscoord ContentBSize() const { 301 return mContentArea.BSize(mReflowInput.GetWritingMode()); 302 } 303 nscoord ContentBEnd() const { 304 NS_ASSERTION( 305 ContentBSize() != NS_UNCONSTRAINEDSIZE, 306 "ContentBSize() is unconstrained, so ContentBEnd() may overflow."); 307 return mContentArea.BEnd(mReflowInput.GetWritingMode()); 308 } 309 LogicalSize ContentSize(WritingMode aWM) const { 310 WritingMode wm = mReflowInput.GetWritingMode(); 311 return mContentArea.Size(wm).ConvertTo(aWM, wm); 312 } 313 314 // Amount of inset to apply during line-breaking, used by text-wrap:balance 315 // to adjust line-breaks for more consistent lengths throughout the block. 316 nscoord mInsetForBalance; 317 318 // Physical size. Use only for physical <-> logical coordinate conversion. 319 // 320 // Note: for vertical-rl writing-mode, if mContainerSize's width is 321 // initialized to zero due to unconstrained block-size, lines will be 322 // positioned (physically) incorrectly. We will fix them up at the end of 323 // nsBlockFrame::Reflow() after we know the total block-size of the frame. 324 nsSize mContainerSize; 325 const nsSize& ContainerSize() const { return mContainerSize; } 326 327 /** 328 * Append aFloatCont and its next-in-flows within the same block to mBlock's 329 * pushed floats list. aFloatCont should not be on any child list when making 330 * this call. Its next-in-flows will be removed from mBlock using StealFrame() 331 * before being added to mBlock's pushed floats list. All appended frames will 332 * be marked NS_FRAME_IS_PUSHED_OUT_OF_FLOW. 333 */ 334 void AppendPushedFloatChain(nsIFrame* aFloatCont); 335 336 // Track child overflow continuations. 337 nsOverflowContinuationTracker* mOverflowTracker; 338 339 //---------------------------------------- 340 341 // This state is "running" state updated by the reflow of each line 342 // in the block. This same state is "recovered" when a line is not 343 // dirty and is passed over during incremental reflow. 344 345 // The current line being reflowed 346 // If it is mBlock->end_lines(), then it is invalid. 347 nsLineList::iterator mCurrentLine; 348 349 // When mHasLineAdjacentToTop is set, this refers to a line 350 // which we know is adjacent to the top of the block (in other words, 351 // all lines before it are empty and do not have clearance. This line is 352 // always before the current line. 353 nsLineList::iterator mLineAdjacentToTop; 354 355 // The current block-direction coordinate in the block 356 nscoord mBCoord; 357 358 // mBlock's computed logical border+padding with pre-reflow skip sides applied 359 // (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides). 360 const LogicalMargin mBorderPadding; 361 362 // The overflow areas of all floats placed so far 363 OverflowAreas mFloatOverflowAreas; 364 365 // Previous child. This is used when pulling up a frame to update 366 // the sibling list. 367 nsIFrame* mPrevChild; 368 369 // The previous child frames collapsed bottom margin value. 370 CollapsingMargin mPrevBEndMargin; 371 372 // The current next-in-flow for the block. When lines are pulled 373 // from a next-in-flow, this is used to know which next-in-flow to 374 // pull from. When a next-in-flow is emptied of lines, we advance 375 // this to the next next-in-flow. 376 nsBlockFrame* mNextInFlow; 377 378 //---------------------------------------- 379 380 // Temporary state, for line-reflow. This state is used during the reflow 381 // of a given line, but doesn't have meaning before or after. 382 383 // The list of floats that are "current-line" floats. These are 384 // added to the line after the line has been reflowed, to keep the 385 // list fiddling from being N^2. 386 nsTArray<nsIFrame*> mCurrentLineFloats; 387 388 // The list of floats which are "below current-line" 389 // floats. These are reflowed/placed after the line is reflowed 390 // and placed. Again, this is done to keep the list fiddling from 391 // being N^2. 392 nsTArray<nsIFrame*> mBelowCurrentLineFloats; 393 394 // The list of floats that are waiting on a break opportunity in order to be 395 // placed, since we're on a nowrap context. 396 nsTArray<nsIFrame*> mNoWrapFloats; 397 398 const nscoord mMinLineHeight; 399 400 int32_t mLineNumber; 401 402 Flags mFlags; 403 404 // Cache the result of nsBlockFrame::FindTrailingClear() from mBlock's 405 // prev-in-flows. See nsBlockFrame::ReflowPushedFloats(). 406 UsedClear mTrailingClearFromPIF; 407 408 // The amount of computed content block-size "consumed" by our previous 409 // continuations. 410 const nscoord mConsumedBSize; 411 412 // The amount of block-axis alignment shift to assume during reflow. 413 // Cached between reflows in the AlignContentShift property. 414 // (This system optimizes reflow for not changing the shift.) 415 nscoord mAlignContentShift; 416 417 // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to 418 // place the line. When redoing the line, it will be used to query the 419 // accurate float available space in AddFloat() and 420 // nsBlockFrame::PlaceLine(). 421 Maybe<nscoord> mLineBSize; 422 423 private: 424 bool CanPlaceFloat(nscoord aFloatISize, 425 const nsFlowAreaRect& aFloatAvailableSpace); 426 427 void PushFloatPastBreak(nsIFrame* aFloat); 428 429 void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord); 430 }; 431 432 }; // namespace mozilla 433 434 #endif // BlockReflowState_h