1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsFrameList.h"
9 #include "mozilla/ArenaObjectID.h"
10 #include "mozilla/PresShell.h"
11 #include "nsBidiPresUtils.h"
12 #include "nsContainerFrame.h"
13 #include "nsGkAtoms.h"
14 #include "nsILineIterator.h"
15 #include "nsLayoutUtils.h"
16 #include "nsPresContext.h"
18 using namespace mozilla
;
23 const AlignedFrameListBytes gEmptyFrameListBytes
= {0};
26 } // namespace mozilla
28 void* nsFrameList::operator new(size_t sz
, mozilla::PresShell
* aPresShell
) {
29 return aPresShell
->AllocateByObjectID(eArenaObjectID_nsFrameList
, sz
);
32 void nsFrameList::Delete(mozilla::PresShell
* aPresShell
) {
33 MOZ_ASSERT(this != &EmptyList(), "Shouldn't Delete() this list");
34 NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
36 aPresShell
->FreeByObjectID(eArenaObjectID_nsFrameList
, this);
39 void nsFrameList::DestroyFrames() {
40 while (nsIFrame
* frame
= RemoveFirstChild()) {
46 void nsFrameList::DestroyFramesFrom(
47 nsIFrame
* aDestructRoot
, layout::PostFrameDestroyData
& aPostDestroyData
) {
48 MOZ_ASSERT(aDestructRoot
, "Missing destruct root");
50 while (nsIFrame
* frame
= RemoveFirstChild()) {
51 frame
->DestroyFrom(aDestructRoot
, aPostDestroyData
);
56 void nsFrameList::SetFrames(nsIFrame
* aFrameList
) {
57 MOZ_ASSERT(!mFirstChild
, "Losing frames");
59 mFirstChild
= aFrameList
;
60 mLastChild
= nsLayoutUtils::GetLastSibling(mFirstChild
);
63 void nsFrameList::RemoveFrame(nsIFrame
* aFrame
) {
64 MOZ_ASSERT(aFrame
, "null ptr");
65 #ifdef DEBUG_FRAME_LIST
66 // ContainsFrame is O(N)
67 MOZ_ASSERT(ContainsFrame(aFrame
), "wrong list");
70 nsIFrame
* nextFrame
= aFrame
->GetNextSibling();
71 if (aFrame
== mFirstChild
) {
72 mFirstChild
= nextFrame
;
73 aFrame
->SetNextSibling(nullptr);
78 nsIFrame
* prevSibling
= aFrame
->GetPrevSibling();
79 NS_ASSERTION(prevSibling
&& prevSibling
->GetNextSibling() == aFrame
,
80 "Broken frame linkage");
81 prevSibling
->SetNextSibling(nextFrame
);
82 aFrame
->SetNextSibling(nullptr);
84 mLastChild
= prevSibling
;
89 nsFrameList
nsFrameList::RemoveFramesAfter(nsIFrame
* aAfterFrame
) {
92 result
.InsertFrames(nullptr, nullptr, *this);
96 MOZ_ASSERT(NotEmpty(), "illegal operation on empty list");
97 #ifdef DEBUG_FRAME_LIST
98 MOZ_ASSERT(ContainsFrame(aAfterFrame
), "wrong list");
101 nsIFrame
* tail
= aAfterFrame
->GetNextSibling();
102 // if (!tail) return EmptyList(); -- worth optimizing this case?
103 nsIFrame
* oldLastChild
= mLastChild
;
104 mLastChild
= aAfterFrame
;
105 aAfterFrame
->SetNextSibling(nullptr);
106 return nsFrameList(tail
, tail
? oldLastChild
: nullptr);
109 nsIFrame
* nsFrameList::RemoveFirstChild() {
111 nsIFrame
* firstChild
= mFirstChild
;
112 RemoveFrame(firstChild
);
118 void nsFrameList::DestroyFrame(nsIFrame
* aFrame
) {
119 MOZ_ASSERT(aFrame
, "null ptr");
124 nsFrameList::Slice
nsFrameList::InsertFrames(nsContainerFrame
* aParent
,
125 nsIFrame
* aPrevSibling
,
126 nsFrameList
& aFrameList
) {
127 MOZ_ASSERT(aFrameList
.NotEmpty(), "Unexpected empty list");
130 aFrameList
.ApplySetParent(aParent
);
133 NS_ASSERTION(IsEmpty() || FirstChild()->GetParent() ==
134 aFrameList
.FirstChild()->GetParent(),
135 "frame to add has different parent");
136 NS_ASSERTION(!aPrevSibling
|| aPrevSibling
->GetParent() ==
137 aFrameList
.FirstChild()->GetParent(),
138 "prev sibling has different parent");
139 #ifdef DEBUG_FRAME_LIST
140 // ContainsFrame is O(N)
141 NS_ASSERTION(!aPrevSibling
|| ContainsFrame(aPrevSibling
),
142 "prev sibling is not on this list");
145 nsIFrame
* firstNewFrame
= aFrameList
.FirstChild();
146 nsIFrame
* nextSibling
;
148 nextSibling
= aPrevSibling
->GetNextSibling();
149 aPrevSibling
->SetNextSibling(firstNewFrame
);
151 nextSibling
= mFirstChild
;
152 mFirstChild
= firstNewFrame
;
155 nsIFrame
* lastNewFrame
= aFrameList
.LastChild();
156 lastNewFrame
->SetNextSibling(nextSibling
);
158 mLastChild
= lastNewFrame
;
164 return Slice(*this, firstNewFrame
, nextSibling
);
167 nsFrameList
nsFrameList::ExtractHead(FrameLinkEnumerator
& aLink
) {
168 MOZ_ASSERT(&aLink
.List() == this, "Unexpected list");
169 MOZ_ASSERT(!aLink
.PrevFrame() ||
170 aLink
.PrevFrame()->GetNextSibling() == aLink
.NextFrame(),
171 "Unexpected PrevFrame()");
172 MOZ_ASSERT(aLink
.PrevFrame() || aLink
.NextFrame() == FirstChild(),
173 "Unexpected NextFrame()");
174 MOZ_ASSERT(!aLink
.PrevFrame() || aLink
.NextFrame() != FirstChild(),
175 "Unexpected NextFrame()");
176 MOZ_ASSERT(aLink
.mEnd
== nullptr,
177 "Unexpected mEnd for frame link enumerator");
179 nsIFrame
* prev
= aLink
.PrevFrame();
180 nsIFrame
* newFirstFrame
= nullptr;
182 // Truncate the list after |prev| and hand the first part to our new list.
183 prev
->SetNextSibling(nullptr);
184 newFirstFrame
= mFirstChild
;
185 mFirstChild
= aLink
.NextFrame();
186 if (!mFirstChild
) { // we handed over the whole list
187 mLastChild
= nullptr;
190 // Now make sure aLink doesn't point to a frame we no longer have.
191 aLink
.mPrev
= nullptr;
193 // else aLink is pointing to before our first frame. Nothing to do.
195 return nsFrameList(newFirstFrame
, prev
);
198 nsFrameList
nsFrameList::ExtractTail(FrameLinkEnumerator
& aLink
) {
199 MOZ_ASSERT(&aLink
.List() == this, "Unexpected list");
200 MOZ_ASSERT(!aLink
.PrevFrame() ||
201 aLink
.PrevFrame()->GetNextSibling() == aLink
.NextFrame(),
202 "Unexpected PrevFrame()");
203 MOZ_ASSERT(aLink
.PrevFrame() || aLink
.NextFrame() == FirstChild(),
204 "Unexpected NextFrame()");
205 MOZ_ASSERT(!aLink
.PrevFrame() || aLink
.NextFrame() != FirstChild(),
206 "Unexpected NextFrame()");
207 MOZ_ASSERT(aLink
.mEnd
== nullptr,
208 "Unexpected mEnd for frame link enumerator");
210 nsIFrame
* prev
= aLink
.PrevFrame();
211 nsIFrame
* newFirstFrame
;
212 nsIFrame
* newLastFrame
;
214 // Truncate the list after |prev| and hand the second part to our new list
215 prev
->SetNextSibling(nullptr);
216 newFirstFrame
= aLink
.NextFrame();
217 newLastFrame
= newFirstFrame
? mLastChild
: nullptr;
220 // Hand the whole list over to our new list
221 newFirstFrame
= mFirstChild
;
222 newLastFrame
= mLastChild
;
226 // Now make sure aLink doesn't point to a frame we no longer have.
227 aLink
.mFrame
= nullptr;
229 MOZ_ASSERT(aLink
.AtEnd(), "What's going on here?");
231 return nsFrameList(newFirstFrame
, newLastFrame
);
234 nsIFrame
* nsFrameList::FrameAt(int32_t aIndex
) const {
235 MOZ_ASSERT(aIndex
>= 0, "invalid arg");
236 if (aIndex
< 0) return nullptr;
237 nsIFrame
* frame
= mFirstChild
;
238 while ((aIndex
-- > 0) && frame
) {
239 frame
= frame
->GetNextSibling();
244 int32_t nsFrameList::IndexOf(nsIFrame
* aFrame
) const {
246 for (nsIFrame
* f
= mFirstChild
; f
; f
= f
->GetNextSibling()) {
247 if (f
== aFrame
) return count
;
253 bool nsFrameList::ContainsFrame(const nsIFrame
* aFrame
) const {
254 MOZ_ASSERT(aFrame
, "null ptr");
256 nsIFrame
* frame
= mFirstChild
;
258 if (frame
== aFrame
) {
261 frame
= frame
->GetNextSibling();
266 int32_t nsFrameList::GetLength() const {
268 nsIFrame
* frame
= mFirstChild
;
271 frame
= frame
->GetNextSibling();
276 void nsFrameList::ApplySetParent(nsContainerFrame
* aParent
) const {
277 NS_ASSERTION(aParent
, "null ptr");
279 for (nsIFrame
* f
= FirstChild(); f
; f
= f
->GetNextSibling()) {
280 f
->SetParent(aParent
);
285 void nsFrameList::UnhookFrameFromSiblings(nsIFrame
* aFrame
) {
286 MOZ_ASSERT(aFrame
->GetPrevSibling() && aFrame
->GetNextSibling());
287 nsIFrame
* const nextSibling
= aFrame
->GetNextSibling();
288 nsIFrame
* const prevSibling
= aFrame
->GetPrevSibling();
289 aFrame
->SetNextSibling(nullptr);
290 prevSibling
->SetNextSibling(nextSibling
);
291 MOZ_ASSERT(!aFrame
->GetPrevSibling() && !aFrame
->GetNextSibling());
294 #ifdef DEBUG_FRAME_DUMP
295 void nsFrameList::List(FILE* out
) const {
296 fprintf_stderr(out
, "<\n");
297 for (nsIFrame
* frame
= mFirstChild
; frame
; frame
= frame
->GetNextSibling()) {
298 frame
->List(out
, " ");
300 fprintf_stderr(out
, ">\n");
304 nsIFrame
* nsFrameList::GetPrevVisualFor(nsIFrame
* aFrame
) const {
305 if (!mFirstChild
) return nullptr;
307 nsIFrame
* parent
= mFirstChild
->GetParent();
308 if (!parent
) return aFrame
? aFrame
->GetPrevSibling() : LastChild();
310 nsBidiDirection paraDir
= nsBidiPresUtils::ParagraphDirection(mFirstChild
);
312 nsAutoLineIterator iter
= parent
->GetLineIterator();
314 // Parent is not a block Frame
315 if (parent
->IsLineFrame()) {
316 // Line frames are not bidi-splittable, so need to consider bidi
318 if (paraDir
== NSBIDI_LTR
) {
319 return nsBidiPresUtils::GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
321 return nsBidiPresUtils::GetFrameToRightOf(aFrame
, mFirstChild
, -1);
324 // Just get the next or prev sibling, depending on block and frame
326 if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild
)) {
327 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
329 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
334 // Parent is a block frame, so use the LineIterator to find the previous
335 // visual sibling on this line, or the last one on the previous line.
339 thisLine
= iter
->FindLineContaining(aFrame
);
340 if (thisLine
< 0) return nullptr;
342 thisLine
= iter
->GetNumLines();
345 nsIFrame
* frame
= nullptr;
346 nsIFrame
* firstFrameOnLine
;
347 int32_t numFramesOnLine
;
351 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
353 if (paraDir
== NSBIDI_LTR
) {
354 frame
= nsBidiPresUtils::GetFrameToLeftOf(aFrame
, firstFrameOnLine
,
357 frame
= nsBidiPresUtils::GetFrameToRightOf(aFrame
, firstFrameOnLine
,
362 if (!frame
&& thisLine
> 0) {
363 // Get the last frame of the previous line
364 iter
->GetLine(thisLine
- 1, &firstFrameOnLine
, &numFramesOnLine
,
367 if (paraDir
== NSBIDI_LTR
) {
368 frame
= nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine
,
371 frame
= nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine
,
378 nsIFrame
* nsFrameList::GetNextVisualFor(nsIFrame
* aFrame
) const {
379 if (!mFirstChild
) return nullptr;
381 nsIFrame
* parent
= mFirstChild
->GetParent();
382 if (!parent
) return aFrame
? aFrame
->GetPrevSibling() : mFirstChild
;
384 nsBidiDirection paraDir
= nsBidiPresUtils::ParagraphDirection(mFirstChild
);
386 nsAutoLineIterator iter
= parent
->GetLineIterator();
388 // Parent is not a block Frame
389 if (parent
->IsLineFrame()) {
390 // Line frames are not bidi-splittable, so need to consider bidi
392 if (paraDir
== NSBIDI_LTR
) {
393 return nsBidiPresUtils::GetFrameToRightOf(aFrame
, mFirstChild
, -1);
395 return nsBidiPresUtils::GetFrameToLeftOf(aFrame
, mFirstChild
, -1);
398 // Just get the next or prev sibling, depending on block and frame
400 if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild
)) {
401 return aFrame
? aFrame
->GetNextSibling() : mFirstChild
;
403 return aFrame
? aFrame
->GetPrevSibling() : LastChild();
408 // Parent is a block frame, so use the LineIterator to find the next visual
409 // sibling on this line, or the first one on the next line.
413 thisLine
= iter
->FindLineContaining(aFrame
);
414 if (thisLine
< 0) return nullptr;
419 nsIFrame
* frame
= nullptr;
420 nsIFrame
* firstFrameOnLine
;
421 int32_t numFramesOnLine
;
425 iter
->GetLine(thisLine
, &firstFrameOnLine
, &numFramesOnLine
, lineBounds
);
427 if (paraDir
== NSBIDI_LTR
) {
428 frame
= nsBidiPresUtils::GetFrameToRightOf(aFrame
, firstFrameOnLine
,
431 frame
= nsBidiPresUtils::GetFrameToLeftOf(aFrame
, firstFrameOnLine
,
436 int32_t numLines
= iter
->GetNumLines();
437 if (!frame
&& thisLine
< numLines
- 1) {
438 // Get the first frame of the next line
439 iter
->GetLine(thisLine
+ 1, &firstFrameOnLine
, &numFramesOnLine
,
442 if (paraDir
== NSBIDI_LTR
) {
443 frame
= nsBidiPresUtils::GetFrameToRightOf(nullptr, firstFrameOnLine
,
446 frame
= nsBidiPresUtils::GetFrameToLeftOf(nullptr, firstFrameOnLine
,
453 #ifdef DEBUG_FRAME_LIST
454 void nsFrameList::VerifyList() const {
455 NS_ASSERTION((mFirstChild
== nullptr) == (mLastChild
== nullptr),
462 // Simple algorithm to find a loop in a linked list -- advance pointers
463 // through it at speeds of 1 and 2, and if they ever get to be equal bail
464 NS_ASSERTION(!mFirstChild
->GetPrevSibling(), "bad prev sibling pointer");
465 nsIFrame
*first
= mFirstChild
, *second
= mFirstChild
;
467 first
= first
->GetNextSibling();
468 second
= second
->GetNextSibling();
472 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
473 "bad prev sibling pointer");
474 second
= second
->GetNextSibling();
475 if (first
== second
) {
476 // Loop detected! Since second advances faster, they can't both be null;
477 // we would have broken out of the loop long ago.
478 NS_ERROR("loop in frame list. This will probably hang soon.");
484 NS_ASSERTION(second
->GetPrevSibling()->GetNextSibling() == second
,
485 "bad prev sibling pointer");
488 NS_ASSERTION(mLastChild
== nsLayoutUtils::GetLastSibling(mFirstChild
),
490 // XXX we should also assert that all GetParent() are either null or
491 // the same non-null value, but nsCSSFrameConstructor::nsFrameItems
492 // prevents that, e.g. table captions.
499 AutoFrameListPtr::~AutoFrameListPtr() {
501 mFrameList
->Delete(mPresContext
->PresShell());
505 } // namespace layout
506 } // namespace mozilla