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 /* class for maintaining a linked list of child frames */
41 #include "nsFrameList.h"
43 #include "nsIFrameDebug.h"
45 #include "nsLayoutUtils.h"
49 #include "nsGkAtoms.h"
50 #include "nsILineIterator.h"
51 #include "nsBidiPresUtils.h"
55 nsFrameList::Destroy()
62 nsFrameList::DestroyFrames()
65 for (nsIFrame
* frame
= mFirstChild
; frame
; frame
= next
) {
66 next
= frame
->GetNextSibling();
73 nsFrameList::AppendFrames(nsIFrame
* aParent
, nsIFrame
* aFrameList
)
75 NS_PRECONDITION(nsnull
!= aFrameList
, "null ptr");
76 if (nsnull
!= aFrameList
) {
77 nsIFrame
* lastChild
= LastChild();
78 if (nsnull
== lastChild
) {
79 mFirstChild
= aFrameList
;
82 lastChild
->SetNextSibling(aFrameList
);
85 for (nsIFrame
* frame
= aFrameList
; frame
;
86 frame
= frame
->GetNextSibling()) {
87 frame
->SetParent(aParent
);
97 nsFrameList::AppendFrame(nsIFrame
* aParent
, nsIFrame
* aFrame
)
99 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
100 if (nsnull
!= aFrame
) {
101 NS_PRECONDITION(!aFrame
->GetNextSibling(), "Can only append one frame here");
102 nsIFrame
* lastChild
= LastChild();
103 if (nsnull
== lastChild
) {
104 mFirstChild
= aFrame
;
107 lastChild
->SetNextSibling(aFrame
);
109 if (nsnull
!= aParent
) {
110 aFrame
->SetParent(aParent
);
119 nsFrameList::RemoveFrame(nsIFrame
* aFrame
, nsIFrame
* aPrevSiblingHint
)
121 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
123 nsIFrame
* nextFrame
= aFrame
->GetNextSibling();
124 if (aFrame
== mFirstChild
) {
125 mFirstChild
= nextFrame
;
126 aFrame
->SetNextSibling(nsnull
);
130 nsIFrame
* prevSibling
= aPrevSiblingHint
;
131 if (!prevSibling
|| prevSibling
->GetNextSibling() != aFrame
) {
132 prevSibling
= GetPrevSiblingFor(aFrame
);
135 prevSibling
->SetNextSibling(nextFrame
);
136 aFrame
->SetNextSibling(nsnull
);
141 // aFrame was not in the list.
146 nsFrameList::RemoveFirstChild()
149 nsIFrame
* nextFrame
= mFirstChild
->GetNextSibling();
150 mFirstChild
->SetNextSibling(nsnull
);
151 mFirstChild
= nextFrame
;
158 nsFrameList::DestroyFrame(nsIFrame
* aFrame
)
160 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
161 if (RemoveFrame(aFrame
)) {
169 nsFrameList::InsertFrame(nsIFrame
* aParent
,
170 nsIFrame
* aPrevSibling
,
173 NS_PRECONDITION(nsnull
!= aNewFrame
, "null ptr");
174 if (nsnull
!= aNewFrame
) {
176 aNewFrame
->SetParent(aParent
);
178 if (nsnull
== aPrevSibling
) {
179 aNewFrame
->SetNextSibling(mFirstChild
);
180 mFirstChild
= aNewFrame
;
183 NS_ASSERTION(aNewFrame
->GetParent() == aPrevSibling
->GetParent(),
184 "prev sibling has different parent");
185 nsIFrame
* nextFrame
= aPrevSibling
->GetNextSibling();
186 NS_ASSERTION(!nextFrame
||
187 aNewFrame
->GetParent() == nextFrame
->GetParent(),
188 "next sibling has different parent");
189 aPrevSibling
->SetNextSibling(aNewFrame
);
190 aNewFrame
->SetNextSibling(nextFrame
);
199 nsFrameList::InsertFrames(nsIFrame
* aParent
,
200 nsIFrame
* aPrevSibling
,
201 nsIFrame
* aFrameList
)
203 NS_PRECONDITION(nsnull
!= aFrameList
, "null ptr");
204 if (nsnull
!= aFrameList
) {
205 nsIFrame
* lastNewFrame
= nsnull
;
207 for (nsIFrame
* frame
= aFrameList
; frame
;
208 frame
= frame
->GetNextSibling()) {
209 frame
->SetParent(aParent
);
210 lastNewFrame
= frame
;
214 // Get the last new frame if necessary
216 nsFrameList
tmp(aFrameList
);
217 lastNewFrame
= tmp
.LastChild();
220 // Link the new frames into the child list
221 if (nsnull
== aPrevSibling
) {
222 lastNewFrame
->SetNextSibling(mFirstChild
);
223 mFirstChild
= aFrameList
;
226 NS_ASSERTION(aFrameList
->GetParent() == aPrevSibling
->GetParent(),
227 "prev sibling has different parent");
228 nsIFrame
* nextFrame
= aPrevSibling
->GetNextSibling();
229 NS_ASSERTION(!nextFrame
||
230 aFrameList
->GetParent() == nextFrame
->GetParent(),
231 "next sibling has different parent");
232 aPrevSibling
->SetNextSibling(aFrameList
);
233 lastNewFrame
->SetNextSibling(nextFrame
);
242 nsFrameList::Split(nsIFrame
* aAfterFrame
, nsIFrame
** aNextFrameResult
)
244 NS_PRECONDITION(nsnull
!= aAfterFrame
, "null ptr");
245 NS_PRECONDITION(nsnull
!= aNextFrameResult
, "null ptr");
246 NS_ASSERTION(ContainsFrame(aAfterFrame
), "split after unknown frame");
248 if (aNextFrameResult
&& aAfterFrame
) {
249 nsIFrame
* nextFrame
= aAfterFrame
->GetNextSibling();
250 aAfterFrame
->SetNextSibling(nsnull
);
251 *aNextFrameResult
= nextFrame
;
258 nsFrameList::LastChild() const
260 nsIFrame
* frame
= mFirstChild
;
265 nsIFrame
* next
= frame
->GetNextSibling();
268 next
= frame
->GetNextSibling();
274 nsFrameList::FrameAt(PRInt32 aIndex
) const
276 NS_PRECONDITION(aIndex
>= 0, "invalid arg");
277 if (aIndex
< 0) return nsnull
;
278 nsIFrame
* frame
= mFirstChild
;
279 while ((aIndex
-- > 0) && frame
) {
280 frame
= frame
->GetNextSibling();
286 nsFrameList::ContainsFrame(const nsIFrame
* aFrame
) const
288 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
289 nsIFrame
* frame
= mFirstChild
;
291 if (frame
== aFrame
) {
294 frame
= frame
->GetNextSibling();
300 nsFrameList::ContainsFrameBefore(const nsIFrame
* aFrame
, const nsIFrame
* aEnd
) const
302 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
303 nsIFrame
* frame
= mFirstChild
;
308 if (frame
== aFrame
) {
311 frame
= frame
->GetNextSibling();
317 nsFrameList::GetLength() const
320 nsIFrame
* frame
= mFirstChild
;
323 frame
= frame
->GetNextSibling();
328 static int PR_CALLBACK
CompareByContentOrder(const void* aF1
, const void* aF2
,
331 const nsIFrame
* f1
= static_cast<const nsIFrame
*>(aF1
);
332 const nsIFrame
* f2
= static_cast<const nsIFrame
*>(aF2
);
333 if (f1
->GetContent() != f2
->GetContent()) {
334 return nsLayoutUtils::CompareTreePosition(f1
->GetContent(), f2
->GetContent());
342 for (f
= f2
; f
; f
= f
->GetPrevInFlow()) {
344 // f1 comes before f2 in the flow
348 for (f
= f1
; f
; f
= f
->GetPrevInFlow()) {
350 // f1 comes after f2 in the flow
355 NS_ASSERTION(PR_FALSE
, "Frames for same content but not in relative flow order");
360 nsFrameList::SortByContentOrder()
365 nsAutoVoidArray array
;
367 for (f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
368 array
.AppendElement(f
);
370 array
.Sort(CompareByContentOrder
, nsnull
);
371 f
= mFirstChild
= static_cast<nsIFrame
*>(array
.FastElementAt(0));
372 for (PRInt32 i
= 1; i
< array
.Count(); ++i
) {
373 nsIFrame
* ff
= static_cast<nsIFrame
*>(array
.FastElementAt(i
));
374 f
->SetNextSibling(ff
);
377 f
->SetNextSibling(nsnull
);
381 nsFrameList::GetPrevSiblingFor(nsIFrame
* aFrame
) const
383 NS_PRECONDITION(nsnull
!= aFrame
, "null ptr");
384 if (aFrame
== mFirstChild
) {
387 nsIFrame
* frame
= mFirstChild
;
389 nsIFrame
* next
= frame
->GetNextSibling();
390 if (next
== aFrame
) {
399 nsFrameList::VerifyParent(nsIFrame
* aParent
) const
402 for (nsIFrame
* frame
= mFirstChild
; frame
;
403 frame
= frame
->GetNextSibling()) {
404 NS_ASSERTION(frame
->GetParent() == aParent
, "bad parent");
411 nsFrameList::List(FILE* out
) const
414 for (nsIFrame
* frame
= mFirstChild
; frame
;
415 frame
= frame
->GetNextSibling()) {
416 nsIFrameDebug
* frameDebug
;
417 if (NS_SUCCEEDED(frame
->QueryInterface(NS_GET_IID(nsIFrameDebug
), (void**)&frameDebug
))) {
418 frameDebug
->List(out
, 1);
427 nsFrameList::GetPrevVisualFor(nsIFrame
* aFrame
) const
429 nsCOMPtr
<nsILineIterator
> iter
;
434 nsIFrame
* parent
= mFirstChild
->GetParent();
436 return aFrame
? GetPrevSiblingFor(aFrame
) : LastChild();
438 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
439 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
441 nsresult result
= parent
->QueryInterface(NS_GET_IID(nsILineIterator
), getter_AddRefs(iter
));
442 if (NS_FAILED(result
) || !iter
) {
443 // Parent is not a block Frame
444 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
445 // Line frames are not bidi-splittable, so need to consider bidi reordering
446 if (baseLevel
== NSBIDI_LTR
) {
447 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
449 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
452 // Just get the next or prev sibling, depending on block and frame direction.
453 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
454 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
455 return aFrame
? GetPrevSiblingFor(aFrame
) : LastChild();
457 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
462 // Parent is a block frame, so use the LineIterator to find the previous visual
463 // sibling on this line, or the last one on the previous line.
467 result
= iter
->FindLineContaining(aFrame
, &thisLine
);
468 if (NS_FAILED(result
) || thisLine
< 0)
471 iter
->GetNumLines(&thisLine
);
474 nsIFrame
* frame
= nsnull
;
475 nsIFrame
* firstFrameOnLine
;
476 PRInt32 numFramesOnLine
;
481 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
483 if (baseLevel
== NSBIDI_LTR
) {
484 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
486 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
490 if (!frame
&& thisLine
> 0) {
491 // Get the last frame of the previous line
492 iter
->GetLine(thisLine
- 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
494 if (baseLevel
== NSBIDI_LTR
) {
495 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
497 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
504 nsFrameList::GetNextVisualFor(nsIFrame
* aFrame
) const
506 nsCOMPtr
<nsILineIterator
> iter
;
511 nsIFrame
* parent
= mFirstChild
->GetParent();
513 return aFrame
? GetPrevSiblingFor(aFrame
) : mFirstChild
;
515 nsBidiLevel baseLevel
= nsBidiPresUtils::GetFrameBaseLevel(mFirstChild
);
516 nsBidiPresUtils
* bidiUtils
= mFirstChild
->PresContext()->GetBidiUtils();
518 nsresult result
= parent
->QueryInterface(NS_GET_IID(nsILineIterator
), getter_AddRefs(iter
));
519 if (NS_FAILED(result
) || !iter
) {
520 // Parent is not a block Frame
521 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
522 // Line frames are not bidi-splittable, so need to consider bidi reordering
523 if (baseLevel
== NSBIDI_LTR
) {
524 return bidiUtils
->GetFrameToRightOf(aFrame
, mFirstChild
, -1);
526 return bidiUtils
->GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
529 // Just get the next or prev sibling, depending on block and frame direction.
530 nsBidiLevel frameEmbeddingLevel
= nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild
);
531 if ((frameEmbeddingLevel
& 1) == (baseLevel
& 1)) {
532 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
534 return aFrame
? GetPrevSiblingFor(aFrame
) : LastChild();
539 // Parent is a block frame, so use the LineIterator to find the next visual
540 // sibling on this line, or the first one on the next line.
544 result
= iter
->FindLineContaining(aFrame
, &thisLine
);
545 if (NS_FAILED(result
) || thisLine
< 0)
551 nsIFrame
* frame
= nsnull
;
552 nsIFrame
* firstFrameOnLine
;
553 PRInt32 numFramesOnLine
;
558 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
560 if (baseLevel
== NSBIDI_LTR
) {
561 frame
= bidiUtils
->GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
563 frame
= bidiUtils
->GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
568 iter
->GetNumLines(&numLines
);
569 if (!frame
&& thisLine
< numLines
- 1) {
570 // Get the first frame of the next line
571 iter
->GetLine(thisLine
+ 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
, &lineFlags
);
573 if (baseLevel
== NSBIDI_LTR
) {
574 frame
= bidiUtils
->GetFrameToRightOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
576 frame
= bidiUtils
->GetFrameToLeftOf(nsnull
, firstFrameOnLine
, numFramesOnLine
);
585 nsFrameList::CheckForLoops()
591 // Simple algorithm to find a loop in a linked list -- advance pointers
592 // through it at speeds of 1 and 2, and if they ever get to be equal bail
593 nsIFrame
*first
= mFirstChild
, *second
= mFirstChild
;
595 first
= first
->GetNextSibling();
596 second
= second
->GetNextSibling();
600 second
= second
->GetNextSibling();
601 if (first
== second
) {
602 // Loop detected! Since second advances faster, they can't both be null;
603 // we would have broken out of the loop long ago.
604 NS_ERROR("loop in frame list. This will probably hang soon.");
607 } while (first
&& second
);