1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 // vim:cindent:ts=2:et:sw=2: 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 /* class that manages rules for positioning floats */ 8 9 #ifndef nsFloatManager_h_ 10 #define nsFloatManager_h_ 11 12 #include "mozilla/Attributes.h" 13 #include "mozilla/WritingModes.h" 14 #include "nsCoord.h" 15 #include "nsFrameList.h" // for DEBUG_FRAME_DUMP 16 #include "nsIntervalSet.h" 17 #include "nsTArray.h" 18 19 class nsIPresShell; 20 class nsIFrame; 21 class nsPresContext; 22 namespace mozilla { 23 struct ReflowInput; 24 } // namespace mozilla 25 26 /** 27 * The available space for content not occupied by floats is divided 28 * into a sequence of rectangles in the block direction. However, we 29 * need to know not only the rectangle, but also whether it was reduced 30 * (from the content rectangle) by floats that actually intruded into 31 * the content rectangle. 32 */ 33 struct nsFlowAreaRect { 34 mozilla::LogicalRect mRect; 35 bool mHasFloats; 36 nsFlowAreaRectnsFlowAreaRect37 nsFlowAreaRect(mozilla::WritingMode aWritingMode, 38 nscoord aICoord, nscoord aBCoord, 39 nscoord aISize, nscoord aBSize, 40 bool aHasFloats) 41 : mRect(aWritingMode, aICoord, aBCoord, aISize, aBSize) 42 , mHasFloats(aHasFloats) {} 43 }; 44 45 #define NS_FLOAT_MANAGER_CACHE_SIZE 4 46 47 class nsFloatManager { 48 public: 49 explicit nsFloatManager(nsIPresShell* aPresShell, mozilla::WritingMode aWM); 50 ~nsFloatManager(); 51 52 void* operator new(size_t aSize) CPP_THROW_NEW; 53 void operator delete(void* aPtr, size_t aSize); 54 55 static void Shutdown(); 56 57 /** 58 * Get float region stored on the frame. (Defaults to mRect if it's 59 * not there.) The float region is the area impacted by this float; 60 * the coordinates are relative to the containing block frame. 61 */ 62 static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM, 63 nsIFrame* aFloatFrame, 64 const nsSize& aContainerSize); 65 /** 66 * Calculate the float region for this frame using aMargin and the 67 * frame's mRect. The region includes the margins around the float, 68 * but doesn't include the relative offsets. 69 * Note that if the frame is or has a continuation, aMargin's top 70 * and/or bottom must be zeroed by the caller. 71 */ 72 static mozilla::LogicalRect CalculateRegionFor( 73 mozilla::WritingMode aWM, 74 nsIFrame* aFloatFrame, 75 const mozilla::LogicalMargin& aMargin, 76 const nsSize& aContainerSize); 77 /** 78 * Store the float region on the frame. The region is stored 79 * as a delta against the mRect, so repositioning the frame will 80 * also reposition the float region. 81 */ 82 static void StoreRegionFor(mozilla::WritingMode aWM, 83 nsIFrame* aFloat, 84 const mozilla::LogicalRect& aRegion, 85 const nsSize& aContainerSize); 86 87 // Structure that stores the current state of a frame manager for 88 // Save/Restore purposes. 89 struct SavedState { SavedStateSavedState90 explicit SavedState() {} 91 private: 92 uint32_t mFloatInfoCount; 93 nscoord mLineLeft, mBlockStart; 94 bool mPushedLeftFloatPastBreak; 95 bool mPushedRightFloatPastBreak; 96 bool mSplitLeftFloatAcrossBreak; 97 bool mSplitRightFloatAcrossBreak; 98 99 friend class nsFloatManager; 100 }; 101 102 /** 103 * Translate the current origin by the specified offsets. This 104 * creates a new local coordinate space relative to the current 105 * coordinate space. 106 */ Translate(nscoord aLineLeft,nscoord aBlockStart)107 void Translate(nscoord aLineLeft, nscoord aBlockStart) 108 { 109 mLineLeft += aLineLeft; 110 mBlockStart += aBlockStart; 111 } 112 113 /** 114 * Returns the current translation from local coordinate space to 115 * world coordinate space. This represents the accumulated calls to 116 * Translate(). 117 */ GetTranslation(nscoord & aLineLeft,nscoord & aBlockStart)118 void GetTranslation(nscoord& aLineLeft, nscoord& aBlockStart) const 119 { 120 aLineLeft = mLineLeft; 121 aBlockStart = mBlockStart; 122 } 123 124 /** 125 * Get information about the area available to content that flows 126 * around floats. Two different types of space can be requested: 127 * BAND_FROM_POINT: returns the band containing block-dir coordinate 128 * |aBCoord| (though actually with the top truncated to begin at 129 * aBCoord), but up to at most |aBSize| (which may be nscoord_MAX). 130 * This will return the tallest rectangle whose block start is 131 * |aBCoord| and in which there are no changes in what floats are 132 * on the sides of that rectangle, but will limit the block size 133 * of the rectangle to |aBSize|. The inline start and end edges 134 * of the rectangle give the area available for line boxes in that 135 * space. The inline size of this resulting rectangle will not be 136 * negative. 137 * WIDTH_WITHIN_HEIGHT: This returns a rectangle whose block start 138 * is aBCoord and whose block size is exactly aBSize. Its inline 139 * start and end edges give the corresponding edges of the space 140 * that can be used for line boxes *throughout* that space. (It 141 * is possible that more inline space could be used in part of the 142 * space if a float begins or ends in it.) The inline size of the 143 * resulting rectangle can be negative. 144 * 145 * @param aBCoord [in] block-dir coordinate for block start of 146 * available space desired 147 * @param aBSize [in] see above 148 * @param aContentArea [in] an nsRect representing the content area 149 * @param aState [in] If null, use the current state, otherwise, do 150 * computation based only on floats present in the given 151 * saved state. 152 * @return An nsFlowAreaRect whose: 153 * mRect is the resulting rectangle for line boxes. It will not 154 * extend beyond aContentArea's inline bounds, but may be 155 * narrower when floats are present. 156 * mBandHasFloats is whether there are floats at the sides of the 157 * return value including those that do not reduce the line box 158 * inline size at all (because they are entirely in the margins) 159 * 160 * aBCoord and aAvailSpace are positioned relative to the current translation 161 */ 162 enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT }; 163 nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM, 164 nscoord aBCoord, BandInfoType aInfoType, 165 nscoord aBSize, mozilla::LogicalRect aContentArea, 166 SavedState* aState, 167 const nsSize& aContainerSize) const; 168 169 /** 170 * Add a float that comes after all floats previously added. Its 171 * block start must be even with or below the top of all previous 172 * floats. 173 * 174 * aMarginRect is relative to the current translation. The caller 175 * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0. 176 */ 177 nsresult AddFloat(nsIFrame* aFloatFrame, 178 const mozilla::LogicalRect& aMarginRect, 179 mozilla::WritingMode aWM, const nsSize& aContainerSize); 180 181 /** 182 * Notify that we tried to place a float that could not fit at all and 183 * had to be pushed to the next page/column? (If so, we can't place 184 * any more floats in this page/column because of the rule that the 185 * top of a float cannot be above the top of an earlier float. It 186 * also means that any clear needs to continue to the next column.) 187 */ SetPushedLeftFloatPastBreak()188 void SetPushedLeftFloatPastBreak() 189 { mPushedLeftFloatPastBreak = true; } SetPushedRightFloatPastBreak()190 void SetPushedRightFloatPastBreak() 191 { mPushedRightFloatPastBreak = true; } 192 193 /** 194 * Notify that we split a float, with part of it needing to be pushed 195 * to the next page/column. (This means that any 'clear' needs to 196 * continue to the next page/column.) 197 */ SetSplitLeftFloatAcrossBreak()198 void SetSplitLeftFloatAcrossBreak() 199 { mSplitLeftFloatAcrossBreak = true; } SetSplitRightFloatAcrossBreak()200 void SetSplitRightFloatAcrossBreak() 201 { mSplitRightFloatAcrossBreak = true; } 202 203 /** 204 * Remove the regions associated with this floating frame and its 205 * next-sibling list. Some of the frames may never have been added; 206 * we just skip those. This is not fully general; it only works as 207 * long as the N frames to be removed are the last N frames to have 208 * been added; if there's a frame in the middle of them that should 209 * not be removed, YOU LOSE. 210 */ 211 nsresult RemoveTrailingRegions(nsIFrame* aFrameList); 212 HasAnyFloats()213 bool HasAnyFloats() const { return !mFloats.IsEmpty(); } 214 215 /** 216 * Methods for dealing with the propagation of float damage during 217 * reflow. 218 */ HasFloatDamage()219 bool HasFloatDamage() const 220 { 221 return !mFloatDamage.IsEmpty(); 222 } 223 IncludeInDamage(nscoord aIntervalBegin,nscoord aIntervalEnd)224 void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) 225 { 226 mFloatDamage.IncludeInterval(aIntervalBegin + mBlockStart, 227 aIntervalEnd + mBlockStart); 228 } 229 IntersectsDamage(nscoord aIntervalBegin,nscoord aIntervalEnd)230 bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const 231 { 232 return mFloatDamage.Intersects(aIntervalBegin + mBlockStart, 233 aIntervalEnd + mBlockStart); 234 } 235 236 /** 237 * Saves the current state of the float manager into aState. 238 */ 239 void PushState(SavedState* aState); 240 241 /** 242 * Restores the float manager to the saved state. 243 * 244 * These states must be managed using stack discipline. PopState can only 245 * be used after PushState has been used to save the state, and it can only 246 * be used once --- although it can be omitted; saved states can be ignored. 247 * States must be popped in the reverse order they were pushed. A 248 * call to PopState invalidates any saved states Pushed after the 249 * state passed to PopState was pushed. 250 */ 251 void PopState(SavedState* aState); 252 253 /** 254 * Get the block start of the last float placed into the float 255 * manager, to enforce the rule that a float can't be above an earlier 256 * float. Returns the minimum nscoord value if there are no floats. 257 * 258 * The result is relative to the current translation. 259 */ 260 nscoord GetLowestFloatTop() const; 261 262 /** 263 * Return the coordinate of the lowest float matching aBreakType in 264 * this float manager. Returns aBCoord if there are no matching 265 * floats. 266 * 267 * Both aBCoord and the result are relative to the current translation. 268 */ 269 enum { 270 // Tell ClearFloats not to push to nscoord_MAX when floats have been 271 // pushed to the next page/column. 272 DONT_CLEAR_PUSHED_FLOATS = (1<<0) 273 }; 274 nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType, 275 uint32_t aFlags = 0) const; 276 277 /** 278 * Checks if clear would pass into the floats' BFC's next-in-flow, 279 * i.e. whether floats affecting this clear have continuations. 280 */ 281 bool ClearContinues(mozilla::StyleClear aBreakType) const; 282 AssertStateMatches(SavedState * aState)283 void AssertStateMatches(SavedState *aState) const 284 { 285 NS_ASSERTION(aState->mLineLeft == mLineLeft && 286 aState->mBlockStart == mBlockStart && 287 aState->mPushedLeftFloatPastBreak == 288 mPushedLeftFloatPastBreak && 289 aState->mPushedRightFloatPastBreak == 290 mPushedRightFloatPastBreak && 291 aState->mSplitLeftFloatAcrossBreak == 292 mSplitLeftFloatAcrossBreak && 293 aState->mSplitRightFloatAcrossBreak == 294 mSplitRightFloatAcrossBreak && 295 aState->mFloatInfoCount == mFloats.Length(), 296 "float manager state should match saved state"); 297 } 298 299 #ifdef DEBUG_FRAME_DUMP 300 /** 301 * Dump the state of the float manager out to a file. 302 */ 303 nsresult List(FILE* out) const; 304 #endif 305 306 private: 307 308 struct FloatInfo { 309 nsIFrame *const mFrame; 310 // The lowest block-ends of left/right floats up to and including 311 // this one. 312 nscoord mLeftBEnd, mRightBEnd; 313 314 FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBStart, 315 nscoord aISize, nscoord aBSize); 316 LineLeftFloatInfo317 nscoord LineLeft() const { return mRect.x; } LineRightFloatInfo318 nscoord LineRight() const { return mRect.XMost(); } ISizeFloatInfo319 nscoord ISize() const { return mRect.width; } BStartFloatInfo320 nscoord BStart() const { return mRect.y; } BEndFloatInfo321 nscoord BEnd() const { return mRect.YMost(); } BSizeFloatInfo322 nscoord BSize() const { return mRect.height; } IsEmptyFloatInfo323 bool IsEmpty() const { return mRect.IsEmpty(); } 324 325 #ifdef NS_BUILD_REFCNT_LOGGING 326 FloatInfo(const FloatInfo& aOther); 327 ~FloatInfo(); 328 #endif 329 330 // NB! This is really a logical rect in a writing mode suitable for 331 // placing floats, which is not necessarily the actual writing mode 332 // either of the block which created the frame manager or the block 333 // that is calling the frame manager. The inline coordinates are in 334 // the line-relative axis of the frame manager and its block 335 // coordinates are in the frame manager's real block direction. 336 nsRect mRect; 337 }; 338 339 #ifdef DEBUG 340 mozilla::WritingMode mWritingMode; 341 #endif 342 343 // Translation from local to global coordinate space. 344 nscoord mLineLeft, mBlockStart; 345 nsTArray<FloatInfo> mFloats; 346 nsIntervalSet mFloatDamage; 347 348 // Did we try to place a float that could not fit at all and had to be 349 // pushed to the next page/column? If so, we can't place any more 350 // floats in this page/column because of the rule that the top of a 351 // float cannot be above the top of an earlier float. And we also 352 // need to apply this information to 'clear', and thus need to 353 // separate left and right floats. 354 bool mPushedLeftFloatPastBreak; 355 bool mPushedRightFloatPastBreak; 356 357 // Did we split a float, with part of it needing to be pushed to the 358 // next page/column. This means that any 'clear' needs to continue to 359 // the next page/column. 360 bool mSplitLeftFloatAcrossBreak; 361 bool mSplitRightFloatAcrossBreak; 362 363 static int32_t sCachedFloatManagerCount; 364 static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; 365 366 nsFloatManager(const nsFloatManager&) = delete; 367 void operator=(const nsFloatManager&) = delete; 368 }; 369 370 /** 371 * A helper class to manage maintenance of the float manager during 372 * nsBlockFrame::Reflow. It automatically restores the old float 373 * manager in the reflow input when the object goes out of scope. 374 */ 375 class nsAutoFloatManager { 376 using ReflowInput = mozilla::ReflowInput; 377 378 public: nsAutoFloatManager(ReflowInput & aReflowInput)379 explicit nsAutoFloatManager(ReflowInput& aReflowInput) 380 : mReflowInput(aReflowInput), 381 mNew(nullptr), 382 mOld(nullptr) {} 383 384 ~nsAutoFloatManager(); 385 386 /** 387 * Create a new float manager for the specified frame. This will 388 * `remember' the old float manager, and install the new float 389 * manager in the reflow input. 390 */ 391 void 392 CreateFloatManager(nsPresContext *aPresContext); 393 394 protected: 395 ReflowInput &mReflowInput; 396 nsFloatManager *mNew; 397 nsFloatManager *mOld; 398 }; 399 400 #endif /* !defined(nsFloatManager_h_) */ 401