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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsFrameList.h"
40 : #include "nsIFrame.h"
41 : #include "nsLayoutUtils.h"
42 :
43 : #ifdef IBMBIDI
44 : #include "nsCOMPtr.h"
45 : #include "nsGkAtoms.h"
46 : #include "nsILineIterator.h"
47 : #include "nsBidiPresUtils.h"
48 : #endif // IBMBIDI
49 :
50 : const nsFrameList* nsFrameList::sEmptyList;
51 :
52 : /* static */
53 : void
54 1404 : nsFrameList::Init()
55 : {
56 1404 : NS_PRECONDITION(!sEmptyList, "Shouldn't be allocated");
57 :
58 1404 : sEmptyList = new nsFrameList();
59 1404 : }
60 :
61 : void
62 0 : nsFrameList::Destroy()
63 : {
64 0 : NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
65 :
66 0 : DestroyFrames();
67 0 : delete this;
68 0 : }
69 :
70 : void
71 0 : nsFrameList::DestroyFrom(nsIFrame* aDestructRoot)
72 : {
73 0 : NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
74 :
75 0 : DestroyFramesFrom(aDestructRoot);
76 0 : delete this;
77 0 : }
78 :
79 : void
80 0 : nsFrameList::DestroyFrames()
81 : {
82 0 : while (nsIFrame* frame = RemoveFirstChild()) {
83 0 : frame->Destroy();
84 : }
85 0 : mLastChild = nsnull;
86 0 : }
87 :
88 : void
89 0 : nsFrameList::DestroyFramesFrom(nsIFrame* aDestructRoot)
90 : {
91 0 : NS_PRECONDITION(aDestructRoot, "Missing destruct root");
92 :
93 0 : while (nsIFrame* frame = RemoveFirstChild()) {
94 0 : frame->DestroyFrom(aDestructRoot);
95 : }
96 0 : mLastChild = nsnull;
97 0 : }
98 :
99 : void
100 0 : nsFrameList::SetFrames(nsIFrame* aFrameList)
101 : {
102 0 : NS_PRECONDITION(!mFirstChild, "Losing frames");
103 :
104 0 : mFirstChild = aFrameList;
105 0 : mLastChild = nsLayoutUtils::GetLastSibling(mFirstChild);
106 0 : }
107 :
108 : void
109 0 : nsFrameList::RemoveFrame(nsIFrame* aFrame)
110 : {
111 0 : NS_PRECONDITION(aFrame, "null ptr");
112 : #ifdef DEBUG_FRAME_LIST
113 : // ContainsFrame is O(N)
114 : NS_PRECONDITION(ContainsFrame(aFrame), "wrong list");
115 : #endif
116 :
117 0 : nsIFrame* nextFrame = aFrame->GetNextSibling();
118 0 : if (aFrame == mFirstChild) {
119 0 : mFirstChild = nextFrame;
120 0 : aFrame->SetNextSibling(nsnull);
121 0 : if (!nextFrame) {
122 0 : mLastChild = nsnull;
123 : }
124 : }
125 : else {
126 0 : nsIFrame* prevSibling = aFrame->GetPrevSibling();
127 0 : NS_ASSERTION(prevSibling && prevSibling->GetNextSibling() == aFrame,
128 : "Broken frame linkage");
129 0 : prevSibling->SetNextSibling(nextFrame);
130 0 : aFrame->SetNextSibling(nsnull);
131 0 : if (!nextFrame) {
132 0 : mLastChild = prevSibling;
133 : }
134 : }
135 0 : }
136 :
137 : bool
138 0 : nsFrameList::RemoveFrameIfPresent(nsIFrame* aFrame)
139 : {
140 0 : NS_PRECONDITION(aFrame, "null ptr");
141 :
142 0 : for (Enumerator e(*this); !e.AtEnd(); e.Next()) {
143 0 : if (e.get() == aFrame) {
144 0 : RemoveFrame(aFrame);
145 0 : return true;
146 : }
147 : }
148 0 : return false;
149 : }
150 :
151 : nsFrameList
152 0 : nsFrameList::RemoveFramesAfter(nsIFrame* aAfterFrame)
153 : {
154 0 : if (!aAfterFrame) {
155 0 : nsFrameList result;
156 0 : result.InsertFrames(nsnull, nsnull, *this);
157 0 : return result;
158 : }
159 :
160 0 : NS_PRECONDITION(NotEmpty(), "illegal operation on empty list");
161 : #ifdef DEBUG_FRAME_LIST
162 : NS_PRECONDITION(ContainsFrame(aAfterFrame), "wrong list");
163 : #endif
164 :
165 0 : nsIFrame* tail = aAfterFrame->GetNextSibling();
166 : // if (!tail) return EmptyList(); -- worth optimizing this case?
167 0 : nsIFrame* oldLastChild = mLastChild;
168 0 : mLastChild = aAfterFrame;
169 0 : aAfterFrame->SetNextSibling(nsnull);
170 0 : return nsFrameList(tail, tail ? oldLastChild : nsnull);
171 : }
172 :
173 : nsIFrame*
174 0 : nsFrameList::RemoveFirstChild()
175 : {
176 0 : if (mFirstChild) {
177 0 : nsIFrame* firstChild = mFirstChild;
178 0 : RemoveFrame(firstChild);
179 0 : return firstChild;
180 : }
181 0 : return nsnull;
182 : }
183 :
184 : void
185 0 : nsFrameList::DestroyFrame(nsIFrame* aFrame)
186 : {
187 0 : NS_PRECONDITION(aFrame, "null ptr");
188 0 : RemoveFrame(aFrame);
189 0 : aFrame->Destroy();
190 0 : }
191 :
192 : bool
193 0 : nsFrameList::DestroyFrameIfPresent(nsIFrame* aFrame)
194 : {
195 0 : NS_PRECONDITION(aFrame, "null ptr");
196 :
197 0 : if (RemoveFrameIfPresent(aFrame)) {
198 0 : aFrame->Destroy();
199 0 : return true;
200 : }
201 0 : return false;
202 : }
203 :
204 : nsFrameList::Slice
205 0 : nsFrameList::InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling,
206 : nsFrameList& aFrameList)
207 : {
208 0 : NS_PRECONDITION(aFrameList.NotEmpty(), "Unexpected empty list");
209 :
210 0 : if (aParent) {
211 0 : aFrameList.ApplySetParent(aParent);
212 : }
213 :
214 0 : NS_ASSERTION(IsEmpty() ||
215 : FirstChild()->GetParent() == aFrameList.FirstChild()->GetParent(),
216 : "frame to add has different parent");
217 0 : NS_ASSERTION(!aPrevSibling ||
218 : aPrevSibling->GetParent() == aFrameList.FirstChild()->GetParent(),
219 : "prev sibling has different parent");
220 : #ifdef DEBUG_FRAME_LIST
221 : // ContainsFrame is O(N)
222 : NS_ASSERTION(!aPrevSibling || ContainsFrame(aPrevSibling),
223 : "prev sibling is not on this list");
224 : #endif
225 :
226 0 : nsIFrame* firstNewFrame = aFrameList.FirstChild();
227 : nsIFrame* nextSibling;
228 0 : if (aPrevSibling) {
229 0 : nextSibling = aPrevSibling->GetNextSibling();
230 0 : aPrevSibling->SetNextSibling(firstNewFrame);
231 : }
232 : else {
233 0 : nextSibling = mFirstChild;
234 0 : mFirstChild = firstNewFrame;
235 : }
236 :
237 0 : nsIFrame* lastNewFrame = aFrameList.LastChild();
238 0 : lastNewFrame->SetNextSibling(nextSibling);
239 0 : if (!nextSibling) {
240 0 : mLastChild = lastNewFrame;
241 : }
242 :
243 0 : VerifyList();
244 :
245 0 : aFrameList.Clear();
246 0 : return Slice(*this, firstNewFrame, nextSibling);
247 : }
248 :
249 : nsFrameList
250 0 : nsFrameList::ExtractHead(FrameLinkEnumerator& aLink)
251 : {
252 0 : NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
253 0 : NS_PRECONDITION(!aLink.PrevFrame() ||
254 : aLink.PrevFrame()->GetNextSibling() ==
255 : aLink.NextFrame(),
256 : "Unexpected PrevFrame()");
257 0 : NS_PRECONDITION(aLink.PrevFrame() ||
258 : aLink.NextFrame() == FirstChild(),
259 : "Unexpected NextFrame()");
260 0 : NS_PRECONDITION(!aLink.PrevFrame() ||
261 : aLink.NextFrame() != FirstChild(),
262 : "Unexpected NextFrame()");
263 0 : NS_PRECONDITION(aLink.mEnd == nsnull,
264 : "Unexpected mEnd for frame link enumerator");
265 :
266 0 : nsIFrame* prev = aLink.PrevFrame();
267 0 : nsIFrame* newFirstFrame = nsnull;
268 0 : if (prev) {
269 : // Truncate the list after |prev| and hand the first part to our new list.
270 0 : prev->SetNextSibling(nsnull);
271 0 : newFirstFrame = mFirstChild;
272 0 : mFirstChild = aLink.NextFrame();
273 0 : if (!mFirstChild) { // we handed over the whole list
274 0 : mLastChild = nsnull;
275 : }
276 :
277 : // Now make sure aLink doesn't point to a frame we no longer have.
278 0 : aLink.mPrev = nsnull;
279 : }
280 : // else aLink is pointing to before our first frame. Nothing to do.
281 :
282 0 : return nsFrameList(newFirstFrame, prev);
283 : }
284 :
285 : nsFrameList
286 0 : nsFrameList::ExtractTail(FrameLinkEnumerator& aLink)
287 : {
288 0 : NS_PRECONDITION(&aLink.List() == this, "Unexpected list");
289 0 : NS_PRECONDITION(!aLink.PrevFrame() ||
290 : aLink.PrevFrame()->GetNextSibling() ==
291 : aLink.NextFrame(),
292 : "Unexpected PrevFrame()");
293 0 : NS_PRECONDITION(aLink.PrevFrame() ||
294 : aLink.NextFrame() == FirstChild(),
295 : "Unexpected NextFrame()");
296 0 : NS_PRECONDITION(!aLink.PrevFrame() ||
297 : aLink.NextFrame() != FirstChild(),
298 : "Unexpected NextFrame()");
299 0 : NS_PRECONDITION(aLink.mEnd == nsnull,
300 : "Unexpected mEnd for frame link enumerator");
301 :
302 0 : nsIFrame* prev = aLink.PrevFrame();
303 : nsIFrame* newFirstFrame;
304 : nsIFrame* newLastFrame;
305 0 : if (prev) {
306 : // Truncate the list after |prev| and hand the second part to our new list
307 0 : prev->SetNextSibling(nsnull);
308 0 : newFirstFrame = aLink.NextFrame();
309 0 : newLastFrame = newFirstFrame ? mLastChild : nsnull;
310 0 : mLastChild = prev;
311 : } else {
312 : // Hand the whole list over to our new list
313 0 : newFirstFrame = mFirstChild;
314 0 : newLastFrame = mLastChild;
315 0 : Clear();
316 : }
317 :
318 : // Now make sure aLink doesn't point to a frame we no longer have.
319 0 : aLink.mFrame = nsnull;
320 :
321 0 : NS_POSTCONDITION(aLink.AtEnd(), "What's going on here?");
322 :
323 0 : return nsFrameList(newFirstFrame, newLastFrame);
324 : }
325 :
326 : nsIFrame*
327 0 : nsFrameList::FrameAt(PRInt32 aIndex) const
328 : {
329 0 : NS_PRECONDITION(aIndex >= 0, "invalid arg");
330 0 : if (aIndex < 0) return nsnull;
331 0 : nsIFrame* frame = mFirstChild;
332 0 : while ((aIndex-- > 0) && frame) {
333 0 : frame = frame->GetNextSibling();
334 : }
335 0 : return frame;
336 : }
337 :
338 : PRInt32
339 0 : nsFrameList::IndexOf(nsIFrame* aFrame) const
340 : {
341 0 : PRInt32 count = 0;
342 0 : for (nsIFrame* f = mFirstChild; f; f = f->GetNextSibling()) {
343 0 : if (f == aFrame)
344 0 : return count;
345 0 : ++count;
346 : }
347 0 : return -1;
348 : }
349 :
350 : bool
351 0 : nsFrameList::ContainsFrame(const nsIFrame* aFrame) const
352 : {
353 0 : NS_PRECONDITION(aFrame, "null ptr");
354 :
355 0 : nsIFrame* frame = mFirstChild;
356 0 : while (frame) {
357 0 : if (frame == aFrame) {
358 0 : return true;
359 : }
360 0 : frame = frame->GetNextSibling();
361 : }
362 0 : return false;
363 : }
364 :
365 : PRInt32
366 0 : nsFrameList::GetLength() const
367 : {
368 0 : PRInt32 count = 0;
369 0 : nsIFrame* frame = mFirstChild;
370 0 : while (frame) {
371 0 : count++;
372 0 : frame = frame->GetNextSibling();
373 : }
374 0 : return count;
375 : }
376 :
377 0 : static int CompareByContentOrder(const nsIFrame* aF1, const nsIFrame* aF2)
378 : {
379 0 : if (aF1->GetContent() != aF2->GetContent()) {
380 0 : return nsLayoutUtils::CompareTreePosition(aF1->GetContent(), aF2->GetContent());
381 : }
382 :
383 0 : if (aF1 == aF2) {
384 0 : return 0;
385 : }
386 :
387 : const nsIFrame* f;
388 0 : for (f = aF2; f; f = f->GetPrevInFlow()) {
389 0 : if (f == aF1) {
390 : // f1 comes before f2 in the flow
391 0 : return -1;
392 : }
393 : }
394 0 : for (f = aF1; f; f = f->GetPrevInFlow()) {
395 0 : if (f == aF2) {
396 : // f1 comes after f2 in the flow
397 0 : return 1;
398 : }
399 : }
400 :
401 0 : NS_ASSERTION(false, "Frames for same content but not in relative flow order");
402 0 : return 0;
403 : }
404 :
405 : class CompareByContentOrderComparator
406 : {
407 : public:
408 : bool Equals(const nsIFrame* aA, const nsIFrame* aB) const {
409 : return aA == aB;
410 : }
411 : bool LessThan(const nsIFrame* aA, const nsIFrame* aB) const {
412 : return CompareByContentOrder(aA, aB) < 0;
413 : }
414 : };
415 :
416 : void
417 0 : nsFrameList::ApplySetParent(nsIFrame* aParent) const
418 : {
419 0 : NS_ASSERTION(aParent, "null ptr");
420 :
421 0 : for (nsIFrame* f = FirstChild(); f; f = f->GetNextSibling()) {
422 0 : f->SetParent(aParent);
423 : }
424 0 : }
425 :
426 : #ifdef DEBUG
427 : void
428 0 : nsFrameList::List(FILE* out) const
429 : {
430 0 : fputs("<\n", out);
431 0 : for (nsIFrame* frame = mFirstChild; frame;
432 : frame = frame->GetNextSibling()) {
433 0 : frame->List(out, 1);
434 : }
435 0 : fputs(">\n", out);
436 0 : }
437 : #endif
438 :
439 : #ifdef IBMBIDI
440 : nsIFrame*
441 0 : nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const
442 : {
443 0 : if (!mFirstChild)
444 0 : return nsnull;
445 :
446 0 : nsIFrame* parent = mFirstChild->GetParent();
447 0 : if (!parent)
448 0 : return aFrame ? aFrame->GetPrevSibling() : LastChild();
449 :
450 0 : nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(mFirstChild);
451 :
452 0 : nsAutoLineIterator iter = parent->GetLineIterator();
453 0 : if (!iter) {
454 : // Parent is not a block Frame
455 0 : if (parent->GetType() == nsGkAtoms::lineFrame) {
456 : // Line frames are not bidi-splittable, so need to consider bidi reordering
457 0 : if (baseLevel == NSBIDI_LTR) {
458 0 : return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
459 : } else { // RTL
460 0 : return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
461 : }
462 : } else {
463 : // Just get the next or prev sibling, depending on block and frame direction.
464 0 : nsBidiLevel frameEmbeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild);
465 0 : if ((frameEmbeddingLevel & 1) == (baseLevel & 1)) {
466 0 : return aFrame ? aFrame->GetPrevSibling() : LastChild();
467 : } else {
468 0 : return aFrame ? aFrame->GetNextSibling() : mFirstChild;
469 : }
470 : }
471 : }
472 :
473 : // Parent is a block frame, so use the LineIterator to find the previous visual
474 : // sibling on this line, or the last one on the previous line.
475 :
476 : PRInt32 thisLine;
477 0 : if (aFrame) {
478 0 : thisLine = iter->FindLineContaining(aFrame);
479 0 : if (thisLine < 0)
480 0 : return nsnull;
481 : } else {
482 0 : thisLine = iter->GetNumLines();
483 : }
484 :
485 0 : nsIFrame* frame = nsnull;
486 : nsIFrame* firstFrameOnLine;
487 : PRInt32 numFramesOnLine;
488 0 : nsRect lineBounds;
489 : PRUint32 lineFlags;
490 :
491 0 : if (aFrame) {
492 0 : iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
493 :
494 0 : if (baseLevel == NSBIDI_LTR) {
495 0 : frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
496 : } else { // RTL
497 0 : frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
498 : }
499 : }
500 :
501 0 : if (!frame && thisLine > 0) {
502 : // Get the last frame of the previous line
503 0 : iter->GetLine(thisLine - 1, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
504 :
505 0 : if (baseLevel == NSBIDI_LTR) {
506 0 : frame = nsBidiPresUtils::GetFrameToLeftOf(nsnull, firstFrameOnLine, numFramesOnLine);
507 : } else { // RTL
508 0 : frame = nsBidiPresUtils::GetFrameToRightOf(nsnull, firstFrameOnLine, numFramesOnLine);
509 : }
510 : }
511 0 : return frame;
512 : }
513 :
514 : nsIFrame*
515 0 : nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const
516 : {
517 0 : if (!mFirstChild)
518 0 : return nsnull;
519 :
520 0 : nsIFrame* parent = mFirstChild->GetParent();
521 0 : if (!parent)
522 0 : return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
523 :
524 0 : nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(mFirstChild);
525 :
526 0 : nsAutoLineIterator iter = parent->GetLineIterator();
527 0 : if (!iter) {
528 : // Parent is not a block Frame
529 0 : if (parent->GetType() == nsGkAtoms::lineFrame) {
530 : // Line frames are not bidi-splittable, so need to consider bidi reordering
531 0 : if (baseLevel == NSBIDI_LTR) {
532 0 : return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
533 : } else { // RTL
534 0 : return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
535 : }
536 : } else {
537 : // Just get the next or prev sibling, depending on block and frame direction.
538 0 : nsBidiLevel frameEmbeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild);
539 0 : if ((frameEmbeddingLevel & 1) == (baseLevel & 1)) {
540 0 : return aFrame ? aFrame->GetNextSibling() : mFirstChild;
541 : } else {
542 0 : return aFrame ? aFrame->GetPrevSibling() : LastChild();
543 : }
544 : }
545 : }
546 :
547 : // Parent is a block frame, so use the LineIterator to find the next visual
548 : // sibling on this line, or the first one on the next line.
549 :
550 : PRInt32 thisLine;
551 0 : if (aFrame) {
552 0 : thisLine = iter->FindLineContaining(aFrame);
553 0 : if (thisLine < 0)
554 0 : return nsnull;
555 : } else {
556 0 : thisLine = -1;
557 : }
558 :
559 0 : nsIFrame* frame = nsnull;
560 : nsIFrame* firstFrameOnLine;
561 : PRInt32 numFramesOnLine;
562 0 : nsRect lineBounds;
563 : PRUint32 lineFlags;
564 :
565 0 : if (aFrame) {
566 0 : iter->GetLine(thisLine, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
567 :
568 0 : if (baseLevel == NSBIDI_LTR) {
569 0 : frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, firstFrameOnLine, numFramesOnLine);
570 : } else { // RTL
571 0 : frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, firstFrameOnLine, numFramesOnLine);
572 : }
573 : }
574 :
575 0 : PRInt32 numLines = iter->GetNumLines();
576 0 : if (!frame && thisLine < numLines - 1) {
577 : // Get the first frame of the next line
578 0 : iter->GetLine(thisLine + 1, &firstFrameOnLine, &numFramesOnLine, lineBounds, &lineFlags);
579 :
580 0 : if (baseLevel == NSBIDI_LTR) {
581 0 : frame = nsBidiPresUtils::GetFrameToRightOf(nsnull, firstFrameOnLine, numFramesOnLine);
582 : } else { // RTL
583 0 : frame = nsBidiPresUtils::GetFrameToLeftOf(nsnull, firstFrameOnLine, numFramesOnLine);
584 : }
585 : }
586 0 : return frame;
587 : }
588 : #endif
589 :
590 : #ifdef DEBUG_FRAME_LIST
591 : void
592 : nsFrameList::VerifyList() const
593 : {
594 : NS_ASSERTION((mFirstChild == nsnull) == (mLastChild == nsnull),
595 : "bad list state");
596 :
597 : if (IsEmpty()) {
598 : return;
599 : }
600 :
601 : // Simple algorithm to find a loop in a linked list -- advance pointers
602 : // through it at speeds of 1 and 2, and if they ever get to be equal bail
603 : NS_ASSERTION(!mFirstChild->GetPrevSibling(), "bad prev sibling pointer");
604 : nsIFrame *first = mFirstChild, *second = mFirstChild;
605 : for (;;) {
606 : first = first->GetNextSibling();
607 : second = second->GetNextSibling();
608 : if (!second) {
609 : break;
610 : }
611 : NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
612 : "bad prev sibling pointer");
613 : second = second->GetNextSibling();
614 : if (first == second) {
615 : // Loop detected! Since second advances faster, they can't both be null;
616 : // we would have broken out of the loop long ago.
617 : NS_ERROR("loop in frame list. This will probably hang soon.");
618 : return;
619 : }
620 : if (!second) {
621 : break;
622 : }
623 : NS_ASSERTION(second->GetPrevSibling()->GetNextSibling() == second,
624 : "bad prev sibling pointer");
625 : }
626 :
627 : NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
628 : "bogus mLastChild");
629 : // XXX we should also assert that all GetParent() are either null or
630 : // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
631 : // prevents that, e.g. table captions.
632 : }
633 : #endif
|