Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 #ifndef nsFrameList_h___
7 #define nsFrameList_h___
9 #include <stdio.h> /* for FILE* */
10 #include "nsDebug.h"
11 #include "nsTArrayForwardDeclare.h"
13 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
14 // DEBUG_FRAME_DUMP enables nsIFrame::List and related methods.
15 // You can also define this in a non-DEBUG build if you need frame dumps.
16 #define DEBUG_FRAME_DUMP 1
17 #endif
19 class nsIFrame;
20 class nsIPresShell;
21 class nsPresContext;
23 namespace mozilla {
24 namespace layout {
25 class FrameChildList;
26 enum FrameChildListID {
27 // The individual concrete child lists.
28 kPrincipalList = 0x1,
29 kPopupList = 0x2,
30 kCaptionList = 0x4,
31 kColGroupList = 0x8,
32 kSelectPopupList = 0x10,
33 kAbsoluteList = 0x20,
34 kFixedList = 0x40,
35 kOverflowList = 0x80,
36 kOverflowContainersList = 0x100,
37 kExcessOverflowContainersList = 0x200,
38 kOverflowOutOfFlowList = 0x400,
39 kFloatList = 0x800,
40 kBulletList = 0x1000,
41 kPushedFloatsList = 0x2000,
42 // A special alias for kPrincipalList that suppress the reflow request that
43 // is normally done when manipulating child lists.
44 kNoReflowPrincipalList = 0x4000
45 };
46 }
47 }
49 // Uncomment this to enable expensive frame-list integrity checking
50 // #define DEBUG_FRAME_LIST
52 /**
53 * A class for managing a list of frames.
54 */
55 class nsFrameList {
56 public:
57 nsFrameList() :
58 mFirstChild(nullptr), mLastChild(nullptr)
59 {
60 }
62 nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) :
63 mFirstChild(aFirstFrame), mLastChild(aLastFrame)
64 {
65 VerifyList();
66 }
68 nsFrameList(const nsFrameList& aOther) :
69 mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild)
70 {
71 }
73 /**
74 * Allocate a nsFrameList from the shell arena.
75 */
76 void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
78 /**
79 * Deallocate this list that was allocated from the shell arena.
80 * The list is required to be empty.
81 */
82 void Delete(nsIPresShell* aPresShell);
84 /**
85 * For each frame in this list: remove it from the list then call
86 * Destroy() on it.
87 */
88 void DestroyFrames();
90 /**
91 * For each frame in this list: remove it from the list then call
92 * DestroyFrom(aDestructRoot) on it.
93 */
94 void DestroyFramesFrom(nsIFrame* aDestructRoot);
96 void Clear() { mFirstChild = mLastChild = nullptr; }
98 void SetFrames(nsIFrame* aFrameList);
100 void SetFrames(nsFrameList& aFrameList) {
101 NS_PRECONDITION(!mFirstChild, "Losing frames");
103 mFirstChild = aFrameList.FirstChild();
104 mLastChild = aFrameList.LastChild();
105 aFrameList.Clear();
106 }
108 class Slice;
110 /**
111 * Append aFrameList to this list. If aParent is not null,
112 * reparents the newly added frames. Clears out aFrameList and
113 * returns a list slice represening the newly-appended frames.
114 */
115 Slice AppendFrames(nsIFrame* aParent, nsFrameList& aFrameList) {
116 return InsertFrames(aParent, LastChild(), aFrameList);
117 }
120 /**
121 * Append aFrame to this list. If aParent is not null,
122 * reparents the newly added frame.
123 */
124 void AppendFrame(nsIFrame* aParent, nsIFrame* aFrame) {
125 nsFrameList temp(aFrame, aFrame);
126 AppendFrames(aParent, temp);
127 }
129 /**
130 * Take aFrame out of the frame list. This also disconnects aFrame
131 * from the sibling list. The frame must be non-null and present on
132 * this list.
133 */
134 void RemoveFrame(nsIFrame* aFrame);
136 /**
137 * Take the frames after aAfterFrame out of the frame list. If
138 * aAfterFrame is null, removes the entire list.
139 * @param aAfterFrame a frame in this list, or null
140 * @return the removed frames, if any
141 */
142 nsFrameList RemoveFramesAfter(nsIFrame* aAfterFrame);
144 /**
145 * Take the first frame (if any) out of the frame list.
146 * @return the first child, or nullptr if the list is empty
147 */
148 nsIFrame* RemoveFirstChild();
150 /**
151 * The following two functions are intended to be used in concert for
152 * removing a frame from its frame list when the set of possible frame
153 * lists is known in advance, but the exact frame list is unknown.
154 * aFrame must be non-null.
155 * Example use:
156 * bool removed = frameList1.StartRemoveFrame(aFrame) ||
157 * frameList2.ContinueRemoveFrame(aFrame) ||
158 * frameList3.ContinueRemoveFrame(aFrame);
159 * MOZ_ASSERT(removed);
160 *
161 * @note One of the frame lists MUST contain aFrame, if it's on some other
162 * frame list then the example above will likely lead to crashes.
163 * This function is O(1).
164 * @return true iff aFrame was removed from /some/ list, not necessarily
165 * this one. If it was removed from a different list then it is
166 * guaranteed that that list is still non-empty.
167 * (this method is implemented in nsIFrame.h to be able to inline)
168 */
169 inline bool StartRemoveFrame(nsIFrame* aFrame);
171 /**
172 * Precondition: StartRemoveFrame MUST be called before this.
173 * This function is O(1).
174 * @see StartRemoveFrame
175 * @return true iff aFrame was removed from this list
176 * (this method is implemented in nsIFrame.h to be able to inline)
177 */
178 inline bool ContinueRemoveFrame(nsIFrame* aFrame);
180 /**
181 * Take aFrame out of the frame list and then destroy it.
182 * The frame must be non-null and present on this list.
183 */
184 void DestroyFrame(nsIFrame* aFrame);
186 /**
187 * Insert aFrame right after aPrevSibling, or prepend it to this
188 * list if aPrevSibling is null. If aParent is not null, also
189 * reparents newly-added frame. Note that this method always
190 * sets the frame's nextSibling pointer.
191 */
192 void InsertFrame(nsIFrame* aParent, nsIFrame* aPrevSibling,
193 nsIFrame* aFrame) {
194 nsFrameList temp(aFrame, aFrame);
195 InsertFrames(aParent, aPrevSibling, temp);
196 }
199 /**
200 * Inserts aFrameList into this list after aPrevSibling (at the beginning if
201 * aPrevSibling is null). If aParent is not null, reparents the newly added
202 * frames. Clears out aFrameList and returns a list slice representing the
203 * newly-inserted frames.
204 */
205 Slice InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling,
206 nsFrameList& aFrameList);
208 class FrameLinkEnumerator;
210 /**
211 * Split this frame list such that all the frames before the link pointed to
212 * by aLink end up in the returned list, while the remaining frames stay in
213 * this list. After this call, aLink points to the beginning of this list.
214 */
215 nsFrameList ExtractHead(FrameLinkEnumerator& aLink);
217 /**
218 * Split this frame list such that all the frames coming after the link
219 * pointed to by aLink end up in the returned list, while the frames before
220 * that link stay in this list. After this call, aLink is at end.
221 */
222 nsFrameList ExtractTail(FrameLinkEnumerator& aLink);
224 nsIFrame* FirstChild() const {
225 return mFirstChild;
226 }
228 nsIFrame* LastChild() const {
229 return mLastChild;
230 }
232 nsIFrame* FrameAt(int32_t aIndex) const;
233 int32_t IndexOf(nsIFrame* aFrame) const;
235 bool IsEmpty() const {
236 return nullptr == mFirstChild;
237 }
239 bool NotEmpty() const {
240 return nullptr != mFirstChild;
241 }
243 bool ContainsFrame(const nsIFrame* aFrame) const;
245 int32_t GetLength() const;
247 /**
248 * If this frame list has only one frame, return that frame.
249 * Otherwise, return null.
250 */
251 nsIFrame* OnlyChild() const {
252 if (FirstChild() == LastChild()) {
253 return FirstChild();
254 }
255 return nullptr;
256 }
258 /**
259 * Call SetParent(aParent) for each frame in this list.
260 * @param aParent the new parent frame, must be non-null
261 */
262 void ApplySetParent(nsIFrame* aParent) const;
264 /**
265 * If this frame list is non-empty then append it to aLists as the
266 * aListID child list.
267 * (this method is implemented in FrameChildList.h for dependency reasons)
268 */
269 inline void AppendIfNonempty(nsTArray<mozilla::layout::FrameChildList>* aLists,
270 mozilla::layout::FrameChildListID aListID) const;
272 /**
273 * Return the frame before this frame in visual order (after Bidi reordering).
274 * If aFrame is null, return the last frame in visual order.
275 */
276 nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const;
278 /**
279 * Return the frame after this frame in visual order (after Bidi reordering).
280 * If aFrame is null, return the first frame in visual order.
281 */
282 nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
284 #ifdef DEBUG_FRAME_DUMP
285 void List(FILE* out) const;
286 #endif
288 static inline const nsFrameList& EmptyList();
290 class Enumerator;
292 /**
293 * A class representing a slice of a frame list.
294 */
295 class Slice {
296 friend class Enumerator;
298 public:
299 // Implicit on purpose, so that we can easily create enumerators from
300 // nsFrameList via this impicit constructor.
301 Slice(const nsFrameList& aList) :
302 #ifdef DEBUG
303 mList(aList),
304 #endif
305 mStart(aList.FirstChild()),
306 mEnd(nullptr)
307 {}
309 Slice(const nsFrameList& aList, nsIFrame* aStart, nsIFrame* aEnd) :
310 #ifdef DEBUG
311 mList(aList),
312 #endif
313 mStart(aStart),
314 mEnd(aEnd)
315 {}
317 Slice(const Slice& aOther) :
318 #ifdef DEBUG
319 mList(aOther.mList),
320 #endif
321 mStart(aOther.mStart),
322 mEnd(aOther.mEnd)
323 {}
325 private:
326 #ifdef DEBUG
327 const nsFrameList& mList;
328 #endif
329 nsIFrame* const mStart; // our starting frame
330 const nsIFrame* const mEnd; // The first frame that is NOT in the slice.
331 // May be null.
332 };
334 class Enumerator {
335 public:
336 Enumerator(const Slice& aSlice) :
337 #ifdef DEBUG
338 mSlice(aSlice),
339 #endif
340 mFrame(aSlice.mStart),
341 mEnd(aSlice.mEnd)
342 {}
344 Enumerator(const Enumerator& aOther) :
345 #ifdef DEBUG
346 mSlice(aOther.mSlice),
347 #endif
348 mFrame(aOther.mFrame),
349 mEnd(aOther.mEnd)
350 {}
352 bool AtEnd() const {
353 // Can't just check mEnd, because some table code goes and destroys the
354 // tail of the frame list (including mEnd!) while iterating over the
355 // frame list.
356 return !mFrame || mFrame == mEnd;
357 }
359 /* Next() needs to know about nsIFrame, and nsIFrame will need to
360 know about nsFrameList methods, so in order to inline this put
361 the implementation in nsIFrame.h */
362 inline void Next();
364 /**
365 * Get the current frame we're pointing to. Do not call this on an
366 * iterator that is at end!
367 */
368 nsIFrame* get() const {
369 NS_PRECONDITION(!AtEnd(), "Enumerator is at end");
370 return mFrame;
371 }
373 /**
374 * Get an enumerator that is just like this one, but not limited in terms of
375 * the part of the list it will traverse.
376 */
377 Enumerator GetUnlimitedEnumerator() const {
378 return Enumerator(*this, nullptr);
379 }
381 #ifdef DEBUG
382 const nsFrameList& List() const { return mSlice.mList; }
383 #endif
385 protected:
386 Enumerator(const Enumerator& aOther, const nsIFrame* const aNewEnd):
387 #ifdef DEBUG
388 mSlice(aOther.mSlice),
389 #endif
390 mFrame(aOther.mFrame),
391 mEnd(aNewEnd)
392 {}
394 #ifdef DEBUG
395 /* Has to be an object, not a reference, since the slice could
396 well be a temporary constructed from an nsFrameList */
397 const Slice mSlice;
398 #endif
399 nsIFrame* mFrame; // our current frame.
400 const nsIFrame* const mEnd; // The first frame we should NOT enumerate.
401 // May be null.
402 };
404 /**
405 * A class that can be used to enumerate links between frames. When created
406 * from an nsFrameList, it points to the "link" immediately before the first
407 * frame. It can then be advanced until it points to the "link" immediately
408 * after the last frame. At any position, PrevFrame() and NextFrame() are
409 * the frames before and after the given link. This means PrevFrame() is
410 * null when the enumerator is at the beginning of the list and NextFrame()
411 * is null when it's AtEnd().
412 */
413 class FrameLinkEnumerator : private Enumerator {
414 public:
415 friend class nsFrameList;
417 FrameLinkEnumerator(const nsFrameList& aList) :
418 Enumerator(aList),
419 mPrev(nullptr)
420 {}
422 FrameLinkEnumerator(const FrameLinkEnumerator& aOther) :
423 Enumerator(aOther),
424 mPrev(aOther.mPrev)
425 {}
427 /* This constructor needs to know about nsIFrame, and nsIFrame will need to
428 know about nsFrameList methods, so in order to inline this put
429 the implementation in nsIFrame.h */
430 inline FrameLinkEnumerator(const nsFrameList& aList, nsIFrame* aPrevFrame);
432 void operator=(const FrameLinkEnumerator& aOther) {
433 NS_PRECONDITION(&List() == &aOther.List(), "Different lists?");
434 mFrame = aOther.mFrame;
435 mPrev = aOther.mPrev;
436 }
438 inline void Next();
440 bool AtEnd() const { return Enumerator::AtEnd(); }
442 nsIFrame* PrevFrame() const { return mPrev; }
443 nsIFrame* NextFrame() const { return mFrame; }
445 protected:
446 nsIFrame* mPrev;
447 };
449 private:
450 void operator delete(void*) MOZ_DELETE;
452 #ifdef DEBUG_FRAME_LIST
453 void VerifyList() const;
454 #else
455 void VerifyList() const {}
456 #endif
458 protected:
459 /**
460 * Disconnect aFrame from its siblings. This must only be called if aFrame
461 * is NOT the first or last sibling, because otherwise its nsFrameList will
462 * have a stale mFirst/LastChild pointer. This precondition is asserted.
463 * This function is O(1).
464 */
465 static void UnhookFrameFromSiblings(nsIFrame* aFrame);
467 nsIFrame* mFirstChild;
468 nsIFrame* mLastChild;
469 };
471 namespace mozilla {
472 namespace layout {
474 /**
475 * Simple "auto_ptr" for nsFrameLists allocated from the shell arena.
476 * The frame list given to the constructor will be deallocated (if non-null)
477 * in the destructor. The frame list must then be empty.
478 */
479 class AutoFrameListPtr {
480 public:
481 AutoFrameListPtr(nsPresContext* aPresContext, nsFrameList* aFrameList)
482 : mPresContext(aPresContext), mFrameList(aFrameList) {}
483 ~AutoFrameListPtr();
484 operator nsFrameList*() const { return mFrameList; }
485 nsFrameList* operator->() const { return mFrameList; }
486 private:
487 nsPresContext* mPresContext;
488 nsFrameList* mFrameList;
489 };
491 namespace detail {
492 union AlignedFrameListBytes {
493 void* ptr;
494 char bytes[sizeof(nsFrameList)];
495 };
496 extern const AlignedFrameListBytes gEmptyFrameListBytes;
497 }
498 }
499 }
501 /* static */ inline const nsFrameList&
502 nsFrameList::EmptyList()
503 {
504 return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes);
505 }
507 #endif /* nsFrameList_h___ */