layout/generic/nsFloatManager.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:6f58f57dc826
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /* class that manages rules for positioning floats */
7
8 #include "nsFloatManager.h"
9 #include "nsIPresShell.h"
10 #include "nsMemory.h"
11 #include "nsHTMLReflowState.h"
12 #include "nsBlockDebugFlags.h"
13 #include "nsError.h"
14 #include <algorithm>
15
16 using namespace mozilla;
17
18 int32_t nsFloatManager::sCachedFloatManagerCount = 0;
19 void* nsFloatManager::sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
20
21 /////////////////////////////////////////////////////////////////////////////
22
23 // PresShell Arena allocate callback (for nsIntervalSet use below)
24 static void*
25 PSArenaAllocCB(size_t aSize, void* aClosure)
26 {
27 return static_cast<nsIPresShell*>(aClosure)->AllocateMisc(aSize);
28 }
29
30 // PresShell Arena free callback (for nsIntervalSet use below)
31 static void
32 PSArenaFreeCB(size_t aSize, void* aPtr, void* aClosure)
33 {
34 static_cast<nsIPresShell*>(aClosure)->FreeMisc(aSize, aPtr);
35 }
36
37 /////////////////////////////////////////////////////////////////////////////
38 // nsFloatManager
39
40 nsFloatManager::nsFloatManager(nsIPresShell* aPresShell)
41 : mX(0), mY(0),
42 mFloatDamage(PSArenaAllocCB, PSArenaFreeCB, aPresShell),
43 mPushedLeftFloatPastBreak(false),
44 mPushedRightFloatPastBreak(false),
45 mSplitLeftFloatAcrossBreak(false),
46 mSplitRightFloatAcrossBreak(false)
47 {
48 MOZ_COUNT_CTOR(nsFloatManager);
49 }
50
51 nsFloatManager::~nsFloatManager()
52 {
53 MOZ_COUNT_DTOR(nsFloatManager);
54 }
55
56 // static
57 void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW
58 {
59 if (sCachedFloatManagerCount > 0) {
60 // We have cached unused instances of this class, return a cached
61 // instance in stead of always creating a new one.
62 return sCachedFloatManagers[--sCachedFloatManagerCount];
63 }
64
65 // The cache is empty, this means we haveto create a new instance using
66 // the global |operator new|.
67 return nsMemory::Alloc(aSize);
68 }
69
70 void
71 nsFloatManager::operator delete(void* aPtr, size_t aSize)
72 {
73 if (!aPtr)
74 return;
75 // This float manager is no longer used, if there's still room in
76 // the cache we'll cache this float manager, unless the layout
77 // module was already shut down.
78
79 if (sCachedFloatManagerCount < NS_FLOAT_MANAGER_CACHE_SIZE &&
80 sCachedFloatManagerCount >= 0) {
81 // There's still space in the cache for more instances, put this
82 // instance in the cache in stead of deleting it.
83
84 sCachedFloatManagers[sCachedFloatManagerCount++] = aPtr;
85 return;
86 }
87
88 // The cache is full, or the layout module has been shut down,
89 // delete this float manager.
90 nsMemory::Free(aPtr);
91 }
92
93
94 /* static */
95 void nsFloatManager::Shutdown()
96 {
97 // The layout module is being shut down, clean up the cache and
98 // disable further caching.
99
100 int32_t i;
101
102 for (i = 0; i < sCachedFloatManagerCount; i++) {
103 void* floatManager = sCachedFloatManagers[i];
104 if (floatManager)
105 nsMemory::Free(floatManager);
106 }
107
108 // Disable further caching.
109 sCachedFloatManagerCount = -1;
110 }
111
112 nsFlowAreaRect
113 nsFloatManager::GetFlowArea(nscoord aYOffset, BandInfoType aInfoType,
114 nscoord aHeight, nsRect aContentArea,
115 SavedState* aState) const
116 {
117 NS_ASSERTION(aHeight >= 0, "unexpected max height");
118 NS_ASSERTION(aContentArea.width >= 0, "unexpected content area width");
119
120 nscoord top = aYOffset + mY;
121 if (top < nscoord_MIN) {
122 NS_WARNING("bad value");
123 top = nscoord_MIN;
124 }
125
126 // Determine the last float that we should consider.
127 uint32_t floatCount;
128 if (aState) {
129 // Use the provided state.
130 floatCount = aState->mFloatInfoCount;
131 NS_ABORT_IF_FALSE(floatCount <= mFloats.Length(), "bad state");
132 } else {
133 // Use our current state.
134 floatCount = mFloats.Length();
135 }
136
137 // If there are no floats at all, or we're below the last one, return
138 // quickly.
139 if (floatCount == 0 ||
140 (mFloats[floatCount-1].mLeftYMost <= top &&
141 mFloats[floatCount-1].mRightYMost <= top)) {
142 return nsFlowAreaRect(aContentArea.x, aYOffset, aContentArea.width,
143 aHeight, false);
144 }
145
146 nscoord bottom;
147 if (aHeight == nscoord_MAX) {
148 // This warning (and the two below) are possible to hit on pages
149 // with really large objects.
150 NS_WARN_IF_FALSE(aInfoType == BAND_FROM_POINT,
151 "bad height");
152 bottom = nscoord_MAX;
153 } else {
154 bottom = top + aHeight;
155 if (bottom < top || bottom > nscoord_MAX) {
156 NS_WARNING("bad value");
157 bottom = nscoord_MAX;
158 }
159 }
160 nscoord left = mX + aContentArea.x;
161 nscoord right = mX + aContentArea.XMost();
162 if (right < left) {
163 NS_WARNING("bad value");
164 right = left;
165 }
166
167 // Walk backwards through the floats until we either hit the front of
168 // the list or we're above |top|.
169 bool haveFloats = false;
170 for (uint32_t i = floatCount; i > 0; --i) {
171 const FloatInfo &fi = mFloats[i-1];
172 if (fi.mLeftYMost <= top && fi.mRightYMost <= top) {
173 // There aren't any more floats that could intersect this band.
174 break;
175 }
176 if (fi.mRect.IsEmpty()) {
177 // For compatibility, ignore floats with empty rects, even though it
178 // disagrees with the spec. (We might want to fix this in the
179 // future, though.)
180 continue;
181 }
182 nscoord floatTop = fi.mRect.y, floatBottom = fi.mRect.YMost();
183 if (top < floatTop && aInfoType == BAND_FROM_POINT) {
184 // This float is below our band. Shrink our band's height if needed.
185 if (floatTop < bottom) {
186 bottom = floatTop;
187 }
188 }
189 // If top == bottom (which happens only with WIDTH_WITHIN_HEIGHT),
190 // we include floats that begin at our 0-height vertical area. We
191 // need to to this to satisfy the invariant that a
192 // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a
193 // BAND_WITHIN_POINT call beginning at its top.
194 else if (top < floatBottom &&
195 (floatTop < bottom || (floatTop == bottom && top == bottom))) {
196 // This float is in our band.
197
198 // Shrink our band's height if needed.
199 if (floatBottom < bottom && aInfoType == BAND_FROM_POINT) {
200 bottom = floatBottom;
201 }
202
203 // Shrink our band's width if needed.
204 if (fi.mFrame->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
205 // A left float.
206 nscoord rightEdge = fi.mRect.XMost();
207 if (rightEdge > left) {
208 left = rightEdge;
209 // Only set haveFloats to true if the float is inside our
210 // containing block. This matches the spec for what some
211 // callers want and disagrees for other callers, so we should
212 // probably provide better information at some point.
213 haveFloats = true;
214 }
215 } else {
216 // A right float.
217 nscoord leftEdge = fi.mRect.x;
218 if (leftEdge < right) {
219 right = leftEdge;
220 // See above.
221 haveFloats = true;
222 }
223 }
224 }
225 }
226
227 nscoord height = (bottom == nscoord_MAX) ? nscoord_MAX : (bottom - top);
228 return nsFlowAreaRect(left - mX, top - mY, right - left, height, haveFloats);
229 }
230
231 nsresult
232 nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect)
233 {
234 NS_ASSERTION(aMarginRect.width >= 0, "negative width!");
235 NS_ASSERTION(aMarginRect.height >= 0, "negative height!");
236
237 FloatInfo info(aFloatFrame, aMarginRect + nsPoint(mX, mY));
238
239 // Set mLeftYMost and mRightYMost.
240 if (HasAnyFloats()) {
241 FloatInfo &tail = mFloats[mFloats.Length() - 1];
242 info.mLeftYMost = tail.mLeftYMost;
243 info.mRightYMost = tail.mRightYMost;
244 } else {
245 info.mLeftYMost = nscoord_MIN;
246 info.mRightYMost = nscoord_MIN;
247 }
248 uint8_t floatStyle = aFloatFrame->StyleDisplay()->mFloats;
249 NS_ASSERTION(floatStyle == NS_STYLE_FLOAT_LEFT ||
250 floatStyle == NS_STYLE_FLOAT_RIGHT, "unexpected float");
251 nscoord& sideYMost = (floatStyle == NS_STYLE_FLOAT_LEFT) ? info.mLeftYMost
252 : info.mRightYMost;
253 nscoord thisYMost = info.mRect.YMost();
254 if (thisYMost > sideYMost)
255 sideYMost = thisYMost;
256
257 if (!mFloats.AppendElement(info))
258 return NS_ERROR_OUT_OF_MEMORY;
259
260 return NS_OK;
261 }
262
263 nsRect
264 nsFloatManager::CalculateRegionFor(nsIFrame* aFloat,
265 const nsMargin& aMargin)
266 {
267 // We consider relatively positioned frames at their original position.
268 nsRect region(aFloat->GetNormalPosition(), aFloat->GetSize());
269
270 // Float region includes its margin
271 region.Inflate(aMargin);
272
273 // Don't store rectangles with negative margin-box width or height in
274 // the float manager; it can't deal with them.
275 if (region.width < 0) {
276 // Preserve the right margin-edge for left floats and the left
277 // margin-edge for right floats
278 const nsStyleDisplay* display = aFloat->StyleDisplay();
279 if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
280 region.x = region.XMost();
281 }
282 region.width = 0;
283 }
284 if (region.height < 0) {
285 region.height = 0;
286 }
287 return region;
288 }
289
290 NS_DECLARE_FRAME_PROPERTY(FloatRegionProperty, nsIFrame::DestroyMargin)
291
292 nsRect
293 nsFloatManager::GetRegionFor(nsIFrame* aFloat)
294 {
295 nsRect region = aFloat->GetRect();
296 void* storedRegion = aFloat->Properties().Get(FloatRegionProperty());
297 if (storedRegion) {
298 nsMargin margin = *static_cast<nsMargin*>(storedRegion);
299 region.Inflate(margin);
300 }
301 return region;
302 }
303
304 void
305 nsFloatManager::StoreRegionFor(nsIFrame* aFloat,
306 nsRect& aRegion)
307 {
308 nsRect rect = aFloat->GetRect();
309 FrameProperties props = aFloat->Properties();
310 if (aRegion.IsEqualEdges(rect)) {
311 props.Delete(FloatRegionProperty());
312 }
313 else {
314 nsMargin* storedMargin = static_cast<nsMargin*>
315 (props.Get(FloatRegionProperty()));
316 if (!storedMargin) {
317 storedMargin = new nsMargin();
318 props.Set(FloatRegionProperty(), storedMargin);
319 }
320 *storedMargin = aRegion - rect;
321 }
322 }
323
324 nsresult
325 nsFloatManager::RemoveTrailingRegions(nsIFrame* aFrameList)
326 {
327 if (!aFrameList) {
328 return NS_OK;
329 }
330 // This could be a good bit simpler if we could guarantee that the
331 // floats given were at the end of our list, so we could just search
332 // for the head of aFrameList. (But we can't;
333 // layout/reftests/bugs/421710-1.html crashes.)
334 nsTHashtable<nsPtrHashKey<nsIFrame> > frameSet(1);
335
336 for (nsIFrame* f = aFrameList; f; f = f->GetNextSibling()) {
337 frameSet.PutEntry(f);
338 }
339
340 uint32_t newLength = mFloats.Length();
341 while (newLength > 0) {
342 if (!frameSet.Contains(mFloats[newLength - 1].mFrame)) {
343 break;
344 }
345 --newLength;
346 }
347 mFloats.TruncateLength(newLength);
348
349 #ifdef DEBUG
350 for (uint32_t i = 0; i < mFloats.Length(); ++i) {
351 NS_ASSERTION(!frameSet.Contains(mFloats[i].mFrame),
352 "Frame region deletion was requested but we couldn't delete it");
353 }
354 #endif
355
356 return NS_OK;
357 }
358
359 void
360 nsFloatManager::PushState(SavedState* aState)
361 {
362 NS_PRECONDITION(aState, "Need a place to save state");
363
364 // This is a cheap push implementation, which
365 // only saves the (x,y) and last frame in the mFrameInfoMap
366 // which is enough info to get us back to where we should be
367 // when pop is called.
368 //
369 // This push/pop mechanism is used to undo any
370 // floats that were added during the unconstrained reflow
371 // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
372 //
373 // It should also be noted that the state for mFloatDamage is
374 // intentionally not saved or restored in PushState() and PopState(),
375 // since that could lead to bugs where damage is missed/dropped when
376 // we move from position A to B (during the intermediate incremental
377 // reflow mentioned above) and then from B to C during the subsequent
378 // reflow. In the typical case A and C will be the same, but not always.
379 // Allowing mFloatDamage to accumulate the damage incurred during both
380 // reflows ensures that nothing gets missed.
381 aState->mX = mX;
382 aState->mY = mY;
383 aState->mPushedLeftFloatPastBreak = mPushedLeftFloatPastBreak;
384 aState->mPushedRightFloatPastBreak = mPushedRightFloatPastBreak;
385 aState->mSplitLeftFloatAcrossBreak = mSplitLeftFloatAcrossBreak;
386 aState->mSplitRightFloatAcrossBreak = mSplitRightFloatAcrossBreak;
387 aState->mFloatInfoCount = mFloats.Length();
388 }
389
390 void
391 nsFloatManager::PopState(SavedState* aState)
392 {
393 NS_PRECONDITION(aState, "No state to restore?");
394
395 mX = aState->mX;
396 mY = aState->mY;
397 mPushedLeftFloatPastBreak = aState->mPushedLeftFloatPastBreak;
398 mPushedRightFloatPastBreak = aState->mPushedRightFloatPastBreak;
399 mSplitLeftFloatAcrossBreak = aState->mSplitLeftFloatAcrossBreak;
400 mSplitRightFloatAcrossBreak = aState->mSplitRightFloatAcrossBreak;
401
402 NS_ASSERTION(aState->mFloatInfoCount <= mFloats.Length(),
403 "somebody misused PushState/PopState");
404 mFloats.TruncateLength(aState->mFloatInfoCount);
405 }
406
407 nscoord
408 nsFloatManager::GetLowestFloatTop() const
409 {
410 if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) {
411 return nscoord_MAX;
412 }
413 if (!HasAnyFloats()) {
414 return nscoord_MIN;
415 }
416 return mFloats[mFloats.Length() - 1].mRect.y - mY;
417 }
418
419 #ifdef DEBUG_FRAME_DUMP
420 void
421 DebugListFloatManager(const nsFloatManager *aFloatManager)
422 {
423 aFloatManager->List(stdout);
424 }
425
426 nsresult
427 nsFloatManager::List(FILE* out) const
428 {
429 if (!HasAnyFloats())
430 return NS_OK;
431
432 for (uint32_t i = 0; i < mFloats.Length(); ++i) {
433 const FloatInfo &fi = mFloats[i];
434 fprintf_stderr(out, "Float %u: frame=%p rect={%d,%d,%d,%d} ymost={l:%d, r:%d}\n",
435 i, static_cast<void*>(fi.mFrame),
436 fi.mRect.x, fi.mRect.y, fi.mRect.width, fi.mRect.height,
437 fi.mLeftYMost, fi.mRightYMost);
438 }
439 return NS_OK;
440 }
441 #endif
442
443 nscoord
444 nsFloatManager::ClearFloats(nscoord aY, uint8_t aBreakType,
445 uint32_t aFlags) const
446 {
447 if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) {
448 return nscoord_MAX;
449 }
450 if (!HasAnyFloats()) {
451 return aY;
452 }
453
454 nscoord bottom = aY + mY;
455
456 const FloatInfo &tail = mFloats[mFloats.Length() - 1];
457 switch (aBreakType) {
458 case NS_STYLE_CLEAR_BOTH:
459 bottom = std::max(bottom, tail.mLeftYMost);
460 bottom = std::max(bottom, tail.mRightYMost);
461 break;
462 case NS_STYLE_CLEAR_LEFT:
463 bottom = std::max(bottom, tail.mLeftYMost);
464 break;
465 case NS_STYLE_CLEAR_RIGHT:
466 bottom = std::max(bottom, tail.mRightYMost);
467 break;
468 default:
469 // Do nothing
470 break;
471 }
472
473 bottom -= mY;
474
475 return bottom;
476 }
477
478 bool
479 nsFloatManager::ClearContinues(uint8_t aBreakType) const
480 {
481 return ((mPushedLeftFloatPastBreak || mSplitLeftFloatAcrossBreak) &&
482 (aBreakType == NS_STYLE_CLEAR_BOTH ||
483 aBreakType == NS_STYLE_CLEAR_LEFT)) ||
484 ((mPushedRightFloatPastBreak || mSplitRightFloatAcrossBreak) &&
485 (aBreakType == NS_STYLE_CLEAR_BOTH ||
486 aBreakType == NS_STYLE_CLEAR_RIGHT));
487 }
488
489 /////////////////////////////////////////////////////////////////////////////
490 // FloatInfo
491
492 nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, const nsRect& aRect)
493 : mFrame(aFrame), mRect(aRect)
494 {
495 MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
496 }
497
498 #ifdef NS_BUILD_REFCNT_LOGGING
499 nsFloatManager::FloatInfo::FloatInfo(const FloatInfo& aOther)
500 : mFrame(aOther.mFrame),
501 mRect(aOther.mRect),
502 mLeftYMost(aOther.mLeftYMost),
503 mRightYMost(aOther.mRightYMost)
504 {
505 MOZ_COUNT_CTOR(nsFloatManager::FloatInfo);
506 }
507
508 nsFloatManager::FloatInfo::~FloatInfo()
509 {
510 MOZ_COUNT_DTOR(nsFloatManager::FloatInfo);
511 }
512 #endif
513
514 //----------------------------------------------------------------------
515
516 nsAutoFloatManager::~nsAutoFloatManager()
517 {
518 // Restore the old float manager in the reflow state if necessary.
519 if (mNew) {
520 #ifdef NOISY_FLOATMANAGER
521 printf("restoring old float manager %p\n", mOld);
522 #endif
523
524 mReflowState.mFloatManager = mOld;
525
526 #ifdef NOISY_FLOATMANAGER
527 if (mOld) {
528 static_cast<nsFrame *>(mReflowState.frame)->ListTag(stdout);
529 printf(": space-manager %p after reflow\n", mOld);
530 mOld->List(stdout);
531 }
532 #endif
533
534 delete mNew;
535 }
536 }
537
538 nsresult
539 nsAutoFloatManager::CreateFloatManager(nsPresContext *aPresContext)
540 {
541 // Create a new float manager and install it in the reflow
542 // state. `Remember' the old float manager so we can restore it
543 // later.
544 mNew = new nsFloatManager(aPresContext->PresShell());
545 if (! mNew)
546 return NS_ERROR_OUT_OF_MEMORY;
547
548 #ifdef NOISY_FLOATMANAGER
549 printf("constructed new float manager %p (replacing %p)\n",
550 mNew, mReflowState.mFloatManager);
551 #endif
552
553 // Set the float manager in the existing reflow state
554 mOld = mReflowState.mFloatManager;
555 mReflowState.mFloatManager = mNew;
556 return NS_OK;
557 }

mercurial