1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 * class that manages regions of 2-D space, originally designed
41 * generally but actually specific to space occupied by floats
44 #include "nsSpaceManager.h"
49 #include "nsVoidArray.h"
52 #include "nsIPresShell.h"
54 #include "nsHTMLReflowState.h"
55 #include "nsHashSets.h"
57 #include "nsIFrameDebug.h"
60 /////////////////////////////////////////////////////////////////////////////
63 PRInt32
nsSpaceManager::sCachedSpaceManagerCount
= 0;
64 void* nsSpaceManager::sCachedSpaceManagers
[NS_SPACE_MANAGER_CACHE_SIZE
];
66 #define NSCOORD_MIN (-2147483647 - 1) /* minimum signed value */
68 nsSpaceManager::BandList::BandList()
69 : nsSpaceManager::BandRect(NSCOORD_MIN
, NSCOORD_MIN
, NSCOORD_MIN
, NSCOORD_MIN
, (nsIFrame
*)nsnull
)
75 nsSpaceManager::BandList::Clear()
78 BandRect
* bandRect
= Head();
80 while (bandRect
!= this) {
81 BandRect
* nxt
= bandRect
->Next();
91 /////////////////////////////////////////////////////////////////////////////
93 // PresShell Arena allocate callback (for nsIntervalSet use below)
94 PR_STATIC_CALLBACK(void*)
95 PSArenaAllocCB(size_t aSize
, void* aClosure
)
97 return static_cast<nsIPresShell
*>(aClosure
)->AllocateFrame(aSize
);
100 // PresShell Arena free callback (for nsIntervalSet use below)
101 PR_STATIC_CALLBACK(void)
102 PSArenaFreeCB(size_t aSize
, void* aPtr
, void* aClosure
)
104 static_cast<nsIPresShell
*>(aClosure
)->FreeFrame(aSize
, aPtr
);
107 /////////////////////////////////////////////////////////////////////////////
110 nsSpaceManager::nsSpaceManager(nsIPresShell
* aPresShell
, nsIFrame
* aFrame
)
112 mLowestTop(NSCOORD_MIN
),
113 mFloatDamage(PSArenaAllocCB
, PSArenaFreeCB
, aPresShell
),
114 mHaveCachedLeftYMost(PR_TRUE
),
115 mHaveCachedRightYMost(PR_TRUE
),
116 mMaximalLeftYMost(nscoord_MIN
),
117 mMaximalRightYMost(nscoord_MIN
),
118 mCachedBandPosition(nsnull
)
120 MOZ_COUNT_CTOR(nsSpaceManager
);
122 mFrameInfoMap
= nsnull
;
126 nsSpaceManager::ClearFrameInfo()
128 while (mFrameInfoMap
) {
129 FrameInfo
* next
= mFrameInfoMap
->mNext
;
130 delete mFrameInfoMap
;
131 mFrameInfoMap
= next
;
135 nsSpaceManager::~nsSpaceManager()
137 MOZ_COUNT_DTOR(nsSpaceManager
);
143 void* nsSpaceManager::operator new(size_t aSize
) CPP_THROW_NEW
145 if (sCachedSpaceManagerCount
> 0) {
146 // We have cached unused instances of this class, return a cached
147 // instance in stead of always creating a new one.
148 return sCachedSpaceManagers
[--sCachedSpaceManagerCount
];
151 // The cache is empty, this means we haveto create a new instance using
152 // the global |operator new|.
153 return nsMemory::Alloc(aSize
);
157 nsSpaceManager::operator delete(void* aPtr
, size_t aSize
)
161 // This space manager is no longer used, if there's still room in
162 // the cache we'll cache this space manager, unless the layout
163 // module was already shut down.
165 if (sCachedSpaceManagerCount
< NS_SPACE_MANAGER_CACHE_SIZE
&&
166 sCachedSpaceManagerCount
>= 0) {
167 // There's still space in the cache for more instances, put this
168 // instance in the cache in stead of deleting it.
170 sCachedSpaceManagers
[sCachedSpaceManagerCount
++] = aPtr
;
174 // The cache is full, or the layout module has been shut down,
175 // delete this space manager.
176 nsMemory::Free(aPtr
);
181 void nsSpaceManager::Shutdown()
183 // The layout module is being shut down, clean up the cache and
184 // disable further caching.
188 for (i
= 0; i
< sCachedSpaceManagerCount
; i
++) {
189 void* spaceManager
= sCachedSpaceManagers
[i
];
191 nsMemory::Free(spaceManager
);
194 // Disable further caching.
195 sCachedSpaceManagerCount
= -1;
199 nsSpaceManager::XMost(nscoord
& aXMost
) const
202 for (FrameInfo
* fi
= mFrameInfoMap
; fi
; fi
= fi
->mNext
) {
203 xMost
= PR_MAX(xMost
, fi
->mRect
.XMost());
206 return !mBandList
.IsEmpty();
210 nsSpaceManager::YMost(nscoord
& aYMost
) const
214 if (mBandList
.IsEmpty()) {
219 BandRect
* lastRect
= mBandList
.Tail();
221 aYMost
= lastRect
->mBottom
;
229 * Internal function that returns the list of available and unavailable space
232 * Note: If the clip rectangle has 0 width and is aligned exactly at
233 * aBand->mLeft or aBand->mRight, we count it as intersecting the band, and we
234 * return an unavailable-space trapezoid for the band. (The alternative would
235 * be to return a zero-width available-space trapezoid, which would make no
236 * sense. See bug 403129)
238 * @param aBand the first rect in the band
239 * @param aY the y-offset in world coordinates
240 * @param aMaxSize the size to use to constrain the band data
241 * @param aBandData the object to populate with available and unavailable space
244 nsSpaceManager::GetBandAvailableSpace(const BandRect
* aBand
,
246 const nsSize
& aMaxSize
,
247 nsBandData
& aBandData
) const
249 nscoord topOfBand
= aBand
->mTop
;
250 nscoord localY
= aY
- mY
;
251 nscoord height
= PR_MIN(aBand
->mBottom
- aY
, aMaxSize
.height
);
252 nsBandTrapezoid
* trapezoid
= aBandData
.mTrapezoids
;
253 nscoord rightEdge
= mX
+ aMaxSize
.width
;
255 // Initialize the band data
256 aBandData
.mCount
= 0;
258 // Skip any rectangles that are to the left of the local coordinate space
259 while (aBand
->mTop
== topOfBand
) {
260 if (aBand
->mRight
> mX
||
261 (aMaxSize
.width
== 0 && aBand
->mRight
== mX
)) {
265 // Get the next rect in the band
266 aBand
= aBand
->Next();
269 // This is used to track the current x-location within the band. This is in
273 // Process the remaining rectangles that are within the clip width
274 while ((aBand
->mTop
== topOfBand
) &&
275 (aBand
->mLeft
< rightEdge
||
276 (aMaxSize
.width
== 0 && aBand
->mLeft
== mX
))) {
277 // Compare the left edge of the occupied space with the current left
279 if (aBand
->mLeft
> left
) {
280 // The rect is to the right of our current left coordinate, so we've
281 // found some available space
282 if (aBandData
.mCount
>= aBandData
.mSize
) {
283 // Not enough space in the array of trapezoids
284 aBandData
.mCount
+= 2 * aBand
->Length() + 2; // estimate the number needed
285 return NS_ERROR_FAILURE
;
287 trapezoid
->mFrames
= nsnull
;
289 // Assign the trapezoid a rectangular shape. The trapezoid must be in the
290 // local coordinate space, so convert the current left coordinate
291 *trapezoid
= nsRect(left
- mX
, localY
, aBand
->mLeft
- left
, height
);
293 // Move to the next output rect
298 // The rect represents unavailable space, so add another trapezoid
299 if (aBandData
.mCount
>= aBandData
.mSize
) {
300 // Not enough space in the array of trapezoids
301 aBandData
.mCount
+= 2 * aBand
->Length() + 1; // estimate the number needed
302 return NS_ERROR_FAILURE
;
304 NS_ASSERTION(aBand
->mFrames
.Count() > 0, "unexpected frame count");
305 trapezoid
->mFrames
= &aBand
->mFrames
;
307 nscoord x
= aBand
->mLeft
;
308 // The first band can straddle the clip rect
310 // Clip the left edge
314 // Assign the trapezoid a rectangular shape. The trapezoid must be in the
315 // local coordinate space, so convert the rects's left coordinate
316 *trapezoid
= nsRect(x
- mX
, localY
, aBand
->mRight
- x
, height
);
318 // Move to the next output rect
322 // Adjust our current x-location within the band
323 left
= aBand
->mRight
;
325 // Move to the next rect within the band
326 aBand
= aBand
->Next();
329 // No more rects left in the band. If we haven't yet reached the right edge,
330 // then all the remaining space is available
331 if (left
< rightEdge
|| aBandData
.mCount
== 0) {
332 if (aBandData
.mCount
>= aBandData
.mSize
) {
333 // Not enough space in the array of trapezoids
335 return NS_ERROR_FAILURE
;
337 trapezoid
->mFrames
= nsnull
;
339 // Assign the trapezoid a rectangular shape. The trapezoid must be in the
340 // local coordinate space, so convert the current left coordinate
341 *trapezoid
= nsRect(left
- mX
, localY
, rightEdge
- left
, height
);
349 nsSpaceManager::GetBandData(nscoord aYOffset
,
350 const nsSize
& aMaxSize
,
351 nsBandData
& aBandData
) const
353 NS_PRECONDITION(aBandData
.mSize
>= 1, "bad band data");
354 nsresult result
= NS_OK
;
356 // Convert the y-offset to world coordinates
357 nscoord y
= mY
+ aYOffset
;
359 // If there are no unavailable rects or the offset is below the bottommost
360 // band, then all the space is available
362 nscoord maxHeight
= aMaxSize
.height
== NS_UNCONSTRAINEDSIZE
? NS_UNCONSTRAINEDSIZE
363 : PR_MAX(0, aMaxSize
.height
- aYOffset
);
365 if (!YMost(yMost
) || (y
>= yMost
)) {
366 // All the requested space is available
367 aBandData
.mCount
= 1;
368 aBandData
.mTrapezoids
[0] = nsRect(0, aYOffset
, aMaxSize
.width
, maxHeight
);
369 aBandData
.mTrapezoids
[0].mFrames
= nsnull
;
371 // Find the first band that contains the y-offset or is below the y-offset
372 BandRect
* band
= GuessBandWithTopAbove(y
);
374 aBandData
.mCount
= 0;
375 while (nsnull
!= band
) {
376 if (band
->mTop
> y
) {
377 // The band is below the y-offset. The area between the y-offset and
378 // the top of the band is available
379 aBandData
.mCount
= 1;
380 aBandData
.mTrapezoids
[0] =
381 nsRect(0, aYOffset
, aMaxSize
.width
, PR_MIN(band
->mTop
- y
, maxHeight
));
382 aBandData
.mTrapezoids
[0].mFrames
= nsnull
;
384 } else if (y
< band
->mBottom
) {
385 // The band contains the y-offset. Return a list of available and
386 // unavailable rects within the band
387 return GetBandAvailableSpace(band
, y
, nsSize(aMaxSize
.width
, maxHeight
), aBandData
);
389 // Skip to the next band
390 band
= GetNextBand(band
);
395 NS_POSTCONDITION(aBandData
.mCount
> 0, "unexpected band data count");
400 * Skips to the start of the next band.
402 * @param aBandRect A rect within the band
403 * @returns The start of the next band, or nsnull of this is the last band.
405 nsSpaceManager::BandRect
*
406 nsSpaceManager::GetNextBand(const BandRect
* aBandRect
) const
408 nscoord topOfBand
= aBandRect
->mTop
;
410 aBandRect
= aBandRect
->Next();
411 while (aBandRect
!= &mBandList
) {
412 // Check whether this rect is part of the same band
413 if (aBandRect
->mTop
!= topOfBand
) {
414 // We found the start of the next band
415 return (BandRect
*)aBandRect
;
418 aBandRect
= aBandRect
->Next();
426 * Skips to the start of the previous band.
428 * @param aBandRect The first rect within a band
429 * @returns The start of the previous band, or nsnull of this is the first band.
431 nsSpaceManager::BandRect
*
432 nsSpaceManager::GetPrevBand(const BandRect
* aBandRect
) const
434 NS_ASSERTION(aBandRect
->Prev() == &mBandList
||
435 aBandRect
->Prev()->mBottom
<= aBandRect
->mTop
,
436 "aBandRect should be first rect within its band");
438 BandRect
* prev
= aBandRect
->Prev();
439 nscoord topOfBand
= prev
->mTop
;
441 while (prev
!= &mBandList
) {
442 // Check whether the prev rect is part of the same band
443 if (prev
->mTop
!= topOfBand
) {
444 // We found the beginning of this band
445 return (BandRect
*)aBandRect
;
449 prev
= aBandRect
->Prev();
457 * Divides the current band into two vertically
459 * @param aBandRect the first rect in the band
460 * @param aBottom where to split the band. This becomes the bottom of the top
464 nsSpaceManager::DivideBand(BandRect
* aBandRect
, nscoord aBottom
)
466 NS_PRECONDITION(aBottom
< aBandRect
->mBottom
, "bad height");
467 nscoord topOfBand
= aBandRect
->mTop
;
468 BandRect
* nextBand
= GetNextBand(aBandRect
);
470 if (nsnull
== nextBand
) {
471 nextBand
= (BandRect
*)&mBandList
;
474 while (topOfBand
== aBandRect
->mTop
) {
475 // Split the band rect into two vertically
476 BandRect
* bottomBandRect
= aBandRect
->SplitVertically(aBottom
);
478 // Insert the new bottom part
479 nextBand
->InsertBefore(bottomBandRect
);
481 // Move to the next rect in the band
482 aBandRect
= aBandRect
->Next();
487 nsSpaceManager::CanJoinBands(BandRect
* aBand
, BandRect
* aPrevBand
)
490 nscoord topOfBand
= aBand
->mTop
;
491 nscoord topOfPrevBand
= aPrevBand
->mTop
;
493 // The bands can be joined if:
494 // - they're adjacent
495 // - they have the same number of rects
496 // - each rect has the same left and right edge as its corresponding rect, and
497 // the rects are occupied by the same frames
498 if (aPrevBand
->mBottom
== aBand
->mTop
) {
499 // Compare each of the rects in the two bands
501 if ((aBand
->mLeft
!= aPrevBand
->mLeft
) || (aBand
->mRight
!= aPrevBand
->mRight
)) {
502 // The rects have different edges
507 if (!aBand
->HasSameFrameList(aPrevBand
)) {
508 // The rects are occupied by different frames
513 // Move to the next rects within the bands
514 aBand
= aBand
->Next();
515 aPrevBand
= aPrevBand
->Next();
517 // Have we reached the end of either band?
518 PRBool endOfBand
= aBand
->mTop
!= topOfBand
;
519 PRBool endOfPrevBand
= aPrevBand
->mTop
!= topOfPrevBand
;
521 if (endOfBand
|| endOfPrevBand
) {
522 result
= endOfBand
& endOfPrevBand
;
528 // The bands aren't adjacent
536 * Tries to join the two adjacent bands. Returns PR_TRUE if successful and
539 * If the two bands are joined, the previous band is the band that's deleted
542 nsSpaceManager::JoinBands(BandRect
* aBand
, BandRect
* aPrevBand
)
544 if (CanJoinBands(aBand
, aPrevBand
)) {
545 BandRect
* startOfNextBand
= aBand
;
546 // We're going to be removing aPrevBand, so if mCachedBandPosition points
547 // to it just advance it to startOfNextBand.
548 if (mCachedBandPosition
== aPrevBand
) {
549 SetCachedBandPosition(startOfNextBand
);
552 while (aPrevBand
!= startOfNextBand
) {
553 // Adjust the top of the band we're keeping, and then move to the next
554 // rect within the band
555 aBand
->mTop
= aPrevBand
->mTop
;
556 aBand
= aBand
->Next();
558 // Delete the rect from the previous band
559 BandRect
* next
= aPrevBand
->Next();
561 NS_ASSERTION(mCachedBandPosition
!= aPrevBand
,
562 "Removing mCachedBandPosition BandRect?");
575 * Adds a new rect to a band.
577 * @param aBand the first rect in the band
578 * @param aBandRect the band rect to add to the band
581 nsSpaceManager::AddRectToBand(BandRect
* aBand
, BandRect
* aBandRect
)
583 NS_PRECONDITION((aBand
->mTop
== aBandRect
->mTop
) &&
584 (aBand
->mBottom
== aBandRect
->mBottom
), "bad band");
585 NS_PRECONDITION(1 == aBandRect
->mFrames
.Count(), "shared band rect");
586 nscoord topOfBand
= aBand
->mTop
;
588 // Figure out where in the band horizontally to insert the rect
590 // Compare the left edge of the new rect with the left edge of the existing
592 if (aBandRect
->mLeft
< aBand
->mLeft
) {
593 // The new rect's left edge is to the left of the existing rect's left edge.
594 // Could be any of these cases (N is new rect, E is existing rect):
596 // Case 1: left-of Case 2: overlaps Case 3: N.contains(E)
597 // --------------- ---------------- ---------------------
598 // +-----+ +-----+ +-----+ +---------+
599 // | N | | E | | N | | N |
600 // +-----+ +-----+ +-----+ +---------+
605 // Do the two rectangles overlap?
606 if (aBandRect
->mRight
<= aBand
->mLeft
) {
607 // No, the new rect is completely to the left of the existing rect
608 // (case #1). Insert a new rect
609 aBand
->InsertBefore(aBandRect
);
610 if (mCachedBandPosition
== aBand
) {
611 SetCachedBandPosition(aBandRect
);
616 // Yes, they overlap. Compare the right edges.
617 if (aBandRect
->mRight
> aBand
->mRight
) {
618 // The new rect's right edge is to the right of the existing rect's
619 // right edge (case #3). Split the new rect
620 BandRect
* r1
= aBandRect
->SplitHorizontally(aBand
->mLeft
);
622 // Insert the part of the new rect that's to the left of the existing
623 // rect as a new band rect
624 aBand
->InsertBefore(aBandRect
);
625 if (mCachedBandPosition
== aBand
) {
626 SetCachedBandPosition(aBandRect
);
629 // Continue below with the part that overlaps the existing rect
633 if (aBand
->mRight
> aBandRect
->mRight
) {
634 // The existing rect extends past the new rect (case #2). Split the
636 BandRect
* r1
= aBand
->SplitHorizontally(aBandRect
->mRight
);
638 // Insert the new right half of the existing rect
639 aBand
->InsertAfter(r1
);
642 // Insert the part of the new rect that's to the left of the existing
644 aBandRect
->mRight
= aBand
->mLeft
;
645 aBand
->InsertBefore(aBandRect
);
647 if (mCachedBandPosition
== aBand
) {
648 SetCachedBandPosition(aBandRect
);
651 // Mark the existing rect as shared
652 aBand
->AddFrame(aBandRect
->FrameAt(0));
657 if (aBandRect
->mLeft
> aBand
->mLeft
) {
658 // The new rect's left edge is to the right of the existing rect's left
659 // edge. Could be any one of these cases:
661 // Case 4: right-of Case 5: overlaps Case 6: E.Contains(N)
662 // --------------- ---------------- ---------------------
663 // +-----+ +-----+ +-----+ +------------+
664 // | E | | N | | E | | E |
665 // +-----+ +-----+ +-----+ +------------+
670 if (aBandRect
->mLeft
>= aBand
->mRight
) {
671 // The new rect is to the right of the existing rect (case #4), so move
672 // to the next rect in the band
673 aBand
= aBand
->Next();
677 // The rects overlap, so divide the existing rect into two rects: the
678 // part to the left of the new rect, and the part that overlaps
679 BandRect
* r1
= aBand
->SplitHorizontally(aBandRect
->mLeft
);
681 // Insert the new right half of the existing rect, and make it the current
683 aBand
->InsertAfter(r1
);
687 // At this point the left edge of the new rect is the same as the left edge
688 // of the existing rect
689 NS_ASSERTION(aBandRect
->mLeft
== aBand
->mLeft
, "unexpected rect");
691 // Compare which rect is wider, the new rect or the existing rect
692 if (aBand
->mRight
> aBandRect
->mRight
) {
693 // The existing rect is wider (case #6). Divide the existing rect into
694 // two rects: the part that overlaps, and the part to the right of the
696 BandRect
* r1
= aBand
->SplitHorizontally(aBandRect
->mRight
);
698 // Insert the new right half of the existing rect
699 aBand
->InsertAfter(r1
);
701 // Mark the overlap as being shared
702 aBand
->AddFrame(aBandRect
->FrameAt(0));
704 // We no longer need aBandRect, since the area it covers is covered by
705 // the part of aBand that SplitHorizontally left in place. Just delete
711 // Indicate the frames share the existing rect
712 aBand
->AddFrame(aBandRect
->FrameAt(0));
714 if (aBand
->mRight
== aBandRect
->mRight
) {
715 // The new and existing rect have the same right edge. We're all done,
716 // and the new band rect is no longer needed
720 // The new rect is wider than the existing rect (cases #5). Set the
721 // new rect to be the overhang, and move to the next rect within the band
722 aBandRect
->mLeft
= aBand
->mRight
;
723 aBand
= aBand
->Next();
727 } while (aBand
->mTop
== topOfBand
);
729 // Insert a new rect. This is an insertion at the _end_ of the band, so we
730 // absolutely do not want to set mCachedBandPosition to aBandRect here.
731 aBand
->InsertBefore(aBandRect
);
734 // When comparing a rect to a band there are seven cases to consider.
735 // 'R' is the rect and 'B' is the band.
737 // Case 1 Case 2 Case 3 Case 4
738 // ------ ------ ------ ------
739 // +-----+ +-----+ +-----+ +-----+
740 // | R | | R | +-----+ +-----+ | | | |
741 // +-----+ +-----+ | | | R | | B | | B |
742 // +-----+ | B | +-----+ | | +-----+ | |
743 // | | | | +-----+ | R | +-----+
744 // | B | +-----+ +-----+
750 // Case 5 Case 6 Case 7
751 // ------ ------ ------
752 // +-----+ +-----+ +-----+ +-----+
753 // | | | R | | B | | | +-----+
754 // | B | +-----+ +-----+ | R | | B |
762 nsSpaceManager::InsertBandRect(BandRect
* aBandRect
)
764 // If there are no existing bands or this rect is below the bottommost
765 // band, then add a new band
767 if (!YMost(yMost
) || (aBandRect
->mTop
>= yMost
)) {
768 mBandList
.Append(aBandRect
);
769 SetCachedBandPosition(aBandRect
);
773 // Examine each band looking for a band that intersects this rect
774 // First guess a band whose top is above aBandRect->mTop. We know
775 // aBandRect won't overlap any bands before that one.
776 BandRect
* band
= GuessBandWithTopAbove(aBandRect
->mTop
);
778 while (nsnull
!= band
) {
779 // Compare the top edge of this rect with the top edge of the band
780 if (aBandRect
->mTop
< band
->mTop
) {
781 // The top edge of the rect is above the top edge of the band.
782 // Is there any overlap?
783 if (aBandRect
->mBottom
<= band
->mTop
) {
784 // Case #1. This rect is completely above the band, so insert a
785 // new band before the current band
786 band
->InsertBefore(aBandRect
);
787 SetCachedBandPosition(aBandRect
);
788 break; // we're all done
791 // Case #2 and case #7. Divide this rect, creating a new rect for
792 // the part that's above the band
793 BandRect
* bandRect1
= new BandRect(aBandRect
->mLeft
, aBandRect
->mTop
,
794 aBandRect
->mRight
, band
->mTop
,
797 // Insert bandRect1 as a new band
798 band
->InsertBefore(bandRect1
);
800 // Modify this rect to exclude the part above the band
801 aBandRect
->mTop
= band
->mTop
;
803 } else if (aBandRect
->mTop
> band
->mTop
) {
804 // The top edge of the rect is below the top edge of the band. Is there
806 if (aBandRect
->mTop
>= band
->mBottom
) {
807 // Case #5. This rect is below the current band. Skip to the next band
808 band
= GetNextBand(band
);
812 // Case #3 and case #4. Divide the current band into two bands with the
813 // top band being the part that's above the rect
814 DivideBand(band
, aBandRect
->mTop
);
816 // Skip to the bottom band that we just created
817 band
= GetNextBand(band
);
820 // At this point the rect and the band should have the same y-offset
821 NS_ASSERTION(aBandRect
->mTop
== band
->mTop
, "unexpected band");
823 // Is the band higher than the rect?
824 if (band
->mBottom
> aBandRect
->mBottom
) {
825 // Divide the band into two bands with the top band the same height
827 DivideBand(band
, aBandRect
->mBottom
);
830 if (aBandRect
->mBottom
== band
->mBottom
) {
831 // Add the rect to the band
832 SetCachedBandPosition(band
); // Do this before AddRectToBand
833 AddRectToBand(band
, aBandRect
);
837 // Case #4 and case #7. The rect contains the band vertically. Divide
838 // the rect, creating a new rect for the part that overlaps the band
839 BandRect
* bandRect1
= new BandRect(aBandRect
->mLeft
, aBandRect
->mTop
,
840 aBandRect
->mRight
, band
->mBottom
,
843 // Add bandRect1 to the band
844 AddRectToBand(band
, bandRect1
);
846 // Modify aBandRect to be the part below the band
847 aBandRect
->mTop
= band
->mBottom
;
849 // Continue with the next band
850 band
= GetNextBand(band
);
851 if (nsnull
== band
) {
852 // Append a new bottommost band
853 mBandList
.Append(aBandRect
);
854 SetCachedBandPosition(aBandRect
);
862 nsSpaceManager::AddRectRegion(nsIFrame
* aFrame
, const nsRect
& aUnavailableSpace
)
864 NS_PRECONDITION(nsnull
!= aFrame
, "null frame");
866 // Convert the frame to world coordinates
867 nsRect
rect(aUnavailableSpace
.x
+ mX
, aUnavailableSpace
.y
+ mY
,
868 aUnavailableSpace
.width
, aUnavailableSpace
.height
);
870 if (rect
.y
> mLowestTop
)
873 // Create a frame info structure
874 FrameInfo
* frameInfo
= CreateFrameInfo(aFrame
, rect
);
875 if (nsnull
== frameInfo
) {
876 return NS_ERROR_OUT_OF_MEMORY
;
879 if (aUnavailableSpace
.IsEmpty())
882 // Allocate a band rect
883 BandRect
* bandRect
= new BandRect(rect
.x
, rect
.y
,
884 PR_MIN(rect
.XMost(), nscoord_MAX
),
885 PR_MIN(rect
.YMost(), nscoord_MAX
),
887 if (nsnull
== bandRect
) {
888 return NS_ERROR_OUT_OF_MEMORY
;
891 // Insert the band rect
892 InsertBandRect(bandRect
);
897 nsSpaceManager::RemoveTrailingRegions(nsIFrame
* aFrameList
) {
898 nsVoidHashSet frameSet
;
901 for (nsIFrame
* f
= aFrameList
; f
; f
= f
->GetNextSibling()) {
905 // Pop frame regions off as long as they're in the set of frames to
907 while (mFrameInfoMap
&& frameSet
.Contains(mFrameInfoMap
->mFrame
)) {
908 RemoveRegion(mFrameInfoMap
->mFrame
);
912 for (FrameInfo
* frameInfo
= mFrameInfoMap
; frameInfo
;
913 frameInfo
= frameInfo
->mNext
) {
914 NS_ASSERTION(!frameSet
.Contains(frameInfo
->mFrame
),
915 "Frame region deletion was requested but we couldn't delete it");
923 nsSpaceManager::RemoveRegion(nsIFrame
* aFrame
)
925 // Get the frame info associated with aFrame
926 FrameInfo
* frameInfo
= GetFrameInfoFor(aFrame
);
928 if (nsnull
== frameInfo
) {
929 NS_WARNING("no region associated with aFrame");
930 return NS_ERROR_INVALID_ARG
;
933 if (!frameInfo
->mRect
.IsEmpty()) {
934 NS_ASSERTION(!mBandList
.IsEmpty(), "no bands");
935 BandRect
* band
= mBandList
.Head();
936 BandRect
* prevBand
= nsnull
;
937 PRBool prevFoundMatchingRect
= PR_FALSE
;
939 // Iterate each band looking for rects tagged with aFrame
940 while (nsnull
!= band
) {
941 BandRect
* rect
= band
;
942 BandRect
* prevRect
= nsnull
;
943 nscoord topOfBand
= band
->mTop
;
944 PRBool foundMatchingRect
= PR_FALSE
;
945 PRBool prevIsSharedRect
= PR_FALSE
;
947 // Iterate each rect in the band
949 PRBool isSharedRect
= PR_FALSE
;
951 if (rect
->IsOccupiedBy(aFrame
)) {
952 // Remember that we found a matching rect in this band
953 foundMatchingRect
= PR_TRUE
;
955 if (rect
->mFrames
.Count() > 1) {
956 // The band rect is occupied by more than one frame
957 rect
->mFrames
.RemoveElement(aFrame
);
959 // Remember that this rect was being shared by more than one frame
961 isSharedRect
= PR_TRUE
;
963 // The rect isn't shared so just delete it
964 BandRect
* next
= rect
->Next();
967 // The rect we're deleting is the start of the band
968 if (topOfBand
== next
->mTop
) {
973 if (mCachedBandPosition
== rect
) {
974 SetCachedBandPosition(band
);
980 // We don't need to try and coalesce adjacent rects in this case
982 prevIsSharedRect
= PR_FALSE
;
987 // If we found a shared rect occupied by aFrame, then we need to try
988 // and coalesce adjacent rects
989 if (prevIsSharedRect
|| (isSharedRect
&& (nsnull
!= prevRect
))) {
990 NS_ASSERTION(nsnull
!= prevRect
, "no previous rect");
991 if ((prevRect
->mRight
== rect
->mLeft
) && (prevRect
->HasSameFrameList(rect
))) {
992 // Modify the current rect's left edge, and delete the previous rect
993 rect
->mLeft
= prevRect
->mLeft
;
995 if (prevRect
== band
) {
996 // the rect we're deleting is the start of the band
998 if (mCachedBandPosition
== prevRect
) {
999 SetCachedBandPosition(band
);
1006 // Get the next rect in the band
1008 prevIsSharedRect
= isSharedRect
;
1009 rect
= rect
->Next();
1010 } while (rect
->mTop
== topOfBand
);
1012 if (nsnull
!= band
) {
1013 // If we found a rect occupied by aFrame in this band or the previous band
1014 // then try to join the two bands
1015 if ((nsnull
!= prevBand
) && (foundMatchingRect
|| prevFoundMatchingRect
)) {
1016 // Try and join this band with the previous band
1017 JoinBands(band
, prevBand
);
1021 // Move to the next band
1022 prevFoundMatchingRect
= foundMatchingRect
;
1024 band
= (rect
== &mBandList
) ? nsnull
: rect
;
1025 if (!mCachedBandPosition
) {
1026 SetCachedBandPosition(band
);
1031 DestroyFrameInfo(frameInfo
);
1036 nsSpaceManager::ClearRegions()
1040 mLowestTop
= NSCOORD_MIN
;
1041 mHaveCachedLeftYMost
= mHaveCachedRightYMost
= PR_TRUE
;
1042 mMaximalLeftYMost
= mMaximalRightYMost
= nscoord_MIN
;
1046 nsSpaceManager::PushState(SavedState
* aState
)
1048 NS_PRECONDITION(aState
, "Need a place to save state");
1050 // This is a cheap push implementation, which
1051 // only saves the (x,y) and last frame in the mFrameInfoMap
1052 // which is enough info to get us back to where we should be
1053 // when pop is called.
1055 // This push/pop mechanism is used to undo any
1056 // floats that were added during the unconstrained reflow
1057 // in nsBlockReflowContext::DoReflowBlock(). (See bug 96736)
1059 // It should also be noted that the state for mFloatDamage is
1060 // intentionally not saved or restored in PushState() and PopState(),
1061 // since that could lead to bugs where damage is missed/dropped when
1062 // we move from position A to B (during the intermediate incremental
1063 // reflow mentioned above) and then from B to C during the subsequent
1064 // reflow. In the typical case A and C will be the same, but not always.
1065 // Allowing mFloatDamage to accumulate the damage incurred during both
1066 // reflows ensures that nothing gets missed.
1069 aState
->mLowestTop
= mLowestTop
;
1070 aState
->mHaveCachedLeftYMost
= mHaveCachedLeftYMost
;
1071 aState
->mHaveCachedRightYMost
= mHaveCachedRightYMost
;
1072 aState
->mMaximalLeftYMost
= mMaximalLeftYMost
;
1073 aState
->mMaximalRightYMost
= mMaximalRightYMost
;
1075 if (mFrameInfoMap
) {
1076 aState
->mLastFrame
= mFrameInfoMap
->mFrame
;
1078 aState
->mLastFrame
= nsnull
;
1083 nsSpaceManager::PopState(SavedState
* aState
)
1085 NS_PRECONDITION(aState
, "No state to restore?");
1087 // This is a quick and dirty pop implementation, to
1088 // match the current implementation of PushState(). The
1089 // idea here is to remove any frames that have been added
1090 // to the mFrameInfoMap since the last call to PushState().
1092 // Say we don't have cached left- and right-YMost, so that we don't
1093 // try to check for it in RemoveRegion. We'll restore these from
1094 // the state anyway.
1095 mHaveCachedLeftYMost
= mHaveCachedRightYMost
= PR_FALSE
;
1097 // mFrameInfoMap is LIFO so keep removing what it points
1098 // to until we hit mLastFrame.
1099 while (mFrameInfoMap
&& mFrameInfoMap
->mFrame
!= aState
->mLastFrame
) {
1100 RemoveRegion(mFrameInfoMap
->mFrame
);
1103 // If we trip this assertion it means that someone added
1104 // PushState()/PopState() calls around code that actually
1105 // removed mLastFrame from mFrameInfoMap, which means our
1106 // state is now out of sync with what we thought it should be.
1108 NS_ASSERTION(((aState
->mLastFrame
&& mFrameInfoMap
) ||
1109 (!aState
->mLastFrame
&& !mFrameInfoMap
)),
1110 "Unexpected outcome!");
1114 mLowestTop
= aState
->mLowestTop
;
1115 mHaveCachedLeftYMost
= aState
->mHaveCachedLeftYMost
;
1116 mHaveCachedRightYMost
= aState
->mHaveCachedRightYMost
;
1117 mMaximalLeftYMost
= aState
->mMaximalLeftYMost
;
1118 mMaximalRightYMost
= aState
->mMaximalRightYMost
;
1122 nsSpaceManager::GetLowestRegionTop()
1124 if (mLowestTop
== NSCOORD_MIN
)
1126 return mLowestTop
- mY
;
1131 DebugListSpaceManager(nsSpaceManager
*aSpaceManager
)
1133 aSpaceManager
->List(stdout
);
1137 nsSpaceManager::List(FILE* out
)
1141 fprintf(out
, "SpaceManager@%p", this);
1143 nsIFrameDebug
* frameDebug
;
1145 if (NS_SUCCEEDED(mFrame
->QueryInterface(NS_GET_IID(nsIFrameDebug
), (void**)&frameDebug
))) {
1146 frameDebug
->GetFrameName(tmp
);
1147 fprintf(out
, " frame=");
1148 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
1149 fprintf(out
, "@%p", mFrame
);
1152 fprintf(out
, " xy=%d,%d <\n", mX
, mY
);
1153 if (mBandList
.IsEmpty()) {
1154 fprintf(out
, " no bands\n");
1157 BandRect
* band
= mBandList
.Head();
1159 PRInt32
const n
= band
->mFrames
.Count();
1160 fprintf(out
, " left=%d top=%d right=%d bottom=%d count=%d frames=",
1161 band
->mLeft
, band
->mTop
, band
->mRight
, band
->mBottom
, n
);
1163 for (PRInt32 i
= 0; i
< n
; i
++) {
1164 nsIFrame
* frame
= (nsIFrame
*)band
->mFrames
.FastElementAt(i
);
1166 nsIFrameDebug
* frameDebug
;
1168 if (NS_SUCCEEDED(frame
->QueryInterface(NS_GET_IID(nsIFrameDebug
), (void**)&frameDebug
))) {
1169 frameDebug
->GetFrameName(tmp
);
1170 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
1171 fprintf(out
, "@%p ", frame
);
1176 band
= band
->Next();
1177 } while (band
!= mBandList
.Head());
1179 fprintf(out
, ">\n");
1184 nsSpaceManager::FrameInfo
*
1185 nsSpaceManager::GetFrameInfoFor(nsIFrame
* aFrame
)
1187 FrameInfo
* result
= nsnull
;
1189 for (result
= mFrameInfoMap
; result
; result
= result
->mNext
) {
1190 if (result
->mFrame
== aFrame
) {
1198 nsSpaceManager::FrameInfo
*
1199 nsSpaceManager::CreateFrameInfo(nsIFrame
* aFrame
, const nsRect
& aRect
)
1201 FrameInfo
* frameInfo
= new FrameInfo(aFrame
, aRect
);
1204 // Link it into the list
1205 frameInfo
->mNext
= mFrameInfoMap
;
1206 mFrameInfoMap
= frameInfo
;
1208 // Optimize for the common case case when the frame being added is
1209 // likely to be near the bottom.
1210 nscoord ymost
= aRect
.YMost();
1211 PRUint8 floatType
= aFrame
->GetStyleDisplay()->mFloats
;
1212 if (mHaveCachedLeftYMost
&& ymost
> mMaximalLeftYMost
&&
1213 floatType
== NS_STYLE_FLOAT_LEFT
) {
1214 mMaximalLeftYMost
= ymost
;
1216 else if (mHaveCachedRightYMost
&& ymost
> mMaximalRightYMost
&&
1217 floatType
== NS_STYLE_FLOAT_RIGHT
) {
1218 mMaximalRightYMost
= ymost
;
1225 nsSpaceManager::DestroyFrameInfo(FrameInfo
* aFrameInfo
)
1227 // See if it's at the head of the list
1228 if (mFrameInfoMap
== aFrameInfo
) {
1229 mFrameInfoMap
= aFrameInfo
->mNext
;
1234 // Find the previous node in the list
1235 for (prev
= mFrameInfoMap
; prev
&& (prev
->mNext
!= aFrameInfo
); prev
= prev
->mNext
) {
1239 // Disconnect it from the list
1240 NS_ASSERTION(prev
, "element not in list");
1242 prev
->mNext
= aFrameInfo
->mNext
;
1246 // Optimize for the case when the frame being removed is likely to be near
1247 // the bottom, but do nothing if we have neither cached value -- that case is
1248 // likely to be hit from PopState().
1249 if (mHaveCachedLeftYMost
|| mHaveCachedRightYMost
) {
1250 PRUint8 floatType
= aFrameInfo
->mFrame
->GetStyleDisplay()->mFloats
;
1251 if (floatType
== NS_STYLE_FLOAT_LEFT
) {
1252 mHaveCachedLeftYMost
= PR_FALSE
;
1255 NS_ASSERTION(floatType
== NS_STYLE_FLOAT_RIGHT
, "Unexpected float type");
1256 mHaveCachedRightYMost
= PR_FALSE
;
1264 nsSpaceManager::ClearFloats(nscoord aY
, PRUint8 aBreakType
)
1266 nscoord bottom
= aY
+ mY
;
1268 if ((!mHaveCachedLeftYMost
&& aBreakType
!= NS_STYLE_CLEAR_RIGHT
) ||
1269 (!mHaveCachedRightYMost
&& aBreakType
!= NS_STYLE_CLEAR_LEFT
)) {
1270 // Recover our maximal YMost values. Might need both if this is a
1271 // NS_STYLE_CLEAR_LEFT_AND_RIGHT
1272 nscoord maximalLeftYMost
= mHaveCachedLeftYMost
? mMaximalLeftYMost
: nscoord_MIN
;
1273 nscoord maximalRightYMost
= mHaveCachedRightYMost
? mMaximalRightYMost
: nscoord_MIN
;
1275 // Optimize for most floats not being near the bottom
1276 for (FrameInfo
*frame
= mFrameInfoMap
; frame
; frame
= frame
->mNext
) {
1277 nscoord ymost
= frame
->mRect
.YMost();
1278 if (ymost
> maximalLeftYMost
) {
1279 if (frame
->mFrame
->GetStyleDisplay()->mFloats
== NS_STYLE_FLOAT_LEFT
) {
1280 NS_ASSERTION(!mHaveCachedLeftYMost
, "Shouldn't happen");
1281 maximalLeftYMost
= ymost
;
1282 // No need to compare to the right ymost
1287 if (ymost
> maximalRightYMost
) {
1288 if (frame
->mFrame
->GetStyleDisplay()->mFloats
== NS_STYLE_FLOAT_RIGHT
) {
1289 NS_ASSERTION(!mHaveCachedRightYMost
, "Shouldn't happen");
1290 maximalRightYMost
= ymost
;
1295 mMaximalLeftYMost
= maximalLeftYMost
;
1296 mMaximalRightYMost
= maximalRightYMost
;
1297 mHaveCachedRightYMost
= mHaveCachedLeftYMost
= PR_TRUE
;
1300 switch (aBreakType
) {
1301 case NS_STYLE_CLEAR_LEFT_AND_RIGHT
:
1302 NS_ASSERTION(mHaveCachedLeftYMost
&& mHaveCachedRightYMost
,
1303 "Need cached values!");
1304 bottom
= PR_MAX(bottom
, mMaximalLeftYMost
);
1305 bottom
= PR_MAX(bottom
, mMaximalRightYMost
);
1307 case NS_STYLE_CLEAR_LEFT
:
1308 NS_ASSERTION(mHaveCachedLeftYMost
, "Need cached value!");
1309 bottom
= PR_MAX(bottom
, mMaximalLeftYMost
);
1311 case NS_STYLE_CLEAR_RIGHT
:
1312 NS_ASSERTION(mHaveCachedRightYMost
, "Need cached value!");
1313 bottom
= PR_MAX(bottom
, mMaximalRightYMost
);
1325 nsSpaceManager::BandRect
*
1326 nsSpaceManager::GuessBandWithTopAbove(nscoord aYOffset
) const
1328 NS_ASSERTION(!mBandList
.IsEmpty(), "no bands");
1329 BandRect
* band
= nsnull
;
1330 if (mCachedBandPosition
) {
1331 band
= mCachedBandPosition
;
1332 // Now seek backward so that we're guaranteed to be the topmost
1333 // band which might contain the y-offset or be below it.
1334 while (band
&& band
->mTop
> aYOffset
) {
1335 band
= GetPrevBand(band
);
1343 return mBandList
.Head();
1346 /////////////////////////////////////////////////////////////////////////////
1349 nsSpaceManager::FrameInfo::FrameInfo(nsIFrame
* aFrame
, const nsRect
& aRect
)
1350 : mFrame(aFrame
), mRect(aRect
), mNext(0)
1352 MOZ_COUNT_CTOR(nsSpaceManager::FrameInfo
);
1355 #ifdef NS_BUILD_REFCNT_LOGGING
1356 nsSpaceManager::FrameInfo::~FrameInfo()
1358 MOZ_COUNT_DTOR(nsSpaceManager::FrameInfo
);
1362 /////////////////////////////////////////////////////////////////////////////
1365 nsSpaceManager::BandRect::BandRect(nscoord aLeft
,
1371 MOZ_COUNT_CTOR(BandRect
);
1379 nsSpaceManager::BandRect::BandRect(nscoord aLeft
,
1383 nsSmallVoidArray
& aFrames
)
1385 MOZ_COUNT_CTOR(BandRect
);
1393 nsSpaceManager::BandRect::~BandRect()
1395 MOZ_COUNT_DTOR(BandRect
);
1398 nsSpaceManager::BandRect
*
1399 nsSpaceManager::BandRect::SplitVertically(nscoord aBottom
)
1401 NS_PRECONDITION((aBottom
> mTop
) && (aBottom
< mBottom
), "bad argument");
1403 // Create a new band rect for the bottom part
1404 BandRect
* bottomBandRect
= new BandRect(mLeft
, aBottom
, mRight
, mBottom
, mFrames
);
1406 // This band rect becomes the top part, so adjust the bottom edge
1408 return bottomBandRect
;
1411 nsSpaceManager::BandRect
*
1412 nsSpaceManager::BandRect::SplitHorizontally(nscoord aRight
)
1414 NS_PRECONDITION((aRight
> mLeft
) && (aRight
< mRight
), "bad argument");
1416 // Create a new band rect for the right part
1417 BandRect
* rightBandRect
= new BandRect(aRight
, mTop
, mRight
, mBottom
, mFrames
);
1419 // This band rect becomes the left part, so adjust the right edge
1421 return rightBandRect
;
1425 nsSpaceManager::BandRect::HasSameFrameList(const BandRect
* aBandRect
) const
1427 const PRInt32 count
= mFrames
.Count();
1429 // Check whether they're occupied by the same number of frames
1430 if (count
!= aBandRect
->mFrames
.Count()) {
1433 // For each frame occupying this band rect check whether it also occupies
1435 for (PRInt32 i
= 0; i
< count
; i
++) {
1436 if (-1 == aBandRect
->mFrames
.IndexOf(mFrames
.FastElementAt(i
))) {
1445 * Internal helper function that counts the number of rects in this band
1446 * including the current band rect
1449 nsSpaceManager::BandRect::Length() const
1452 BandRect
* bandRect
= Next();
1454 // Because there's a header cell we know we'll either find the next band
1455 // (which has a different y-offset) or the header cell which has an invalid
1457 while (bandRect
->mTop
== mTop
) {
1459 bandRect
= bandRect
->Next();
1466 //----------------------------------------------------------------------
1468 nsAutoSpaceManager::~nsAutoSpaceManager()
1470 // Restore the old space manager in the reflow state if necessary.
1472 #ifdef NOISY_SPACEMANAGER
1473 printf("restoring old space manager %p\n", mOld
);
1476 mReflowState
.mSpaceManager
= mOld
;
1478 #ifdef NOISY_SPACEMANAGER
1480 static_cast<nsFrame
*>(mReflowState
.frame
)->ListTag(stdout
);
1481 printf(": space-manager %p after reflow\n", mOld
);
1494 nsAutoSpaceManager::CreateSpaceManagerFor(nsPresContext
*aPresContext
, nsIFrame
*aFrame
)
1496 // Create a new space manager and install it in the reflow
1497 // state. `Remember' the old space manager so we can restore it
1499 mNew
= new nsSpaceManager(aPresContext
->PresShell(), aFrame
);
1501 return NS_ERROR_OUT_OF_MEMORY
;
1503 #ifdef NOISY_SPACEMANAGER
1504 printf("constructed new space manager %p (replacing %p)\n",
1505 mNew
, mReflowState
.mSpaceManager
);
1508 // Set the space manager in the existing reflow state
1509 mOld
= mReflowState
.mSpaceManager
;
1510 mReflowState
.mSpaceManager
= mNew
;