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 Communicator client 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 ***** */
39 #include "nsFrameList.h"
41 #include "nsLayoutUtils.h"
45 #include "nsGkAtoms.h"
46 #include "nsILineIterator.h"
47 #include "nsBidiPresUtils.h"
50 const nsFrameList
* nsFrameList::sEmptyList
;
56 NS_PRECONDITION(!sEmptyList
, "Shouldn't be allocated");
58 sEmptyList
= new nsFrameList();
60 return NS_ERROR_OUT_OF_MEMORY
;
66 nsFrameList::Destroy()
68 NS_PRECONDITION(this != sEmptyList
, "Shouldn't Destroy() sEmptyList");
75 nsFrameList::DestroyFrom(nsIFrame
* aDestructRoot
)
77 NS_PRECONDITION(this != sEmptyList
, "Shouldn't Destroy() sEmptyList");
79 DestroyFramesFrom(aDestructRoot
);
84 nsFrameList::DestroyFrames()
86 while (nsIFrame
* frame
= RemoveFirstChild()) {
93 nsFrameList::DestroyFramesFrom(nsIFrame
* aDestructRoot
)
95 NS_PRECONDITION(aDestructRoot
, "Missing destruct root");
97 while (nsIFrame
* frame
= RemoveFirstChild()) {
98 frame
->DestroyFrom(aDestructRoot
);
104 nsFrameList::SetFrames(nsIFrame
* aFrameList
)
106 NS_PRECONDITION(!mFirstChild
, "Losing frames");
108 mFirstChild
= aFrameList
;
109 mLastChild
= nsLayoutUtils::GetLastSibling(mFirstChild
);
113 nsFrameList::RemoveFrame(nsIFrame
* aFrame
)
115 NS_PRECONDITION(aFrame
, "null ptr");
116 #ifdef DEBUG_FRAME_LIST
117 // ContainsFrame is O(N)
118 NS_PRECONDITION(ContainsFrame(aFrame
), "wrong list");
121 nsIFrame
* nextFrame
= aFrame
->GetNextSibling();
122 if (aFrame
== mFirstChild
) {
123 mFirstChild
= nextFrame
;
124 aFrame
->SetNextSibling(nsnull
);
130 nsIFrame
* prevSibling
= aFrame
->GetPrevSibling();
131 NS_ASSERTION(prevSibling
&& prevSibling
->GetNextSibling() == aFrame
,
132 "Broken frame linkage");
133 prevSibling
->SetNextSibling(nextFrame
);
134 aFrame
->SetNextSibling(nsnull
);
136 mLastChild
= prevSibling
;
142 nsFrameList::RemoveFrameIfPresent(nsIFrame
* aFrame
)
144 NS_PRECONDITION(aFrame
, "null ptr");
146 for (Enumerator
e(*this); !e
.AtEnd(); e
.Next()) {
147 if (e
.get() == aFrame
) {
156 nsFrameList::RemoveFramesAfter(nsIFrame
* aAfterFrame
)
160 result
.InsertFrames(nsnull
, nsnull
, *this);
164 NS_PRECONDITION(NotEmpty(), "illegal operation on empty list");
165 #ifdef DEBUG_FRAME_LIST
166 NS_PRECONDITION(ContainsFrame(aAfterFrame
), "wrong list");
169 nsIFrame
* tail
= aAfterFrame
->GetNextSibling();
170 // if (!tail) return EmptyList(); -- worth optimizing this case?
171 nsIFrame
* oldLastChild
= mLastChild
;
172 mLastChild
= aAfterFrame
;
173 aAfterFrame
->SetNextSibling(nsnull
);
174 return nsFrameList(tail
, tail
? oldLastChild
: nsnull
);
178 nsFrameList::RemoveFirstChild()
181 nsIFrame
* firstChild
= mFirstChild
;
182 RemoveFrame(firstChild
);
189 nsFrameList::DestroyFrame(nsIFrame
* aFrame
)
191 NS_PRECONDITION(aFrame
, "null ptr");
197 nsFrameList::DestroyFrameIfPresent(nsIFrame
* aFrame
)
199 NS_PRECONDITION(aFrame
, "null ptr");
201 if (RemoveFrameIfPresent(aFrame
)) {
209 nsFrameList::InsertFrames(nsIFrame
* aParent
, nsIFrame
* aPrevSibling
,
210 nsFrameList
& aFrameList
)
212 NS_PRECONDITION(aFrameList
.NotEmpty(), "Unexpected empty list");
215 aFrameList
.ApplySetParent(aParent
);
218 NS_ASSERTION(IsEmpty() ||
219 FirstChild()->GetParent() == aFrameList
.FirstChild()->GetParent(),
220 "frame to add has different parent");
221 NS_ASSERTION(!aPrevSibling
||
222 aPrevSibling
->GetParent() == aFrameList
.FirstChild()->GetParent(),
223 "prev sibling has different parent");
224 #ifdef DEBUG_FRAME_LIST
225 // ContainsFrame is O(N)
226 NS_ASSERTION(!aPrevSibling
|| ContainsFrame(aPrevSibling
),
227 "prev sibling is not on this list");
230 nsIFrame
* firstNewFrame
= aFrameList
.FirstChild();
231 nsIFrame
* nextSibling
;
233 nextSibling
= aPrevSibling
->GetNextSibling();
234 aPrevSibling
->SetNextSibling(firstNewFrame
);
237 nextSibling
= mFirstChild
;
238 mFirstChild
= firstNewFrame
;
241 nsIFrame
* lastNewFrame
= aFrameList
.LastChild();
242 lastNewFrame
->SetNextSibling(nextSibling
);
244 mLastChild
= lastNewFrame
;
250 return Slice(*this, firstNewFrame
, nextSibling
);
254 nsFrameList::ExtractHead(FrameLinkEnumerator
& aLink
)
256 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
257 NS_PRECONDITION(!aLink
.PrevFrame() ||
258 aLink
.PrevFrame()->GetNextSibling() ==
260 "Unexpected PrevFrame()");
261 NS_PRECONDITION(aLink
.PrevFrame() ||
262 aLink
.NextFrame() == FirstChild(),
263 "Unexpected NextFrame()");
264 NS_PRECONDITION(!aLink
.PrevFrame() ||
265 aLink
.NextFrame() != FirstChild(),
266 "Unexpected NextFrame()");
267 NS_PRECONDITION(aLink
.mEnd
== nsnull
,
268 "Unexpected mEnd for frame link enumerator");
270 nsIFrame
* prev
= aLink
.PrevFrame();
271 nsIFrame
* newFirstFrame
= nsnull
;
273 // Truncate the list after |prev| and hand the first part to our new list.
274 prev
->SetNextSibling(nsnull
);
275 newFirstFrame
= mFirstChild
;
276 mFirstChild
= aLink
.NextFrame();
277 if (!mFirstChild
) { // we handed over the whole list
281 // Now make sure aLink doesn't point to a frame we no longer have.
282 aLink
.mPrev
= nsnull
;
284 // else aLink is pointing to before our first frame. Nothing to do.
286 return nsFrameList(newFirstFrame
, prev
);
290 nsFrameList::ExtractTail(FrameLinkEnumerator
& aLink
)
292 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
293 NS_PRECONDITION(!aLink
.PrevFrame() ||
294 aLink
.PrevFrame()->GetNextSibling() ==
296 "Unexpected PrevFrame()");
297 NS_PRECONDITION(aLink
.PrevFrame() ||
298 aLink
.NextFrame() == FirstChild(),
299 "Unexpected NextFrame()");
300 NS_PRECONDITION(!aLink
.PrevFrame() ||
301 aLink
.NextFrame() != FirstChild(),
302 "Unexpected NextFrame()");
303 NS_PRECONDITION(aLink
.mEnd
== nsnull
,
304 "Unexpected mEnd for frame link enumerator");
306 nsIFrame
* prev
= aLink
.PrevFrame();
307 nsIFrame
* newFirstFrame
;
308 nsIFrame
* newLastFrame
;
310 // Truncate the list after |prev| and hand the second part to our new list
311 prev
->SetNextSibling(nsnull
);
312 newFirstFrame
= aLink
.NextFrame();
313 newLastFrame
= newFirstFrame
? mLastChild
: nsnull
;
316 // Hand the whole list over to our new list
317 newFirstFrame
= mFirstChild
;
318 newLastFrame
= mLastChild
;
322 // Now make sure aLink doesn't point to a frame we no longer have.
323 aLink
.mFrame
= nsnull
;
325 NS_POSTCONDITION(aLink
.AtEnd(), "What's going on here?");
327 return nsFrameList(newFirstFrame
, newLastFrame
);
331 nsFrameList::FrameAt(PRInt32 aIndex
) const
333 NS_PRECONDITION(aIndex
>= 0, "invalid arg");
334 if (aIndex
< 0) return nsnull
;
335 nsIFrame
* frame
= mFirstChild
;
336 while ((aIndex
-- > 0) && frame
) {
337 frame
= frame
->GetNextSibling();
343 nsFrameList::IndexOf(nsIFrame
* aFrame
) const
346 for (nsIFrame
* f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
355 nsFrameList::ContainsFrame(const nsIFrame
* aFrame
) const
357 NS_PRECONDITION(aFrame
, "null ptr");
359 nsIFrame
* frame
= mFirstChild
;
361 if (frame
== aFrame
) {
364 frame
= frame
->GetNextSibling();
370 nsFrameList::GetLength() const
373 nsIFrame
* frame
= mFirstChild
;
376 frame
= frame
->GetNextSibling();
381 static int CompareByContentOrder(const nsIFrame
* aF1
, const nsIFrame
* aF2
)
383 if (aF1
->GetContent() != aF2
->GetContent()) {
384 return nsLayoutUtils::CompareTreePosition(aF1
->GetContent(), aF2
->GetContent());
392 for (f
= aF2
; f
; f
= f
->GetPrevInFlow()) {
394 // f1 comes before f2 in the flow
398 for (f
= aF1
; f
; f
= f
->GetPrevInFlow()) {
400 // f1 comes after f2 in the flow
405 NS_ASSERTION(PR_FALSE
, "Frames for same content but not in relative flow order");
409 class CompareByContentOrderComparator
412 PRBool
Equals(const nsIFrame
* aA
, const nsIFrame
* aB
) const {
415 PRBool
LessThan(const nsIFrame
* aA
, const nsIFrame
* aB
) const {
416 return CompareByContentOrder(aA
, aB
) < 0;
421 nsFrameList::ApplySetParent(nsIFrame
* aParent
) const
423 NS_ASSERTION(aParent
, "null ptr");
425 for (nsIFrame
* f
= FirstChild(); f
; f
= f
->GetNextSibling()) {
426 f
->SetParent(aParent
);
432 nsFrameList::List(FILE* out
) const
435 for (nsIFrame
* frame
= mFirstChild
; frame
;
436 frame
= frame
->GetNextSibling()) {
445 nsFrameList::GetPrevVisualFor(nsIFrame
* aFrame
) const
450 nsIFrame
* parent
= mFirstChild
->GetParent();
452 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
454 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
455 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
457 nsAutoLineIterator iter
= parent
->GetLineIterator();
459 // Parent is not a block Frame
460 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
461 // Line frames are not bidi-splittable, so need to consider bidi reordering
462 if (baseLevel
== NSBIDI_LTR
) {
463 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
465 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
468 // Just get the next or prev sibling, depending on block and frame direction.
469 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
470 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
471 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
473 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
478 // Parent is a block frame, so use the LineIterator to find the previous visual
479 // sibling on this line, or the last one on the previous line.
483 thisLine
= iter
->FindLineContaining(aFrame
);
487 thisLine
= iter
->GetNumLines();
490 nsIFrame
* frame
= nsnull
;
491 nsIFrame
* firstFrameOnLine
;
492 PRInt32 numFramesOnLine
;
497 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
499 if (baseLevel
== NSBIDI_LTR
) {
500 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
502 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
506 if (!frame
&& thisLine
> 0) {
507 // Get the last frame of the previous line
508 iter
->GetLine(thisLine
- 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
510 if (baseLevel
== NSBIDI_LTR
) {
511 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
513 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
520 nsFrameList::GetNextVisualFor(nsIFrame
* aFrame
) const
525 nsIFrame
* parent
= mFirstChild
->GetParent();
527 return aFrame
? aFrame
->GetPrevSibling() : mFirstChild
;
529 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
530 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
532 nsAutoLineIterator iter
= parent
->GetLineIterator();
534 // Parent is not a block Frame
535 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
536 // Line frames are not bidi-splittable, so need to consider bidi reordering
537 if (baseLevel
== NSBIDI_LTR
) {
538 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
540 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
543 // Just get the next or prev sibling, depending on block and frame direction.
544 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
545 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
546 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
548 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
553 // Parent is a block frame, so use the LineIterator to find the next visual
554 // sibling on this line, or the first one on the next line.
558 thisLine
= iter
->FindLineContaining(aFrame
);
565 nsIFrame
* frame
= nsnull
;
566 nsIFrame
* firstFrameOnLine
;
567 PRInt32 numFramesOnLine
;
572 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
574 if (baseLevel
== NSBIDI_LTR
) {
575 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
577 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
581 PRInt32 numLines
= iter
->GetNumLines();
582 if (!frame
&& thisLine
< numLines
- 1) {
583 // Get the first frame of the next line
584 iter
->GetLine(thisLine
+ 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
586 if (baseLevel
== NSBIDI_LTR
) {
587 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
589 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
596 #ifdef DEBUG_FRAME_LIST
598 nsFrameList::VerifyList() const
600 NS_ASSERTION((mFirstChild
== nsnull
) == (mLastChild
== nsnull
),
607 // Simple algorithm to find a loop in a linked list -- advance pointers
608 // through it at speeds of 1 and 2, and if they ever get to be equal bail
609 NS_ASSERTION(!mFirstChild
->GetPrevSibling(), "bad prev sibling pointer");
610 nsIFrame
*first
= mFirstChild
, *second
= mFirstChild
;
612 first
= first
->GetNextSibling();
613 second
= second
->GetNextSibling();
617 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
618 "bad prev sibling pointer");
619 second
= second
->GetNextSibling();
620 if (first
== second
) {
621 // Loop detected! Since second advances faster, they can't both be null;
622 // we would have broken out of the loop long ago.
623 NS_ERROR("loop in frame list. This will probably hang soon.");
629 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
630 "bad prev sibling pointer");
633 NS_ASSERTION(mLastChild
== nsLayoutUtils::GetLastSibling(mFirstChild
),
635 // XXX we should also assert that all GetParent() are either null or
636 // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
637 // prevents that, e.g. table captions.