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