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
)
158 NS_PRECONDITION(NotEmpty(), "illegal operation on empty list");
159 #ifdef DEBUG_FRAME_LIST
160 NS_PRECONDITION(ContainsFrame(aAfterFrame
), "wrong list");
163 nsIFrame
* tail
= aAfterFrame
->GetNextSibling();
164 // if (!tail) return EmptyList(); -- worth optimizing this case?
165 nsIFrame
* oldLastChild
= mLastChild
;
166 mLastChild
= aAfterFrame
;
167 aAfterFrame
->SetNextSibling(nsnull
);
168 return nsFrameList(tail
, tail
? oldLastChild
: nsnull
);
172 nsFrameList::RemoveFirstChild()
175 nsIFrame
* firstChild
= mFirstChild
;
176 RemoveFrame(firstChild
);
183 nsFrameList::DestroyFrame(nsIFrame
* aFrame
)
185 NS_PRECONDITION(aFrame
, "null ptr");
191 nsFrameList::DestroyFrameIfPresent(nsIFrame
* aFrame
)
193 NS_PRECONDITION(aFrame
, "null ptr");
195 if (RemoveFrameIfPresent(aFrame
)) {
203 nsFrameList::InsertFrames(nsIFrame
* aParent
, nsIFrame
* aPrevSibling
,
204 nsFrameList
& aFrameList
)
206 NS_PRECONDITION(aFrameList
.NotEmpty(), "Unexpected empty list");
209 aFrameList
.ApplySetParent(aParent
);
212 NS_ASSERTION(IsEmpty() ||
213 FirstChild()->GetParent() == aFrameList
.FirstChild()->GetParent(),
214 "frame to add has different parent");
215 NS_ASSERTION(!aPrevSibling
||
216 aPrevSibling
->GetParent() == aFrameList
.FirstChild()->GetParent(),
217 "prev sibling has different parent");
218 #ifdef DEBUG_FRAME_LIST
219 // ContainsFrame is O(N)
220 NS_ASSERTION(!aPrevSibling
|| ContainsFrame(aPrevSibling
),
221 "prev sibling is not on this list");
224 nsIFrame
* firstNewFrame
= aFrameList
.FirstChild();
225 nsIFrame
* nextSibling
;
227 nextSibling
= aPrevSibling
->GetNextSibling();
228 aPrevSibling
->SetNextSibling(firstNewFrame
);
231 nextSibling
= mFirstChild
;
232 mFirstChild
= firstNewFrame
;
235 nsIFrame
* lastNewFrame
= aFrameList
.LastChild();
236 lastNewFrame
->SetNextSibling(nextSibling
);
238 mLastChild
= lastNewFrame
;
244 return Slice(*this, firstNewFrame
, nextSibling
);
248 nsFrameList::ExtractHead(FrameLinkEnumerator
& aLink
)
250 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
251 NS_PRECONDITION(!aLink
.PrevFrame() ||
252 aLink
.PrevFrame()->GetNextSibling() ==
254 "Unexpected PrevFrame()");
255 NS_PRECONDITION(aLink
.PrevFrame() ||
256 aLink
.NextFrame() == FirstChild(),
257 "Unexpected NextFrame()");
258 NS_PRECONDITION(!aLink
.PrevFrame() ||
259 aLink
.NextFrame() != FirstChild(),
260 "Unexpected NextFrame()");
261 NS_PRECONDITION(aLink
.mEnd
== nsnull
,
262 "Unexpected mEnd for frame link enumerator");
264 nsIFrame
* prev
= aLink
.PrevFrame();
265 nsIFrame
* newFirstFrame
= nsnull
;
267 // Truncate the list after |prev| and hand the first part to our new list.
268 prev
->SetNextSibling(nsnull
);
269 newFirstFrame
= mFirstChild
;
270 mFirstChild
= aLink
.NextFrame();
271 if (!mFirstChild
) { // we handed over the whole list
275 // Now make sure aLink doesn't point to a frame we no longer have.
276 aLink
.mPrev
= nsnull
;
278 // else aLink is pointing to before our first frame. Nothing to do.
280 return nsFrameList(newFirstFrame
, prev
);
284 nsFrameList::ExtractTail(FrameLinkEnumerator
& aLink
)
286 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
287 NS_PRECONDITION(!aLink
.PrevFrame() ||
288 aLink
.PrevFrame()->GetNextSibling() ==
290 "Unexpected PrevFrame()");
291 NS_PRECONDITION(aLink
.PrevFrame() ||
292 aLink
.NextFrame() == FirstChild(),
293 "Unexpected NextFrame()");
294 NS_PRECONDITION(!aLink
.PrevFrame() ||
295 aLink
.NextFrame() != FirstChild(),
296 "Unexpected NextFrame()");
297 NS_PRECONDITION(aLink
.mEnd
== nsnull
,
298 "Unexpected mEnd for frame link enumerator");
300 nsIFrame
* prev
= aLink
.PrevFrame();
301 nsIFrame
* newFirstFrame
;
302 nsIFrame
* newLastFrame
;
304 // Truncate the list after |prev| and hand the second part to our new list
305 prev
->SetNextSibling(nsnull
);
306 newFirstFrame
= aLink
.NextFrame();
307 newLastFrame
= newFirstFrame
? mLastChild
: nsnull
;
310 // Hand the whole list over to our new list
311 newFirstFrame
= mFirstChild
;
312 newLastFrame
= mLastChild
;
316 // Now make sure aLink doesn't point to a frame we no longer have.
317 aLink
.mFrame
= nsnull
;
319 NS_POSTCONDITION(aLink
.AtEnd(), "What's going on here?");
321 return nsFrameList(newFirstFrame
, newLastFrame
);
325 nsFrameList::FrameAt(PRInt32 aIndex
) const
327 NS_PRECONDITION(aIndex
>= 0, "invalid arg");
328 if (aIndex
< 0) return nsnull
;
329 nsIFrame
* frame
= mFirstChild
;
330 while ((aIndex
-- > 0) && frame
) {
331 frame
= frame
->GetNextSibling();
337 nsFrameList::IndexOf(nsIFrame
* aFrame
) const
340 for (nsIFrame
* f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
349 nsFrameList::ContainsFrame(const nsIFrame
* aFrame
) const
351 NS_PRECONDITION(aFrame
, "null ptr");
353 nsIFrame
* frame
= mFirstChild
;
355 if (frame
== aFrame
) {
358 frame
= frame
->GetNextSibling();
364 nsFrameList::ContainsFrameBefore(const nsIFrame
* aFrame
, const nsIFrame
* aEnd
) const
366 NS_PRECONDITION(aFrame
, "null ptr");
368 nsIFrame
* frame
= mFirstChild
;
373 if (frame
== aFrame
) {
376 frame
= frame
->GetNextSibling();
382 nsFrameList::GetLength() const
385 nsIFrame
* frame
= mFirstChild
;
388 frame
= frame
->GetNextSibling();
393 static int CompareByContentOrder(const nsIFrame
* aF1
, const nsIFrame
* aF2
)
395 if (aF1
->GetContent() != aF2
->GetContent()) {
396 return nsLayoutUtils::CompareTreePosition(aF1
->GetContent(), aF2
->GetContent());
404 for (f
= aF2
; f
; f
= f
->GetPrevInFlow()) {
406 // f1 comes before f2 in the flow
410 for (f
= aF1
; f
; f
= f
->GetPrevInFlow()) {
412 // f1 comes after f2 in the flow
417 NS_ASSERTION(PR_FALSE
, "Frames for same content but not in relative flow order");
421 class CompareByContentOrderComparator
424 PRBool
Equals(const nsIFrame
* aA
, const nsIFrame
* aB
) const {
427 PRBool
LessThan(const nsIFrame
* aA
, const nsIFrame
* aB
) const {
428 return CompareByContentOrder(aA
, aB
) < 0;
433 nsFrameList::SortByContentOrder()
438 nsAutoTArray
<nsIFrame
*, 8> array
;
440 for (f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
441 array
.AppendElement(f
);
443 array
.Sort(CompareByContentOrderComparator());
444 f
= mFirstChild
= array
.ElementAt(0);
445 for (PRUint32 i
= 1; i
< array
.Length(); ++i
) {
446 nsIFrame
* ff
= array
.ElementAt(i
);
447 f
->SetNextSibling(ff
);
450 f
->SetNextSibling(nsnull
);
455 nsFrameList::ApplySetParent(nsIFrame
* aParent
) const
457 NS_ASSERTION(aParent
, "null ptr");
459 for (nsIFrame
* f
= FirstChild(); f
; f
= f
->GetNextSibling()) {
460 f
->SetParent(aParent
);
466 nsFrameList::List(FILE* out
) const
469 for (nsIFrame
* frame
= mFirstChild
; frame
;
470 frame
= frame
->GetNextSibling()) {
479 nsFrameList::GetPrevVisualFor(nsIFrame
* aFrame
) const
484 nsIFrame
* parent
= mFirstChild
->GetParent();
486 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
488 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
489 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
491 nsAutoLineIterator iter
= parent
->GetLineIterator();
493 // Parent is not a block Frame
494 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
495 // Line frames are not bidi-splittable, so need to consider bidi reordering
496 if (baseLevel
== NSBIDI_LTR
) {
497 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
499 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
502 // Just get the next or prev sibling, depending on block and frame direction.
503 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
504 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
505 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
507 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
512 // Parent is a block frame, so use the LineIterator to find the previous visual
513 // sibling on this line, or the last one on the previous line.
517 thisLine
= iter
->FindLineContaining(aFrame
);
521 thisLine
= iter
->GetNumLines();
524 nsIFrame
* frame
= nsnull
;
525 nsIFrame
* firstFrameOnLine
;
526 PRInt32 numFramesOnLine
;
531 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
533 if (baseLevel
== NSBIDI_LTR
) {
534 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
536 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
540 if (!frame
&& thisLine
> 0) {
541 // Get the last frame of the previous line
542 iter
->GetLine(thisLine
- 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
544 if (baseLevel
== NSBIDI_LTR
) {
545 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
547 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
554 nsFrameList::GetNextVisualFor(nsIFrame
* aFrame
) const
559 nsIFrame
* parent
= mFirstChild
->GetParent();
561 return aFrame
? aFrame
->GetPrevSibling() : mFirstChild
;
563 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
564 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
566 nsAutoLineIterator iter
= parent
->GetLineIterator();
568 // Parent is not a block Frame
569 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
570 // Line frames are not bidi-splittable, so need to consider bidi reordering
571 if (baseLevel
== NSBIDI_LTR
) {
572 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
574 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
577 // Just get the next or prev sibling, depending on block and frame direction.
578 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
579 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
580 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
582 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
587 // Parent is a block frame, so use the LineIterator to find the next visual
588 // sibling on this line, or the first one on the next line.
592 thisLine
= iter
->FindLineContaining(aFrame
);
599 nsIFrame
* frame
= nsnull
;
600 nsIFrame
* firstFrameOnLine
;
601 PRInt32 numFramesOnLine
;
606 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
608 if (baseLevel
== NSBIDI_LTR
) {
609 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
611 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
615 PRInt32 numLines
= iter
->GetNumLines();
616 if (!frame
&& thisLine
< numLines
- 1) {
617 // Get the first frame of the next line
618 iter
->GetLine(thisLine
+ 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
620 if (baseLevel
== NSBIDI_LTR
) {
621 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
623 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
630 #ifdef DEBUG_FRAME_LIST
632 nsFrameList::VerifyList() const
634 NS_ASSERTION((mFirstChild
== nsnull
) == (mLastChild
== nsnull
),
641 // Simple algorithm to find a loop in a linked list -- advance pointers
642 // through it at speeds of 1 and 2, and if they ever get to be equal bail
643 nsIFrame
*first
= mFirstChild
, *second
= mFirstChild
;
645 first
= first
->GetNextSibling();
646 second
= second
->GetNextSibling();
650 second
= second
->GetNextSibling();
651 if (first
== second
) {
652 // Loop detected! Since second advances faster, they can't both be null;
653 // we would have broken out of the loop long ago.
654 NS_ERROR("loop in frame list. This will probably hang soon.");
657 } while (first
&& second
);
659 NS_ASSERTION(mLastChild
== nsLayoutUtils::GetLastSibling(mFirstChild
),
661 // XXX we should also assert that all GetParent() are either null or
662 // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
663 // prevents that, e.g. table captions.