1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsFrameList.h"
7 #include "nsContainerFrame.h"
8 #include "nsLayoutUtils.h"
9 #include "nsPresContext.h"
10 #include "nsIPresShell.h"
12 #include "nsGkAtoms.h"
13 #include "nsILineIterator.h"
14 #include "nsBidiPresUtils.h"
19 const AlignedFrameListBytes gEmptyFrameListBytes
= { 0 };
25 nsFrameList::operator new(size_t sz
, nsIPresShell
* aPresShell
) CPP_THROW_NEW
27 return aPresShell
->AllocateByObjectID(nsPresArena::nsFrameList_id
, sz
);
31 nsFrameList::Delete(nsIPresShell
* aPresShell
)
33 NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list");
34 NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
36 aPresShell
->FreeByObjectID(nsPresArena::nsFrameList_id
, this);
40 nsFrameList::DestroyFrames()
42 while (nsIFrame
* frame
= RemoveFirstChild()) {
49 nsFrameList::DestroyFramesFrom(nsIFrame
* aDestructRoot
)
51 NS_PRECONDITION(aDestructRoot
, "Missing destruct root");
53 while (nsIFrame
* frame
= RemoveFirstChild()) {
54 frame
->DestroyFrom(aDestructRoot
);
60 nsFrameList::SetFrames(nsIFrame
* aFrameList
)
62 NS_PRECONDITION(!mFirstChild
, "Losing frames");
64 mFirstChild
= aFrameList
;
65 mLastChild
= nsLayoutUtils::GetLastSibling(mFirstChild
);
69 nsFrameList::RemoveFrame(nsIFrame
* aFrame
)
71 NS_PRECONDITION(aFrame
, "null ptr");
72 #ifdef DEBUG_FRAME_LIST
73 // ContainsFrame is O(N)
74 NS_PRECONDITION(ContainsFrame(aFrame
), "wrong list");
77 nsIFrame
* nextFrame
= aFrame
->GetNextSibling();
78 if (aFrame
== mFirstChild
) {
79 mFirstChild
= nextFrame
;
80 aFrame
->SetNextSibling(nullptr);
86 nsIFrame
* prevSibling
= aFrame
->GetPrevSibling();
87 NS_ASSERTION(prevSibling
&& prevSibling
->GetNextSibling() == aFrame
,
88 "Broken frame linkage");
89 prevSibling
->SetNextSibling(nextFrame
);
90 aFrame
->SetNextSibling(nullptr);
92 mLastChild
= prevSibling
;
98 nsFrameList::RemoveFramesAfter(nsIFrame
* aAfterFrame
)
102 result
.InsertFrames(nullptr, nullptr, *this);
106 NS_PRECONDITION(NotEmpty(), "illegal operation on empty list");
107 #ifdef DEBUG_FRAME_LIST
108 NS_PRECONDITION(ContainsFrame(aAfterFrame
), "wrong list");
111 nsIFrame
* tail
= aAfterFrame
->GetNextSibling();
112 // if (!tail) return EmptyList(); -- worth optimizing this case?
113 nsIFrame
* oldLastChild
= mLastChild
;
114 mLastChild
= aAfterFrame
;
115 aAfterFrame
->SetNextSibling(nullptr);
116 return nsFrameList(tail
, tail
? oldLastChild
: nullptr);
120 nsFrameList::RemoveFirstChild()
123 nsIFrame
* firstChild
= mFirstChild
;
124 RemoveFrame(firstChild
);
131 nsFrameList::DestroyFrame(nsIFrame
* aFrame
)
133 NS_PRECONDITION(aFrame
, "null ptr");
139 nsFrameList::InsertFrames(nsContainerFrame
* aParent
, nsIFrame
* aPrevSibling
,
140 nsFrameList
& aFrameList
)
142 NS_PRECONDITION(aFrameList
.NotEmpty(), "Unexpected empty list");
145 aFrameList
.ApplySetParent(aParent
);
148 NS_ASSERTION(IsEmpty() ||
149 FirstChild()->GetParent() == aFrameList
.FirstChild()->GetParent(),
150 "frame to add has different parent");
151 NS_ASSERTION(!aPrevSibling
||
152 aPrevSibling
->GetParent() == aFrameList
.FirstChild()->GetParent(),
153 "prev sibling has different parent");
154 #ifdef DEBUG_FRAME_LIST
155 // ContainsFrame is O(N)
156 NS_ASSERTION(!aPrevSibling
|| ContainsFrame(aPrevSibling
),
157 "prev sibling is not on this list");
160 nsIFrame
* firstNewFrame
= aFrameList
.FirstChild();
161 nsIFrame
* nextSibling
;
163 nextSibling
= aPrevSibling
->GetNextSibling();
164 aPrevSibling
->SetNextSibling(firstNewFrame
);
167 nextSibling
= mFirstChild
;
168 mFirstChild
= firstNewFrame
;
171 nsIFrame
* lastNewFrame
= aFrameList
.LastChild();
172 lastNewFrame
->SetNextSibling(nextSibling
);
174 mLastChild
= lastNewFrame
;
180 return Slice(*this, firstNewFrame
, nextSibling
);
184 nsFrameList::ExtractHead(FrameLinkEnumerator
& aLink
)
186 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
187 NS_PRECONDITION(!aLink
.PrevFrame() ||
188 aLink
.PrevFrame()->GetNextSibling() ==
190 "Unexpected PrevFrame()");
191 NS_PRECONDITION(aLink
.PrevFrame() ||
192 aLink
.NextFrame() == FirstChild(),
193 "Unexpected NextFrame()");
194 NS_PRECONDITION(!aLink
.PrevFrame() ||
195 aLink
.NextFrame() != FirstChild(),
196 "Unexpected NextFrame()");
197 NS_PRECONDITION(aLink
.mEnd
== nullptr,
198 "Unexpected mEnd for frame link enumerator");
200 nsIFrame
* prev
= aLink
.PrevFrame();
201 nsIFrame
* newFirstFrame
= nullptr;
203 // Truncate the list after |prev| and hand the first part to our new list.
204 prev
->SetNextSibling(nullptr);
205 newFirstFrame
= mFirstChild
;
206 mFirstChild
= aLink
.NextFrame();
207 if (!mFirstChild
) { // we handed over the whole list
208 mLastChild
= nullptr;
211 // Now make sure aLink doesn't point to a frame we no longer have.
212 aLink
.mPrev
= nullptr;
214 // else aLink is pointing to before our first frame. Nothing to do.
216 return nsFrameList(newFirstFrame
, prev
);
220 nsFrameList::ExtractTail(FrameLinkEnumerator
& aLink
)
222 NS_PRECONDITION(&aLink
.List() == this, "Unexpected list");
223 NS_PRECONDITION(!aLink
.PrevFrame() ||
224 aLink
.PrevFrame()->GetNextSibling() ==
226 "Unexpected PrevFrame()");
227 NS_PRECONDITION(aLink
.PrevFrame() ||
228 aLink
.NextFrame() == FirstChild(),
229 "Unexpected NextFrame()");
230 NS_PRECONDITION(!aLink
.PrevFrame() ||
231 aLink
.NextFrame() != FirstChild(),
232 "Unexpected NextFrame()");
233 NS_PRECONDITION(aLink
.mEnd
== nullptr,
234 "Unexpected mEnd for frame link enumerator");
236 nsIFrame
* prev
= aLink
.PrevFrame();
237 nsIFrame
* newFirstFrame
;
238 nsIFrame
* newLastFrame
;
240 // Truncate the list after |prev| and hand the second part to our new list
241 prev
->SetNextSibling(nullptr);
242 newFirstFrame
= aLink
.NextFrame();
243 newLastFrame
= newFirstFrame
? mLastChild
: nullptr;
246 // Hand the whole list over to our new list
247 newFirstFrame
= mFirstChild
;
248 newLastFrame
= mLastChild
;
252 // Now make sure aLink doesn't point to a frame we no longer have.
253 aLink
.mFrame
= nullptr;
255 NS_POSTCONDITION(aLink
.AtEnd(), "What's going on here?");
257 return nsFrameList(newFirstFrame
, newLastFrame
);
261 nsFrameList::FrameAt(int32_t aIndex
) const
263 NS_PRECONDITION(aIndex
>= 0, "invalid arg");
264 if (aIndex
< 0) return nullptr;
265 nsIFrame
* frame
= mFirstChild
;
266 while ((aIndex
-- > 0) && frame
) {
267 frame
= frame
->GetNextSibling();
273 nsFrameList::IndexOf(nsIFrame
* aFrame
) const
276 for (nsIFrame
* f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
285 nsFrameList::ContainsFrame(const nsIFrame
* aFrame
) const
287 NS_PRECONDITION(aFrame
, "null ptr");
289 nsIFrame
* frame
= mFirstChild
;
291 if (frame
== aFrame
) {
294 frame
= frame
->GetNextSibling();
300 nsFrameList::GetLength() const
303 nsIFrame
* frame
= mFirstChild
;
306 frame
= frame
->GetNextSibling();
312 nsFrameList::ApplySetParent(nsContainerFrame
* aParent
) const
314 NS_ASSERTION(aParent
, "null ptr");
316 for (nsIFrame
* f
= FirstChild(); f
; f
= f
->GetNextSibling()) {
317 f
->SetParent(aParent
);
322 nsFrameList::UnhookFrameFromSiblings(nsIFrame
* aFrame
)
324 MOZ_ASSERT(aFrame
->GetPrevSibling() && aFrame
->GetNextSibling());
325 nsIFrame
* const nextSibling
= aFrame
->GetNextSibling();
326 nsIFrame
* const prevSibling
= aFrame
->GetPrevSibling();
327 aFrame
->SetNextSibling(nullptr);
328 prevSibling
->SetNextSibling(nextSibling
);
329 MOZ_ASSERT(!aFrame
->GetPrevSibling() && !aFrame
->GetNextSibling());
332 #ifdef DEBUG_FRAME_DUMP
334 nsFrameList::List(FILE* out
) const
336 fprintf_stderr(out
, "<\n");
337 for (nsIFrame
* frame
= mFirstChild
; frame
;
338 frame
= frame
->GetNextSibling()) {
339 frame
->List(out
, " ");
341 fprintf_stderr(out
, ">\n");
346 nsFrameList::GetPrevVisualFor(nsIFrame
* aFrame
) const
351 nsIFrame
* parent
= mFirstChild
->GetParent();
353 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
355 nsBidiDirection paraDir
= nsBidiPresUtils::ParagraphDirection(mFirstChild
);
357 nsAutoLineIterator iter
= parent
->GetLineIterator();
359 // Parent is not a block Frame
360 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
361 // Line frames are not bidi-splittable, so need to consider bidi reordering
362 if (paraDir
== NSBIDI_LTR
) {
363 return nsBidiPresUtils::GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
365 return nsBidiPresUtils::GetFrameToRightOf(aFrame
, mFirstChild
, -1);
368 // Just get the next or prev sibling, depending on block and frame direction.
369 if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild
)) {
370 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
372 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
377 // Parent is a block frame, so use the LineIterator to find the previous visual
378 // sibling on this line, or the last one on the previous line.
382 thisLine
= iter
->FindLineContaining(aFrame
);
386 thisLine
= iter
->GetNumLines();
389 nsIFrame
* frame
= nullptr;
390 nsIFrame
* firstFrameOnLine
;
391 int32_t numFramesOnLine
;
395 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
397 if (paraDir
== NSBIDI_LTR
) {
398 frame
= nsBidiPresUtils::GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
400 frame
= nsBidiPresUtils::GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
404 if (!frame
&& thisLine
> 0) {
405 // Get the last frame of the previous line
406 iter
->GetLine(thisLine
- 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
408 if (paraDir
== NSBIDI_LTR
) {
409 frame
= nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine
, numFramesOnLine
);
411 frame
= nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine
, numFramesOnLine
);
418 nsFrameList::GetNextVisualFor(nsIFrame
* aFrame
) const
423 nsIFrame
* parent
= mFirstChild
->GetParent();
425 return aFrame
? aFrame
->GetPrevSibling() : mFirstChild
;
427 nsBidiDirection paraDir
= nsBidiPresUtils::ParagraphDirection(mFirstChild
);
429 nsAutoLineIterator iter
= parent
->GetLineIterator();
431 // Parent is not a block Frame
432 if (parent
->GetType() == nsGkAtoms::lineFrame
) {
433 // Line frames are not bidi-splittable, so need to consider bidi reordering
434 if (paraDir
== NSBIDI_LTR
) {
435 return nsBidiPresUtils::GetFrameToRightOf(aFrame
, mFirstChild
, -1);
437 return nsBidiPresUtils::GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
440 // Just get the next or prev sibling, depending on block and frame direction.
441 if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild
)) {
442 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
444 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
449 // Parent is a block frame, so use the LineIterator to find the next visual
450 // sibling on this line, or the first one on the next line.
454 thisLine
= iter
->FindLineContaining(aFrame
);
461 nsIFrame
* frame
= nullptr;
462 nsIFrame
* firstFrameOnLine
;
463 int32_t numFramesOnLine
;
467 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
469 if (paraDir
== NSBIDI_LTR
) {
470 frame
= nsBidiPresUtils::GetFrameToRightOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
472 frame
= nsBidiPresUtils::GetFrameToLeftOf(aFrame
, firstFrameOnLine
, numFramesOnLine
);
476 int32_t numLines
= iter
->GetNumLines();
477 if (!frame
&& thisLine
< numLines
- 1) {
478 // Get the first frame of the next line
479 iter
->GetLine(thisLine
+ 1, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
481 if (paraDir
== NSBIDI_LTR
) {
482 frame
= nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine
, numFramesOnLine
);
484 frame
= nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine
, numFramesOnLine
);
490 #ifdef DEBUG_FRAME_LIST
492 nsFrameList::VerifyList() const
494 NS_ASSERTION((mFirstChild
== nullptr) == (mLastChild
== nullptr),
501 // Simple algorithm to find a loop in a linked list -- advance pointers
502 // through it at speeds of 1 and 2, and if they ever get to be equal bail
503 NS_ASSERTION(!mFirstChild
->GetPrevSibling(), "bad prev sibling pointer");
504 nsIFrame
*first
= mFirstChild
, *second
= mFirstChild
;
506 first
= first
->GetNextSibling();
507 second
= second
->GetNextSibling();
511 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
512 "bad prev sibling pointer");
513 second
= second
->GetNextSibling();
514 if (first
== second
) {
515 // Loop detected! Since second advances faster, they can't both be null;
516 // we would have broken out of the loop long ago.
517 NS_ERROR("loop in frame list. This will probably hang soon.");
523 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
524 "bad prev sibling pointer");
527 NS_ASSERTION(mLastChild
== nsLayoutUtils::GetLastSibling(mFirstChild
),
529 // XXX we should also assert that all GetParent() are either null or
530 // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
531 // prevents that, e.g. table captions.
538 AutoFrameListPtr::~AutoFrameListPtr()
541 mFrameList
->Delete(mPresContext
->PresShell());