1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /* class that manages rules for positioning floats */
41 :
42 : #ifndef nsFloatManager_h_
43 : #define nsFloatManager_h_
44 :
45 : #include "mozilla/Attributes.h"
46 :
47 : #include "nsIntervalSet.h"
48 : #include "nsCoord.h"
49 : #include "nsRect.h"
50 : #include "nsTArray.h"
51 :
52 : class nsIPresShell;
53 : class nsIFrame;
54 : struct nsHTMLReflowState;
55 : class nsPresContext;
56 :
57 : /**
58 : * The available space for content not occupied by floats is divided
59 : * into a (vertical) sequence of rectangles. However, we need to know
60 : * not only the rectangle, but also whether it was reduced (from the
61 : * content rectangle) by floats that actually intruded into the content
62 : * rectangle.
63 : */
64 0 : struct nsFlowAreaRect {
65 : nsRect mRect;
66 : bool mHasFloats;
67 :
68 0 : nsFlowAreaRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight,
69 : bool aHasFloats)
70 0 : : mRect(aX, aY, aWidth, aHeight), mHasFloats(aHasFloats) {}
71 : };
72 :
73 : #define NS_FLOAT_MANAGER_CACHE_SIZE 4
74 :
75 : class nsFloatManager {
76 : public:
77 : nsFloatManager(nsIPresShell* aPresShell);
78 : ~nsFloatManager();
79 :
80 : void* operator new(size_t aSize) CPP_THROW_NEW;
81 : void operator delete(void* aPtr, size_t aSize);
82 :
83 : static void Shutdown();
84 :
85 : /**
86 : * Get float region stored on the frame. (Defaults to mRect if it's
87 : * not there.) The float region is the area impacted by this float;
88 : * the coordinates are relative to the containing block frame.
89 : */
90 : static nsRect GetRegionFor(nsIFrame* aFloatFrame);
91 : /**
92 : * Calculate the float region for this frame using aMargin and the
93 : * frame's mRect. The region includes the margins around the float,
94 : * but doesn't include the relative offsets.
95 : * Note that if the frame is or has a continuation, aMargin's top
96 : * and/or bottom must be zeroed by the caller.
97 : */
98 : static nsRect CalculateRegionFor(nsIFrame* aFloatFrame,
99 : const nsMargin& aMargin);
100 : /**
101 : * Store the float region on the frame. The region is stored
102 : * as a delta against the mRect, so repositioning the frame will
103 : * also reposition the float region.
104 : */
105 : static void StoreRegionFor(nsIFrame* aFloat, nsRect& aRegion);
106 :
107 : // Structure that stores the current state of a frame manager for
108 : // Save/Restore purposes.
109 : struct SavedState;
110 : friend struct SavedState;
111 : struct SavedState {
112 : private:
113 : PRUint32 mFloatInfoCount;
114 : nscoord mX, mY;
115 : bool mPushedLeftFloatPastBreak;
116 : bool mPushedRightFloatPastBreak;
117 : bool mSplitLeftFloatAcrossBreak;
118 : bool mSplitRightFloatAcrossBreak;
119 :
120 : friend class nsFloatManager;
121 : };
122 :
123 : /**
124 : * Translate the current origin by the specified (dx, dy). This
125 : * creates a new local coordinate space relative to the current
126 : * coordinate space.
127 : */
128 0 : void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; }
129 :
130 : /**
131 : * Returns the current translation from local coordinate space to
132 : * world coordinate space. This represents the accumulated calls to
133 : * Translate().
134 : */
135 0 : void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; }
136 :
137 : /**
138 : * Get information about the area available to content that flows
139 : * around floats. Two different types of space can be requested:
140 : * BAND_FROM_POINT: returns the band containing vertical coordinate
141 : * |aY| (though actually with the top truncated to begin at aY),
142 : * but up to at most |aHeight| (which may be nscoord_MAX).
143 : * This will return the tallest rectangle whose top is |aY| and in
144 : * which there are no changes in what floats are on the sides of
145 : * that rectangle, but will limit the height of the rectangle to
146 : * |aHeight|. The left and right edges of the rectangle give the
147 : * area available for line boxes in that space. The width of this
148 : * resulting rectangle will not be negative.
149 : * WIDTH_WITHIN_HEIGHT: This returns a rectangle whose top is aY and
150 : * whose height is exactly aHeight. Its left and right edges give
151 : * the left and right edges of the space that can be used for line
152 : * boxes *throughout* that space. (It is possible that more
153 : * horizontal space could be used in part of the space if a float
154 : * begins or ends in it.) The width of the resulting rectangle
155 : * can be negative.
156 : *
157 : * @param aY [in] vertical coordinate for top of available space
158 : * desired
159 : * @param aHeight [in] see above
160 : * @param aContentArea [in] an nsRect representing the content area
161 : * @param aState [in] If null, use the current state, otherwise, do
162 : * computation based only on floats present in the given
163 : * saved state.
164 : * @return An nsFlowAreaRect whose:
165 : * mRect is the resulting rectangle for line boxes. It will not
166 : * extend beyond aContentArea's horizontal bounds, but may be
167 : * narrower when floats are present.
168 : * mBandHasFloats is whether there are floats at the sides of the
169 : * return value including those that do not reduce the line box
170 : * width at all (because they are entirely in the margins)
171 : *
172 : * aY and aAvailSpace are positioned relative to the current translation
173 : */
174 : enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT };
175 : nsFlowAreaRect GetFlowArea(nscoord aY, BandInfoType aInfoType,
176 : nscoord aHeight, nsRect aContentArea,
177 : SavedState* aState) const;
178 :
179 : /**
180 : * Add a float that comes after all floats previously added. Its top
181 : * must be even with or below the top of all previous floats.
182 : *
183 : * aMarginRect is relative to the current translation. The caller
184 : * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0.
185 : */
186 : nsresult AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect);
187 :
188 : /**
189 : * Notify that we tried to place a float that could not fit at all and
190 : * had to be pushed to the next page/column? (If so, we can't place
191 : * any more floats in this page/column because of the rule that the
192 : * top of a float cannot be above the top of an earlier float. It
193 : * also means that any clear needs to continue to the next column.)
194 : */
195 0 : void SetPushedLeftFloatPastBreak()
196 0 : { mPushedLeftFloatPastBreak = true; }
197 0 : void SetPushedRightFloatPastBreak()
198 0 : { mPushedRightFloatPastBreak = true; }
199 :
200 : /**
201 : * Notify that we split a float, with part of it needing to be pushed
202 : * to the next page/column. (This means that any 'clear' needs to
203 : * continue to the next page/column.)
204 : */
205 0 : void SetSplitLeftFloatAcrossBreak()
206 0 : { mSplitLeftFloatAcrossBreak = true; }
207 0 : void SetSplitRightFloatAcrossBreak()
208 0 : { mSplitRightFloatAcrossBreak = true; }
209 :
210 : /**
211 : * Remove the regions associated with this floating frame and its
212 : * next-sibling list. Some of the frames may never have been added;
213 : * we just skip those. This is not fully general; it only works as
214 : * long as the N frames to be removed are the last N frames to have
215 : * been added; if there's a frame in the middle of them that should
216 : * not be removed, YOU LOSE.
217 : */
218 : nsresult RemoveTrailingRegions(nsIFrame* aFrameList);
219 :
220 : private:
221 : struct FloatInfo;
222 : public:
223 :
224 0 : bool HasAnyFloats() const { return !mFloats.IsEmpty(); }
225 :
226 : /**
227 : * Methods for dealing with the propagation of float damage during
228 : * reflow.
229 : */
230 0 : bool HasFloatDamage() const
231 : {
232 0 : return !mFloatDamage.IsEmpty();
233 : }
234 :
235 0 : void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd)
236 : {
237 0 : mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY);
238 0 : }
239 :
240 0 : bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const
241 : {
242 0 : return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY);
243 : }
244 :
245 : /**
246 : * Saves the current state of the float manager into aState.
247 : */
248 : void PushState(SavedState* aState);
249 :
250 : /**
251 : * Restores the float manager to the saved state.
252 : *
253 : * These states must be managed using stack discipline. PopState can only
254 : * be used after PushState has been used to save the state, and it can only
255 : * be used once --- although it can be omitted; saved states can be ignored.
256 : * States must be popped in the reverse order they were pushed. A
257 : * call to PopState invalidates any saved states Pushed after the
258 : * state passed to PopState was pushed.
259 : */
260 : void PopState(SavedState* aState);
261 :
262 : /**
263 : * Get the top of the last float placed into the float manager, to
264 : * enforce the rule that a float can't be above an earlier float.
265 : * Returns the minimum nscoord value if there are no floats.
266 : *
267 : * The result is relative to the current translation.
268 : */
269 : nscoord GetLowestFloatTop() const;
270 :
271 : /**
272 : * Return the coordinate of the lowest float matching aBreakType in this
273 : * float manager. Returns aY if there are no matching floats.
274 : *
275 : * Both aY and the result are relative to the current translation.
276 : */
277 : enum {
278 : // Tell ClearFloats not to push to nscoord_MAX when floats have been
279 : // pushed to the next page/column.
280 : DONT_CLEAR_PUSHED_FLOATS = (1<<0)
281 : };
282 : nscoord ClearFloats(nscoord aY, PRUint8 aBreakType, PRUint32 aFlags = 0) const;
283 :
284 : /**
285 : * Checks if clear would pass into the floats' BFC's next-in-flow,
286 : * i.e. whether floats affecting this clear have continuations.
287 : */
288 : bool ClearContinues(PRUint8 aBreakType) const;
289 :
290 0 : void AssertStateMatches(SavedState *aState) const
291 : {
292 0 : NS_ASSERTION(aState->mX == mX && aState->mY == mY &&
293 : aState->mPushedLeftFloatPastBreak ==
294 : mPushedLeftFloatPastBreak &&
295 : aState->mPushedRightFloatPastBreak ==
296 : mPushedRightFloatPastBreak &&
297 : aState->mSplitLeftFloatAcrossBreak ==
298 : mSplitLeftFloatAcrossBreak &&
299 : aState->mSplitRightFloatAcrossBreak ==
300 : mSplitRightFloatAcrossBreak &&
301 : aState->mFloatInfoCount == mFloats.Length(),
302 : "float manager state should match saved state");
303 0 : }
304 :
305 : #ifdef DEBUG
306 : /**
307 : * Dump the state of the float manager out to a file.
308 : */
309 : nsresult List(FILE* out) const;
310 : #endif
311 :
312 : private:
313 :
314 : struct FloatInfo {
315 : nsIFrame *const mFrame;
316 : nsRect mRect;
317 : // The lowest bottoms of left/right floats up to and including this one.
318 : nscoord mLeftYMost, mRightYMost;
319 :
320 : FloatInfo(nsIFrame* aFrame, const nsRect& aRect);
321 : #ifdef NS_BUILD_REFCNT_LOGGING
322 : FloatInfo(const FloatInfo& aOther);
323 : ~FloatInfo();
324 : #endif
325 : };
326 :
327 : nscoord mX, mY; // translation from local to global coordinate space
328 : nsTArray<FloatInfo> mFloats;
329 : nsIntervalSet mFloatDamage;
330 :
331 : // Did we try to place a float that could not fit at all and had to be
332 : // pushed to the next page/column? If so, we can't place any more
333 : // floats in this page/column because of the rule that the top of a
334 : // float cannot be above the top of an earlier float. And we also
335 : // need to apply this information to 'clear', and thus need to
336 : // separate left and right floats.
337 : bool mPushedLeftFloatPastBreak;
338 : bool mPushedRightFloatPastBreak;
339 :
340 : // Did we split a float, with part of it needing to be pushed to the
341 : // next page/column. This means that any 'clear' needs to continue to
342 : // the next page/column.
343 : bool mSplitLeftFloatAcrossBreak;
344 : bool mSplitRightFloatAcrossBreak;
345 :
346 : static PRInt32 sCachedFloatManagerCount;
347 : static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
348 :
349 : nsFloatManager(const nsFloatManager&) MOZ_DELETE;
350 : void operator=(const nsFloatManager&) MOZ_DELETE;
351 : };
352 :
353 : /**
354 : * A helper class to manage maintenance of the float manager during
355 : * nsBlockFrame::Reflow. It automatically restores the old float
356 : * manager in the reflow state when the object goes out of scope.
357 : */
358 : class nsAutoFloatManager {
359 : public:
360 0 : nsAutoFloatManager(nsHTMLReflowState& aReflowState)
361 : : mReflowState(aReflowState),
362 : mNew(nsnull),
363 0 : mOld(nsnull) {}
364 :
365 : ~nsAutoFloatManager();
366 :
367 : /**
368 : * Create a new float manager for the specified frame. This will
369 : * `remember' the old float manager, and install the new float
370 : * manager in the reflow state.
371 : */
372 : nsresult
373 : CreateFloatManager(nsPresContext *aPresContext);
374 :
375 : protected:
376 : nsHTMLReflowState &mReflowState;
377 : nsFloatManager *mNew;
378 : nsFloatManager *mOld;
379 : };
380 :
381 : #endif /* !defined(nsFloatManager_h_) */
|