|
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/. */ |
|
5 |
|
6 #ifndef nsFrameList_h___ |
|
7 #define nsFrameList_h___ |
|
8 |
|
9 #include <stdio.h> /* for FILE* */ |
|
10 #include "nsDebug.h" |
|
11 #include "nsTArrayForwardDeclare.h" |
|
12 |
|
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 |
|
18 |
|
19 class nsIFrame; |
|
20 class nsIPresShell; |
|
21 class nsPresContext; |
|
22 |
|
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 } |
|
48 |
|
49 // Uncomment this to enable expensive frame-list integrity checking |
|
50 // #define DEBUG_FRAME_LIST |
|
51 |
|
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 } |
|
61 |
|
62 nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) : |
|
63 mFirstChild(aFirstFrame), mLastChild(aLastFrame) |
|
64 { |
|
65 VerifyList(); |
|
66 } |
|
67 |
|
68 nsFrameList(const nsFrameList& aOther) : |
|
69 mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild) |
|
70 { |
|
71 } |
|
72 |
|
73 /** |
|
74 * Allocate a nsFrameList from the shell arena. |
|
75 */ |
|
76 void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW; |
|
77 |
|
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); |
|
83 |
|
84 /** |
|
85 * For each frame in this list: remove it from the list then call |
|
86 * Destroy() on it. |
|
87 */ |
|
88 void DestroyFrames(); |
|
89 |
|
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); |
|
95 |
|
96 void Clear() { mFirstChild = mLastChild = nullptr; } |
|
97 |
|
98 void SetFrames(nsIFrame* aFrameList); |
|
99 |
|
100 void SetFrames(nsFrameList& aFrameList) { |
|
101 NS_PRECONDITION(!mFirstChild, "Losing frames"); |
|
102 |
|
103 mFirstChild = aFrameList.FirstChild(); |
|
104 mLastChild = aFrameList.LastChild(); |
|
105 aFrameList.Clear(); |
|
106 } |
|
107 |
|
108 class Slice; |
|
109 |
|
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 } |
|
118 |
|
119 |
|
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 } |
|
128 |
|
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); |
|
135 |
|
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); |
|
143 |
|
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(); |
|
149 |
|
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); |
|
170 |
|
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); |
|
179 |
|
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); |
|
185 |
|
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 } |
|
197 |
|
198 |
|
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); |
|
207 |
|
208 class FrameLinkEnumerator; |
|
209 |
|
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); |
|
216 |
|
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); |
|
223 |
|
224 nsIFrame* FirstChild() const { |
|
225 return mFirstChild; |
|
226 } |
|
227 |
|
228 nsIFrame* LastChild() const { |
|
229 return mLastChild; |
|
230 } |
|
231 |
|
232 nsIFrame* FrameAt(int32_t aIndex) const; |
|
233 int32_t IndexOf(nsIFrame* aFrame) const; |
|
234 |
|
235 bool IsEmpty() const { |
|
236 return nullptr == mFirstChild; |
|
237 } |
|
238 |
|
239 bool NotEmpty() const { |
|
240 return nullptr != mFirstChild; |
|
241 } |
|
242 |
|
243 bool ContainsFrame(const nsIFrame* aFrame) const; |
|
244 |
|
245 int32_t GetLength() const; |
|
246 |
|
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 } |
|
257 |
|
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; |
|
263 |
|
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; |
|
271 |
|
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; |
|
277 |
|
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; |
|
283 |
|
284 #ifdef DEBUG_FRAME_DUMP |
|
285 void List(FILE* out) const; |
|
286 #endif |
|
287 |
|
288 static inline const nsFrameList& EmptyList(); |
|
289 |
|
290 class Enumerator; |
|
291 |
|
292 /** |
|
293 * A class representing a slice of a frame list. |
|
294 */ |
|
295 class Slice { |
|
296 friend class Enumerator; |
|
297 |
|
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 {} |
|
308 |
|
309 Slice(const nsFrameList& aList, nsIFrame* aStart, nsIFrame* aEnd) : |
|
310 #ifdef DEBUG |
|
311 mList(aList), |
|
312 #endif |
|
313 mStart(aStart), |
|
314 mEnd(aEnd) |
|
315 {} |
|
316 |
|
317 Slice(const Slice& aOther) : |
|
318 #ifdef DEBUG |
|
319 mList(aOther.mList), |
|
320 #endif |
|
321 mStart(aOther.mStart), |
|
322 mEnd(aOther.mEnd) |
|
323 {} |
|
324 |
|
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 }; |
|
333 |
|
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 {} |
|
343 |
|
344 Enumerator(const Enumerator& aOther) : |
|
345 #ifdef DEBUG |
|
346 mSlice(aOther.mSlice), |
|
347 #endif |
|
348 mFrame(aOther.mFrame), |
|
349 mEnd(aOther.mEnd) |
|
350 {} |
|
351 |
|
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 } |
|
358 |
|
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(); |
|
363 |
|
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 } |
|
372 |
|
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 } |
|
380 |
|
381 #ifdef DEBUG |
|
382 const nsFrameList& List() const { return mSlice.mList; } |
|
383 #endif |
|
384 |
|
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 {} |
|
393 |
|
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 }; |
|
403 |
|
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; |
|
416 |
|
417 FrameLinkEnumerator(const nsFrameList& aList) : |
|
418 Enumerator(aList), |
|
419 mPrev(nullptr) |
|
420 {} |
|
421 |
|
422 FrameLinkEnumerator(const FrameLinkEnumerator& aOther) : |
|
423 Enumerator(aOther), |
|
424 mPrev(aOther.mPrev) |
|
425 {} |
|
426 |
|
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); |
|
431 |
|
432 void operator=(const FrameLinkEnumerator& aOther) { |
|
433 NS_PRECONDITION(&List() == &aOther.List(), "Different lists?"); |
|
434 mFrame = aOther.mFrame; |
|
435 mPrev = aOther.mPrev; |
|
436 } |
|
437 |
|
438 inline void Next(); |
|
439 |
|
440 bool AtEnd() const { return Enumerator::AtEnd(); } |
|
441 |
|
442 nsIFrame* PrevFrame() const { return mPrev; } |
|
443 nsIFrame* NextFrame() const { return mFrame; } |
|
444 |
|
445 protected: |
|
446 nsIFrame* mPrev; |
|
447 }; |
|
448 |
|
449 private: |
|
450 void operator delete(void*) MOZ_DELETE; |
|
451 |
|
452 #ifdef DEBUG_FRAME_LIST |
|
453 void VerifyList() const; |
|
454 #else |
|
455 void VerifyList() const {} |
|
456 #endif |
|
457 |
|
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); |
|
466 |
|
467 nsIFrame* mFirstChild; |
|
468 nsIFrame* mLastChild; |
|
469 }; |
|
470 |
|
471 namespace mozilla { |
|
472 namespace layout { |
|
473 |
|
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 }; |
|
490 |
|
491 namespace detail { |
|
492 union AlignedFrameListBytes { |
|
493 void* ptr; |
|
494 char bytes[sizeof(nsFrameList)]; |
|
495 }; |
|
496 extern const AlignedFrameListBytes gEmptyFrameListBytes; |
|
497 } |
|
498 } |
|
499 } |
|
500 |
|
501 /* static */ inline const nsFrameList& |
|
502 nsFrameList::EmptyList() |
|
503 { |
|
504 return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes); |
|
505 } |
|
506 |
|
507 #endif /* nsFrameList_h___ */ |