Bug 508473 part III: Pass destruction root to frame destruction methods r=bz sr=roc
[gecko.git] / layout / generic / nsFrameList.cpp
blob5689d9e8cf8ced1281008f9d8810aa5d7a6be305
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
13 * License.
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.
22 * Contributor(s):
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"
40 #include "nsIFrame.h"
41 #include "nsLayoutUtils.h"
43 #ifdef IBMBIDI
44 #include "nsCOMPtr.h"
45 #include "nsGkAtoms.h"
46 #include "nsILineIterator.h"
47 #include "nsBidiPresUtils.h"
48 #endif // IBMBIDI
50 const nsFrameList* nsFrameList::sEmptyList;
52 /* static */
53 nsresult
54 nsFrameList::Init()
56 NS_PRECONDITION(!sEmptyList, "Shouldn't be allocated");
58 sEmptyList = new nsFrameList();
59 if (!sEmptyList)
60 return NS_ERROR_OUT_OF_MEMORY;
62 return NS_OK;
65 void
66 nsFrameList::Destroy()
68 NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
70 DestroyFrames();
71 delete this;
74 void
75 nsFrameList::DestroyFrom(nsIFrame* aDestructRoot)
77 NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
79 DestroyFramesFrom(aDestructRoot);
80 delete this;
83 void
84 nsFrameList::DestroyFrames()
86 while (nsIFrame* frame = RemoveFirstChild()) {
87 frame->Destroy();
89 mLastChild = nsnull;
92 void
93 nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot)
95 NS_PRECONDITION(aDestructRoot, "Missing destruct root");
97 while (nsIFrame* frame = RemoveFirstChild()) {
98 frame->DestroyFrom(aDestructRoot);
100 mLastChild = nsnull;
103 void
104 nsFrameList::SetFrames(nsIFrame* aFrameList)
106 NS_PRECONDITION(!mFirstChild, "Losing frames");
108 mFirstChild = aFrameList;
109 mLastChild = nsLayoutUtils::GetLastSibling(mFirstChild);
112 void
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");
119 #endif
121 nsIFrame* nextFrame = aFrame->GetNextSibling();
122 if (aFrame == mFirstChild) {
123 mFirstChild = nextFrame;
124 aFrame->SetNextSibling(nsnull);
125 if (!nextFrame) {
126 mLastChild = nsnull;
129 else {
130 nsIFrame* prevSibling = aFrame->GetPrevSibling();
131 NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
132 "Broken frame linkage");
133 prevSibling->SetNextSibling(nextFrame);
134 aFrame->SetNextSibling(nsnull);
135 if (!nextFrame) {
136 mLastChild = prevSibling;
141 PRBool
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) {
148 RemoveFrame(aFrame);
149 return PR_TRUE;
152 return PR_FALSE;
155 nsFrameList
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");
161 #endif
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);
171 nsIFrame*
172 nsFrameList::RemoveFirstChild()
174 if (mFirstChild) {
175 nsIFrame* firstChild = mFirstChild;
176 RemoveFrame(firstChild);
177 return firstChild;
179 return nsnull;
182 void
183 nsFrameList::DestroyFrame(nsIFrame* aFrame)
185 NS_PRECONDITION(aFrame, "null ptr");
186 RemoveFrame(aFrame);
187 aFrame->Destroy();
190 PRBool
191 nsFrameList::DestroyFrameIfPresent(nsIFrame* aFrame)
193 NS_PRECONDITION(aFrame, "null ptr");
195 if (RemoveFrameIfPresent(aFrame)) {
196 aFrame->Destroy();
197 return PR_TRUE;
199 return PR_FALSE;
202 nsFrameList::Slice
203 nsFrameList::InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling,
204 nsFrameList& aFrameList)
206 NS_PRECONDITION(aFrameList.NotEmpty(), "Unexpected empty list");
208 if (aParent) {
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");
222 #endif
224 nsIFrame* firstNewFrame = aFrameList.FirstChild();
225 nsIFrame* nextSibling;
226 if (aPrevSibling) {
227 nextSibling = aPrevSibling->GetNextSibling();
228 aPrevSibling->SetNextSibling(firstNewFrame);
230 else {
231 nextSibling = mFirstChild;
232 mFirstChild = firstNewFrame;
235 nsIFrame* lastNewFrame = aFrameList.LastChild();
236 lastNewFrame->SetNextSibling(nextSibling);
237 if (!nextSibling) {
238 mLastChild = lastNewFrame;
241 VerifyList();
243 aFrameList.Clear();
244 return Slice(*this, firstNewFrame, nextSibling);
247 nsFrameList
248 nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
250 NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
251 NS_PRECONDITION(!aLink.PrevFrame() ||
252 aLink.PrevFrame()->GetNextSibling() ==
253 aLink.NextFrame(),
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;
266 if (prev) {
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
272 mLastChild = nsnull;
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);
283 nsFrameList
284 nsFrameList::ExtractTail(FrameLinkEnumerator& aLink)
286 NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
287 NS_PRECONDITION(!aLink.PrevFrame() ||
288 aLink.PrevFrame()->GetNextSibling() ==
289 aLink.NextFrame(),
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;
303 if (prev) {
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;
308 mLastChild = prev;
309 } else {
310 // Hand the whole list over to our new list
311 newFirstFrame = mFirstChild;
312 newLastFrame = mLastChild;
313 Clear();
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);
324 nsIFrame*
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();
333 return frame;
336 PRInt32
337 nsFrameList::IndexOf(nsIFrame* aFrame) const
339 PRInt32 count = 0;
340 for (nsIFrame* f = mFirstChild; f; f = f->GetNextSibling()) {
341 if (f == aFrame)
342 return count;
343 ++count;
345 return -1;
348 PRBool
349 nsFrameList::ContainsFrame(const nsIFrame* aFrame) const
351 NS_PRECONDITION(aFrame, "null ptr");
353 nsIFrame* frame = mFirstChild;
354 while (frame) {
355 if (frame == aFrame) {
356 return PR_TRUE;
358 frame = frame->GetNextSibling();
360 return PR_FALSE;
363 PRBool
364 nsFrameList::ContainsFrameBefore(const nsIFrame* aFrame, const nsIFrame* aEnd) const
366 NS_PRECONDITION(aFrame, "null ptr");
368 nsIFrame* frame = mFirstChild;
369 while (frame) {
370 if (frame == aEnd) {
371 return PR_FALSE;
373 if (frame == aFrame) {
374 return PR_TRUE;
376 frame = frame->GetNextSibling();
378 return PR_FALSE;
381 PRInt32
382 nsFrameList::GetLength() const
384 PRInt32 count = 0;
385 nsIFrame* frame = mFirstChild;
386 while (frame) {
387 count++;
388 frame = frame->GetNextSibling();
390 return count;
393 static int CompareByContentOrder(const nsIFrame* aF1, const nsIFrame* aF2)
395 if (aF1->GetContent() != aF2->GetContent()) {
396 return nsLayoutUtils::CompareTreePosition(aF1->GetContent(), aF2->GetContent());
399 if (aF1 == aF2) {
400 return 0;
403 const nsIFrame* f;
404 for (f = aF2; f; f = f->GetPrevInFlow()) {
405 if (f == aF1) {
406 // f1 comes before f2 in the flow
407 return -1;
410 for (f = aF1; f; f = f->GetPrevInFlow()) {
411 if (f == aF2) {
412 // f1 comes after f2 in the flow
413 return 1;
417 NS_ASSERTION(PR_FALSE, "Frames for same content but not in relative flow order");
418 return 0;
421 class CompareByContentOrderComparator
423 public:
424 PRBool Equals(const nsIFrame* aA, const nsIFrame* aB) const {
425 return aA == aB;
427 PRBool LessThan(const nsIFrame* aA, const nsIFrame* aB) const {
428 return CompareByContentOrder(aA, aB) < 0;
432 void
433 nsFrameList::SortByContentOrder()
435 if (IsEmpty())
436 return;
438 nsAutoTArray<nsIFrame*, 8> array;
439 nsIFrame* f;
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);
448 f = ff;
450 f->SetNextSibling(nsnull);
451 mLastChild = f;
454 void
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);
464 #ifdef DEBUG
465 void
466 nsFrameList::List(FILE* out) const
468 fputs("<\n", out);
469 for (nsIFrame* frame = mFirstChild; frame;
470 frame = frame->GetNextSibling()) {
471 frame->List(out, 1);
473 fputs(">\n", out);
475 #endif
477 #ifdef IBMBIDI
478 nsIFrame*
479 nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const
481 if (!mFirstChild)
482 return nsnull;
484 nsIFrame* parent = mFirstChild->GetParent();
485 if (!parent)
486 return aFrame ? aFrame->GetPrevSibling() : LastChild();
488 nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(mFirstChild);
489 nsBidiPresUtils* bidiUtils = mFirstChild->PresContext()->GetBidiUtils();
491 nsAutoLineIterator iter = parent->GetLineIterator();
492 if (!iter) {
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);
498 } else { // RTL
499 return bidiUtils->GetFrameToRightOf(aFrame, mFirstChild, -1);
501 } else {
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();
506 } else {
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.
515 PRInt32 thisLine;
516 if (aFrame) {
517 thisLine = iter->FindLineContaining(aFrame);
518 if (thisLine < 0)
519 return nsnull;
520 } else {
521 thisLine = iter->GetNumLines();
524 nsIFrame* frame = nsnull;
525 nsIFrame* firstFrameOnLine;
526 PRInt32 numFramesOnLine;
527 nsRect lineBounds;
528 PRUint32 lineFlags;
530 if (aFrame) {
531 iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
533 if (baseLevel == NSBIDI_LTR) {
534 frame = bidiUtils->GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
535 } else { // RTL
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);
546 } else { // RTL
547 frame = bidiUtils->GetFrameToRightOf(nsnull, firstFrameOnLine, numFramesOnLine);
550 return frame;
553 nsIFrame*
554 nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const
556 if (!mFirstChild)
557 return nsnull;
559 nsIFrame* parent = mFirstChild->GetParent();
560 if (!parent)
561 return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
563 nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(mFirstChild);
564 nsBidiPresUtils* bidiUtils = mFirstChild->PresContext()->GetBidiUtils();
566 nsAutoLineIterator iter = parent->GetLineIterator();
567 if (!iter) {
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);
573 } else { // RTL
574 return bidiUtils->GetFrameToLeftOf(aFrame, mFirstChild, -1);
576 } else {
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;
581 } else {
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.
590 PRInt32 thisLine;
591 if (aFrame) {
592 thisLine = iter->FindLineContaining(aFrame);
593 if (thisLine < 0)
594 return nsnull;
595 } else {
596 thisLine = -1;
599 nsIFrame* frame = nsnull;
600 nsIFrame* firstFrameOnLine;
601 PRInt32 numFramesOnLine;
602 nsRect lineBounds;
603 PRUint32 lineFlags;
605 if (aFrame) {
606 iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
608 if (baseLevel == NSBIDI_LTR) {
609 frame = bidiUtils->GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
610 } else { // RTL
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);
622 } else { // RTL
623 frame = bidiUtils->GetFrameToLeftOf(nsnull, firstFrameOnLine, numFramesOnLine);
626 return frame;
628 #endif
630 #ifdef DEBUG_FRAME_LIST
631 void
632 nsFrameList::VerifyList() const
634 NS_ASSERTION((mFirstChild == nsnull) == (mLastChild == nsnull),
635 "bad list state");
637 if (IsEmpty()) {
638 return;
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;
644 do {
645 first = first->GetNextSibling();
646 second = second->GetNextSibling();
647 if (!second) {
648 break;
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.");
655 return;
657 } while (first && second);
659 NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
660 "bogus mLastChild");
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.
665 #endif