/src/mozilla-central/layout/generic/nsFloatManager.h
Line | Count | Source (jump to first uncovered line) |
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 | | /* 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/TypedEnumBits.h" |
14 | | #include "mozilla/UniquePtr.h" |
15 | | #include "mozilla/WritingModes.h" |
16 | | #include "nsCoord.h" |
17 | | #include "nsFrameList.h" // for DEBUG_FRAME_DUMP |
18 | | #include "nsIntervalSet.h" |
19 | | #include "nsPoint.h" |
20 | | #include "nsTArray.h" |
21 | | |
22 | | class nsIPresShell; |
23 | | class nsIFrame; |
24 | | class nsPresContext; |
25 | | namespace mozilla { |
26 | | struct ReflowInput; |
27 | | class StyleBasicShape; |
28 | | } // namespace mozilla |
29 | | |
30 | | enum class nsFlowAreaRectFlags : uint32_t { |
31 | | NO_FLAGS = 0, |
32 | | HAS_FLOATS = 1 << 0, |
33 | | MAY_WIDEN = 1 << 1 |
34 | | }; |
35 | | MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsFlowAreaRectFlags) |
36 | | |
37 | | /** |
38 | | * The available space for content not occupied by floats is divided |
39 | | * into a sequence of rectangles in the block direction. However, we |
40 | | * need to know not only the rectangle, but also whether it was reduced |
41 | | * (from the content rectangle) by floats that actually intruded into |
42 | | * the content rectangle. If it has been reduced by floats, then we also |
43 | | * track whether the flow area might widen as the floats narrow in the |
44 | | * block direction. |
45 | | */ |
46 | | struct nsFlowAreaRect { |
47 | | mozilla::LogicalRect mRect; |
48 | | |
49 | | nsFlowAreaRectFlags mAreaFlags; |
50 | | |
51 | | nsFlowAreaRect(mozilla::WritingMode aWritingMode, |
52 | | nscoord aICoord, nscoord aBCoord, |
53 | | nscoord aISize, nscoord aBSize, |
54 | | nsFlowAreaRectFlags aAreaFlags) |
55 | | : mRect(aWritingMode, aICoord, aBCoord, aISize, aBSize) |
56 | 0 | , mAreaFlags(aAreaFlags) {} |
57 | | |
58 | 0 | bool HasFloats() const { |
59 | 0 | return (bool)(mAreaFlags & nsFlowAreaRectFlags::HAS_FLOATS); |
60 | 0 | } |
61 | 0 | bool MayWiden() const { |
62 | 0 | return (bool)(mAreaFlags & nsFlowAreaRectFlags::MAY_WIDEN); |
63 | 0 | } |
64 | | }; |
65 | | |
66 | 0 | #define NS_FLOAT_MANAGER_CACHE_SIZE 64 |
67 | | |
68 | | /** |
69 | | * nsFloatManager is responsible for implementing CSS's rules for |
70 | | * positioning floats. An nsFloatManager object is created during reflow for |
71 | | * any block with NS_BLOCK_FLOAT_MGR. During reflow, the float manager for |
72 | | * the nearest such ancestor block is found in ReflowInput::mFloatManager. |
73 | | * |
74 | | * According to the line-relative mappings in CSS Writing Modes spec [1], |
75 | | * line-right and line-left are calculated with respect to the writing mode |
76 | | * of the containing block of the floats. All the writing modes passed to |
77 | | * nsFloatManager methods should be the containing block's writing mode. |
78 | | * |
79 | | * However, according to the abstract-to-physical mappings table [2], the |
80 | | * 'direction' property of the containing block doesn't affect the |
81 | | * interpretation of line-right and line-left. We actually implement this by |
82 | | * passing in the writing mode of the block formatting context (BFC), i.e. |
83 | | * the of BlockReflowInput's writing mode. |
84 | | * |
85 | | * nsFloatManager uses a special logical coordinate space with inline |
86 | | * coordinates on the line-axis and block coordinates on the block-axis |
87 | | * based on the writing mode of the block formatting context. All the |
88 | | * physical types like nsRect, nsPoint, etc. use this coordinate space. See |
89 | | * FloatInfo::mRect for an example. |
90 | | * |
91 | | * [1] https://drafts.csswg.org/css-writing-modes/#line-mappings |
92 | | * [2] https://drafts.csswg.org/css-writing-modes/#logical-to-physical |
93 | | */ |
94 | | class nsFloatManager { |
95 | | public: |
96 | | explicit nsFloatManager(nsIPresShell* aPresShell, mozilla::WritingMode aWM); |
97 | | ~nsFloatManager(); |
98 | | |
99 | | void* operator new(size_t aSize) CPP_THROW_NEW; |
100 | | void operator delete(void* aPtr, size_t aSize); |
101 | | |
102 | | static void Shutdown(); |
103 | | |
104 | | /** |
105 | | * Get float region stored on the frame. (Defaults to mRect if it's |
106 | | * not there.) The float region is the area impacted by this float; |
107 | | * the coordinates are relative to the containing block frame. |
108 | | */ |
109 | | static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM, |
110 | | nsIFrame* aFloatFrame, |
111 | | const nsSize& aContainerSize); |
112 | | /** |
113 | | * Calculate the float region for this frame using aMargin and the |
114 | | * frame's mRect. The region includes the margins around the float, |
115 | | * but doesn't include the relative offsets. |
116 | | * Note that if the frame is or has a continuation, aMargin's top |
117 | | * and/or bottom must be zeroed by the caller. |
118 | | */ |
119 | | static mozilla::LogicalRect CalculateRegionFor( |
120 | | mozilla::WritingMode aWM, |
121 | | nsIFrame* aFloatFrame, |
122 | | const mozilla::LogicalMargin& aMargin, |
123 | | const nsSize& aContainerSize); |
124 | | /** |
125 | | * Store the float region on the frame. The region is stored |
126 | | * as a delta against the mRect, so repositioning the frame will |
127 | | * also reposition the float region. |
128 | | */ |
129 | | static void StoreRegionFor(mozilla::WritingMode aWM, |
130 | | nsIFrame* aFloat, |
131 | | const mozilla::LogicalRect& aRegion, |
132 | | const nsSize& aContainerSize); |
133 | | |
134 | | // Structure that stores the current state of a float manager for |
135 | | // Save/Restore purposes. |
136 | | struct SavedState { |
137 | | explicit SavedState() |
138 | | : mFloatInfoCount(0) |
139 | | , mLineLeft(0) |
140 | | , mBlockStart(0) |
141 | | , mPushedLeftFloatPastBreak(false) |
142 | | , mPushedRightFloatPastBreak(false) |
143 | | , mSplitLeftFloatAcrossBreak(false) |
144 | 0 | , mSplitRightFloatAcrossBreak(false) {} |
145 | | |
146 | | private: |
147 | | uint32_t mFloatInfoCount; |
148 | | nscoord mLineLeft, mBlockStart; |
149 | | bool mPushedLeftFloatPastBreak; |
150 | | bool mPushedRightFloatPastBreak; |
151 | | bool mSplitLeftFloatAcrossBreak; |
152 | | bool mSplitRightFloatAcrossBreak; |
153 | | |
154 | | friend class nsFloatManager; |
155 | | }; |
156 | | |
157 | | /** |
158 | | * Translate the current origin by the specified offsets. This |
159 | | * creates a new local coordinate space relative to the current |
160 | | * coordinate space. |
161 | | */ |
162 | | void Translate(nscoord aLineLeft, nscoord aBlockStart) |
163 | 0 | { |
164 | 0 | mLineLeft += aLineLeft; |
165 | 0 | mBlockStart += aBlockStart; |
166 | 0 | } |
167 | | |
168 | | /** |
169 | | * Returns the current translation from local coordinate space to |
170 | | * world coordinate space. This represents the accumulated calls to |
171 | | * Translate(). |
172 | | */ |
173 | | void GetTranslation(nscoord& aLineLeft, nscoord& aBlockStart) const |
174 | 0 | { |
175 | 0 | aLineLeft = mLineLeft; |
176 | 0 | aBlockStart = mBlockStart; |
177 | 0 | } |
178 | | |
179 | | /** |
180 | | * Get information about the area available to content that flows |
181 | | * around floats. Two different types of space can be requested: |
182 | | * BandFromPoint: returns the band containing block-dir coordinate |
183 | | * |aBCoord| (though actually with the top truncated to begin at |
184 | | * aBCoord), but up to at most |aBSize| (which may be nscoord_MAX). |
185 | | * This will return the tallest rectangle whose block start is |
186 | | * |aBCoord| and in which there are no changes in what floats are |
187 | | * on the sides of that rectangle, but will limit the block size |
188 | | * of the rectangle to |aBSize|. The inline start and end edges |
189 | | * of the rectangle give the area available for line boxes in that |
190 | | * space. The inline size of this resulting rectangle will not be |
191 | | * negative. |
192 | | * WidthWithinHeight: This returns a rectangle whose block start |
193 | | * is aBCoord and whose block size is exactly aBSize. Its inline |
194 | | * start and end edges give the corresponding edges of the space |
195 | | * that can be used for line boxes *throughout* that space. (It |
196 | | * is possible that more inline space could be used in part of the |
197 | | * space if a float begins or ends in it.) The inline size of the |
198 | | * resulting rectangle can be negative. |
199 | | * |
200 | | * ShapeType can be used to request two different types of flow areas. |
201 | | * (This is the float area defined in CSS Shapes Module Level 1 ยง1.4): |
202 | | * Margin: uses the float element's margin-box to request the flow area. |
203 | | * ShapeOutside: uses the float element's shape-outside value to request |
204 | | * the float area. |
205 | | * |
206 | | * @param aBCoord [in] block-dir coordinate for block start of available space |
207 | | * desired, which are positioned relative to the current translation. |
208 | | * @param aBSize [in] see above |
209 | | * @param aContentArea [in] an nsRect representing the content area |
210 | | * @param aState [in] If null, use the current state, otherwise, do |
211 | | * computation based only on floats present in the given |
212 | | * saved state. |
213 | | * @return An nsFlowAreaRect whose: |
214 | | * mRect is the resulting rectangle for line boxes. It will not |
215 | | * extend beyond aContentArea's inline bounds, but may be |
216 | | * narrower when floats are present. |
217 | | * mHasFloats is whether there are floats at the sides of the |
218 | | * return value including those that do not reduce the line box |
219 | | * inline size at all (because they are entirely in the margins) |
220 | | */ |
221 | | enum class BandInfoType { BandFromPoint, WidthWithinHeight }; |
222 | | enum class ShapeType { Margin, ShapeOutside }; |
223 | | nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM, |
224 | | nscoord aBCoord, nscoord aBSize, |
225 | | BandInfoType aBandInfoType, ShapeType aShapeType, |
226 | | mozilla::LogicalRect aContentArea, |
227 | | SavedState* aState, |
228 | | const nsSize& aContainerSize) const; |
229 | | |
230 | | /** |
231 | | * Add a float that comes after all floats previously added. Its |
232 | | * block start must be even with or below the top of all previous |
233 | | * floats. |
234 | | * |
235 | | * aMarginRect is relative to the current translation. The caller |
236 | | * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0. |
237 | | */ |
238 | | void AddFloat(nsIFrame* aFloatFrame, |
239 | | const mozilla::LogicalRect& aMarginRect, |
240 | | mozilla::WritingMode aWM, const nsSize& aContainerSize); |
241 | | |
242 | | /** |
243 | | * Notify that we tried to place a float that could not fit at all and |
244 | | * had to be pushed to the next page/column? (If so, we can't place |
245 | | * any more floats in this page/column because of the rule that the |
246 | | * top of a float cannot be above the top of an earlier float. It |
247 | | * also means that any clear needs to continue to the next column.) |
248 | | */ |
249 | | void SetPushedLeftFloatPastBreak() |
250 | 0 | { mPushedLeftFloatPastBreak = true; } |
251 | | void SetPushedRightFloatPastBreak() |
252 | 0 | { mPushedRightFloatPastBreak = true; } |
253 | | |
254 | | /** |
255 | | * Notify that we split a float, with part of it needing to be pushed |
256 | | * to the next page/column. (This means that any 'clear' needs to |
257 | | * continue to the next page/column.) |
258 | | */ |
259 | | void SetSplitLeftFloatAcrossBreak() |
260 | 0 | { mSplitLeftFloatAcrossBreak = true; } |
261 | | void SetSplitRightFloatAcrossBreak() |
262 | 0 | { mSplitRightFloatAcrossBreak = true; } |
263 | | |
264 | | /** |
265 | | * Remove the regions associated with this floating frame and its |
266 | | * next-sibling list. Some of the frames may never have been added; |
267 | | * we just skip those. This is not fully general; it only works as |
268 | | * long as the N frames to be removed are the last N frames to have |
269 | | * been added; if there's a frame in the middle of them that should |
270 | | * not be removed, YOU LOSE. |
271 | | */ |
272 | | nsresult RemoveTrailingRegions(nsIFrame* aFrameList); |
273 | | |
274 | 0 | bool HasAnyFloats() const { return !mFloats.IsEmpty(); } |
275 | | |
276 | | /** |
277 | | * Methods for dealing with the propagation of float damage during |
278 | | * reflow. |
279 | | */ |
280 | | bool HasFloatDamage() const |
281 | 0 | { |
282 | 0 | return !mFloatDamage.IsEmpty(); |
283 | 0 | } |
284 | | |
285 | | void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
286 | 0 | { |
287 | 0 | mFloatDamage.IncludeInterval(aIntervalBegin + mBlockStart, |
288 | 0 | aIntervalEnd + mBlockStart); |
289 | 0 | } |
290 | | |
291 | | bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const |
292 | 0 | { |
293 | 0 | return mFloatDamage.Intersects(aIntervalBegin + mBlockStart, |
294 | 0 | aIntervalEnd + mBlockStart); |
295 | 0 | } |
296 | | |
297 | | /** |
298 | | * Saves the current state of the float manager into aState. |
299 | | */ |
300 | | void PushState(SavedState* aState); |
301 | | |
302 | | /** |
303 | | * Restores the float manager to the saved state. |
304 | | * |
305 | | * These states must be managed using stack discipline. PopState can only |
306 | | * be used after PushState has been used to save the state, and it can only |
307 | | * be used once --- although it can be omitted; saved states can be ignored. |
308 | | * States must be popped in the reverse order they were pushed. A |
309 | | * call to PopState invalidates any saved states Pushed after the |
310 | | * state passed to PopState was pushed. |
311 | | */ |
312 | | void PopState(SavedState* aState); |
313 | | |
314 | | /** |
315 | | * Get the block start of the last float placed into the float |
316 | | * manager, to enforce the rule that a float can't be above an earlier |
317 | | * float. Returns the minimum nscoord value if there are no floats. |
318 | | * |
319 | | * The result is relative to the current translation. |
320 | | */ |
321 | | nscoord GetLowestFloatTop() const; |
322 | | |
323 | | /** |
324 | | * Return the coordinate of the lowest float matching aBreakType in |
325 | | * this float manager. Returns aBCoord if there are no matching |
326 | | * floats. |
327 | | * |
328 | | * Both aBCoord and the result are relative to the current translation. |
329 | | */ |
330 | | enum { |
331 | | // Tell ClearFloats not to push to nscoord_MAX when floats have been |
332 | | // pushed to the next page/column. |
333 | | DONT_CLEAR_PUSHED_FLOATS = (1<<0) |
334 | | }; |
335 | | nscoord ClearFloats(nscoord aBCoord, mozilla::StyleClear aBreakType, |
336 | | uint32_t aFlags = 0) const; |
337 | | |
338 | | /** |
339 | | * Checks if clear would pass into the floats' BFC's next-in-flow, |
340 | | * i.e. whether floats affecting this clear have continuations. |
341 | | */ |
342 | | bool ClearContinues(mozilla::StyleClear aBreakType) const; |
343 | | |
344 | | void AssertStateMatches(SavedState *aState) const |
345 | 0 | { |
346 | 0 | NS_ASSERTION(aState->mLineLeft == mLineLeft && |
347 | 0 | aState->mBlockStart == mBlockStart && |
348 | 0 | aState->mPushedLeftFloatPastBreak == |
349 | 0 | mPushedLeftFloatPastBreak && |
350 | 0 | aState->mPushedRightFloatPastBreak == |
351 | 0 | mPushedRightFloatPastBreak && |
352 | 0 | aState->mSplitLeftFloatAcrossBreak == |
353 | 0 | mSplitLeftFloatAcrossBreak && |
354 | 0 | aState->mSplitRightFloatAcrossBreak == |
355 | 0 | mSplitRightFloatAcrossBreak && |
356 | 0 | aState->mFloatInfoCount == mFloats.Length(), |
357 | 0 | "float manager state should match saved state"); |
358 | 0 | } |
359 | | |
360 | | #ifdef DEBUG_FRAME_DUMP |
361 | | /** |
362 | | * Dump the state of the float manager out to a file. |
363 | | */ |
364 | | nsresult List(FILE* out) const; |
365 | | #endif |
366 | | |
367 | | private: |
368 | | |
369 | | class ShapeInfo; |
370 | | class RoundedBoxShapeInfo; |
371 | | class EllipseShapeInfo; |
372 | | class PolygonShapeInfo; |
373 | | class ImageShapeInfo; |
374 | | |
375 | | struct FloatInfo { |
376 | | nsIFrame *const mFrame; |
377 | | // The lowest block-ends of left/right floats up to and including |
378 | | // this one. |
379 | | nscoord mLeftBEnd, mRightBEnd; |
380 | | |
381 | | FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBlockStart, |
382 | | const mozilla::LogicalRect& aMarginRect, |
383 | | mozilla::WritingMode aWM, const nsSize& aContainerSize); |
384 | | |
385 | 0 | nscoord LineLeft() const { return mRect.x; } |
386 | 0 | nscoord LineRight() const { return mRect.XMost(); } |
387 | 0 | nscoord ISize() const { return mRect.width; } |
388 | 0 | nscoord BStart() const { return mRect.y; } |
389 | 0 | nscoord BEnd() const { return mRect.YMost(); } |
390 | 0 | nscoord BSize() const { return mRect.height; } |
391 | 0 | bool IsEmpty() const { return mRect.IsEmpty(); } |
392 | | |
393 | | // aBStart and aBEnd are the starting and ending coordinate of a band. |
394 | | // LineLeft() and LineRight() return the innermost line-left extent and |
395 | | // line-right extent within the given band, respectively. |
396 | | nscoord LineLeft(ShapeType aShapeType, |
397 | | const nscoord aBStart, const nscoord aBEnd) const; |
398 | | nscoord LineRight(ShapeType aShapeType, |
399 | | const nscoord aBStart, const nscoord aBEnd) const; |
400 | | nscoord BStart(ShapeType aShapeType) const; |
401 | | nscoord BEnd(ShapeType aShapeType) const; |
402 | | bool IsEmpty(ShapeType aShapeType) const; |
403 | | bool MayNarrowInBlockDirection(ShapeType aShapeType) const; |
404 | | |
405 | | #ifdef NS_BUILD_REFCNT_LOGGING |
406 | | FloatInfo(FloatInfo&& aOther); |
407 | | ~FloatInfo(); |
408 | | #endif |
409 | | |
410 | | // NB! This is really a logical rect in a writing mode suitable for |
411 | | // placing floats, which is not necessarily the actual writing mode |
412 | | // either of the block which created the float manager or the block |
413 | | // that is calling the float manager. The inline coordinates are in |
414 | | // the line-relative axis of the float manager and its block |
415 | | // coordinates are in the float manager's block direction. |
416 | | nsRect mRect; |
417 | | // Pointer to a concrete subclass of ShapeInfo or null, which means that |
418 | | // there is no shape-outside. |
419 | | mozilla::UniquePtr<ShapeInfo> mShapeInfo; |
420 | | }; |
421 | | |
422 | | #ifdef DEBUG |
423 | | // Store the writing mode from the block frame which establishes the block |
424 | | // formatting context (BFC) when the nsFloatManager is created. |
425 | | mozilla::WritingMode mWritingMode; |
426 | | #endif |
427 | | |
428 | | // Translation from local to global coordinate space. |
429 | | nscoord mLineLeft, mBlockStart; |
430 | | // We use 11 here in order to fill up the jemalloc allocatoed chunk nicely, |
431 | | // see https://bugzilla.mozilla.org/show_bug.cgi?id=1362876#c6. |
432 | | AutoTArray<FloatInfo, 11> mFloats; |
433 | | nsIntervalSet mFloatDamage; |
434 | | |
435 | | // Did we try to place a float that could not fit at all and had to be |
436 | | // pushed to the next page/column? If so, we can't place any more |
437 | | // floats in this page/column because of the rule that the top of a |
438 | | // float cannot be above the top of an earlier float. And we also |
439 | | // need to apply this information to 'clear', and thus need to |
440 | | // separate left and right floats. |
441 | | bool mPushedLeftFloatPastBreak; |
442 | | bool mPushedRightFloatPastBreak; |
443 | | |
444 | | // Did we split a float, with part of it needing to be pushed to the |
445 | | // next page/column. This means that any 'clear' needs to continue to |
446 | | // the next page/column. |
447 | | bool mSplitLeftFloatAcrossBreak; |
448 | | bool mSplitRightFloatAcrossBreak; |
449 | | |
450 | | static int32_t sCachedFloatManagerCount; |
451 | | static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; |
452 | | |
453 | | nsFloatManager(const nsFloatManager&) = delete; |
454 | | void operator=(const nsFloatManager&) = delete; |
455 | | }; |
456 | | |
457 | | /** |
458 | | * A helper class to manage maintenance of the float manager during |
459 | | * nsBlockFrame::Reflow. It automatically restores the old float |
460 | | * manager in the reflow input when the object goes out of scope. |
461 | | */ |
462 | | class nsAutoFloatManager { |
463 | | using ReflowInput = mozilla::ReflowInput; |
464 | | |
465 | | public: |
466 | | explicit nsAutoFloatManager(ReflowInput& aReflowInput) |
467 | | : mReflowInput(aReflowInput) |
468 | | , mOld(nullptr) |
469 | 0 | {} |
470 | | |
471 | | ~nsAutoFloatManager(); |
472 | | |
473 | | /** |
474 | | * Create a new float manager for the specified frame. This will |
475 | | * `remember' the old float manager, and install the new float |
476 | | * manager in the reflow input. |
477 | | */ |
478 | | void |
479 | | CreateFloatManager(nsPresContext *aPresContext); |
480 | | |
481 | | protected: |
482 | | ReflowInput &mReflowInput; |
483 | | mozilla::UniquePtr<nsFloatManager> mNew; |
484 | | |
485 | | // A non-owning pointer, which points to the object owned by |
486 | | // nsAutoFloatManager::mNew. |
487 | | nsFloatManager* mOld; |
488 | | }; |
489 | | |
490 | | #endif /* !defined(nsFloatManager_h_) */ |