1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsFrameList.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,507 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef nsFrameList_h___ 1.10 +#define nsFrameList_h___ 1.11 + 1.12 +#include <stdio.h> /* for FILE* */ 1.13 +#include "nsDebug.h" 1.14 +#include "nsTArrayForwardDeclare.h" 1.15 + 1.16 +#if defined(DEBUG) || defined(MOZ_DUMP_PAINTING) 1.17 +// DEBUG_FRAME_DUMP enables nsIFrame::List and related methods. 1.18 +// You can also define this in a non-DEBUG build if you need frame dumps. 1.19 +#define DEBUG_FRAME_DUMP 1 1.20 +#endif 1.21 + 1.22 +class nsIFrame; 1.23 +class nsIPresShell; 1.24 +class nsPresContext; 1.25 + 1.26 +namespace mozilla { 1.27 +namespace layout { 1.28 + class FrameChildList; 1.29 + enum FrameChildListID { 1.30 + // The individual concrete child lists. 1.31 + kPrincipalList = 0x1, 1.32 + kPopupList = 0x2, 1.33 + kCaptionList = 0x4, 1.34 + kColGroupList = 0x8, 1.35 + kSelectPopupList = 0x10, 1.36 + kAbsoluteList = 0x20, 1.37 + kFixedList = 0x40, 1.38 + kOverflowList = 0x80, 1.39 + kOverflowContainersList = 0x100, 1.40 + kExcessOverflowContainersList = 0x200, 1.41 + kOverflowOutOfFlowList = 0x400, 1.42 + kFloatList = 0x800, 1.43 + kBulletList = 0x1000, 1.44 + kPushedFloatsList = 0x2000, 1.45 + // A special alias for kPrincipalList that suppress the reflow request that 1.46 + // is normally done when manipulating child lists. 1.47 + kNoReflowPrincipalList = 0x4000 1.48 + }; 1.49 +} 1.50 +} 1.51 + 1.52 +// Uncomment this to enable expensive frame-list integrity checking 1.53 +// #define DEBUG_FRAME_LIST 1.54 + 1.55 +/** 1.56 + * A class for managing a list of frames. 1.57 + */ 1.58 +class nsFrameList { 1.59 +public: 1.60 + nsFrameList() : 1.61 + mFirstChild(nullptr), mLastChild(nullptr) 1.62 + { 1.63 + } 1.64 + 1.65 + nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) : 1.66 + mFirstChild(aFirstFrame), mLastChild(aLastFrame) 1.67 + { 1.68 + VerifyList(); 1.69 + } 1.70 + 1.71 + nsFrameList(const nsFrameList& aOther) : 1.72 + mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild) 1.73 + { 1.74 + } 1.75 + 1.76 + /** 1.77 + * Allocate a nsFrameList from the shell arena. 1.78 + */ 1.79 + void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW; 1.80 + 1.81 + /** 1.82 + * Deallocate this list that was allocated from the shell arena. 1.83 + * The list is required to be empty. 1.84 + */ 1.85 + void Delete(nsIPresShell* aPresShell); 1.86 + 1.87 + /** 1.88 + * For each frame in this list: remove it from the list then call 1.89 + * Destroy() on it. 1.90 + */ 1.91 + void DestroyFrames(); 1.92 + 1.93 + /** 1.94 + * For each frame in this list: remove it from the list then call 1.95 + * DestroyFrom(aDestructRoot) on it. 1.96 + */ 1.97 + void DestroyFramesFrom(nsIFrame* aDestructRoot); 1.98 + 1.99 + void Clear() { mFirstChild = mLastChild = nullptr; } 1.100 + 1.101 + void SetFrames(nsIFrame* aFrameList); 1.102 + 1.103 + void SetFrames(nsFrameList& aFrameList) { 1.104 + NS_PRECONDITION(!mFirstChild, "Losing frames"); 1.105 + 1.106 + mFirstChild = aFrameList.FirstChild(); 1.107 + mLastChild = aFrameList.LastChild(); 1.108 + aFrameList.Clear(); 1.109 + } 1.110 + 1.111 + class Slice; 1.112 + 1.113 + /** 1.114 + * Append aFrameList to this list. If aParent is not null, 1.115 + * reparents the newly added frames. Clears out aFrameList and 1.116 + * returns a list slice represening the newly-appended frames. 1.117 + */ 1.118 + Slice AppendFrames(nsIFrame* aParent, nsFrameList& aFrameList) { 1.119 + return InsertFrames(aParent, LastChild(), aFrameList); 1.120 + } 1.121 + 1.122 + 1.123 + /** 1.124 + * Append aFrame to this list. If aParent is not null, 1.125 + * reparents the newly added frame. 1.126 + */ 1.127 + void AppendFrame(nsIFrame* aParent, nsIFrame* aFrame) { 1.128 + nsFrameList temp(aFrame, aFrame); 1.129 + AppendFrames(aParent, temp); 1.130 + } 1.131 + 1.132 + /** 1.133 + * Take aFrame out of the frame list. This also disconnects aFrame 1.134 + * from the sibling list. The frame must be non-null and present on 1.135 + * this list. 1.136 + */ 1.137 + void RemoveFrame(nsIFrame* aFrame); 1.138 + 1.139 + /** 1.140 + * Take the frames after aAfterFrame out of the frame list. If 1.141 + * aAfterFrame is null, removes the entire list. 1.142 + * @param aAfterFrame a frame in this list, or null 1.143 + * @return the removed frames, if any 1.144 + */ 1.145 + nsFrameList RemoveFramesAfter(nsIFrame* aAfterFrame); 1.146 + 1.147 + /** 1.148 + * Take the first frame (if any) out of the frame list. 1.149 + * @return the first child, or nullptr if the list is empty 1.150 + */ 1.151 + nsIFrame* RemoveFirstChild(); 1.152 + 1.153 + /** 1.154 + * The following two functions are intended to be used in concert for 1.155 + * removing a frame from its frame list when the set of possible frame 1.156 + * lists is known in advance, but the exact frame list is unknown. 1.157 + * aFrame must be non-null. 1.158 + * Example use: 1.159 + * bool removed = frameList1.StartRemoveFrame(aFrame) || 1.160 + * frameList2.ContinueRemoveFrame(aFrame) || 1.161 + * frameList3.ContinueRemoveFrame(aFrame); 1.162 + * MOZ_ASSERT(removed); 1.163 + * 1.164 + * @note One of the frame lists MUST contain aFrame, if it's on some other 1.165 + * frame list then the example above will likely lead to crashes. 1.166 + * This function is O(1). 1.167 + * @return true iff aFrame was removed from /some/ list, not necessarily 1.168 + * this one. If it was removed from a different list then it is 1.169 + * guaranteed that that list is still non-empty. 1.170 + * (this method is implemented in nsIFrame.h to be able to inline) 1.171 + */ 1.172 + inline bool StartRemoveFrame(nsIFrame* aFrame); 1.173 + 1.174 + /** 1.175 + * Precondition: StartRemoveFrame MUST be called before this. 1.176 + * This function is O(1). 1.177 + * @see StartRemoveFrame 1.178 + * @return true iff aFrame was removed from this list 1.179 + * (this method is implemented in nsIFrame.h to be able to inline) 1.180 + */ 1.181 + inline bool ContinueRemoveFrame(nsIFrame* aFrame); 1.182 + 1.183 + /** 1.184 + * Take aFrame out of the frame list and then destroy it. 1.185 + * The frame must be non-null and present on this list. 1.186 + */ 1.187 + void DestroyFrame(nsIFrame* aFrame); 1.188 + 1.189 + /** 1.190 + * Insert aFrame right after aPrevSibling, or prepend it to this 1.191 + * list if aPrevSibling is null. If aParent is not null, also 1.192 + * reparents newly-added frame. Note that this method always 1.193 + * sets the frame's nextSibling pointer. 1.194 + */ 1.195 + void InsertFrame(nsIFrame* aParent, nsIFrame* aPrevSibling, 1.196 + nsIFrame* aFrame) { 1.197 + nsFrameList temp(aFrame, aFrame); 1.198 + InsertFrames(aParent, aPrevSibling, temp); 1.199 + } 1.200 + 1.201 + 1.202 + /** 1.203 + * Inserts aFrameList into this list after aPrevSibling (at the beginning if 1.204 + * aPrevSibling is null). If aParent is not null, reparents the newly added 1.205 + * frames. Clears out aFrameList and returns a list slice representing the 1.206 + * newly-inserted frames. 1.207 + */ 1.208 + Slice InsertFrames(nsIFrame* aParent, nsIFrame* aPrevSibling, 1.209 + nsFrameList& aFrameList); 1.210 + 1.211 + class FrameLinkEnumerator; 1.212 + 1.213 + /** 1.214 + * Split this frame list such that all the frames before the link pointed to 1.215 + * by aLink end up in the returned list, while the remaining frames stay in 1.216 + * this list. After this call, aLink points to the beginning of this list. 1.217 + */ 1.218 + nsFrameList ExtractHead(FrameLinkEnumerator& aLink); 1.219 + 1.220 + /** 1.221 + * Split this frame list such that all the frames coming after the link 1.222 + * pointed to by aLink end up in the returned list, while the frames before 1.223 + * that link stay in this list. After this call, aLink is at end. 1.224 + */ 1.225 + nsFrameList ExtractTail(FrameLinkEnumerator& aLink); 1.226 + 1.227 + nsIFrame* FirstChild() const { 1.228 + return mFirstChild; 1.229 + } 1.230 + 1.231 + nsIFrame* LastChild() const { 1.232 + return mLastChild; 1.233 + } 1.234 + 1.235 + nsIFrame* FrameAt(int32_t aIndex) const; 1.236 + int32_t IndexOf(nsIFrame* aFrame) const; 1.237 + 1.238 + bool IsEmpty() const { 1.239 + return nullptr == mFirstChild; 1.240 + } 1.241 + 1.242 + bool NotEmpty() const { 1.243 + return nullptr != mFirstChild; 1.244 + } 1.245 + 1.246 + bool ContainsFrame(const nsIFrame* aFrame) const; 1.247 + 1.248 + int32_t GetLength() const; 1.249 + 1.250 + /** 1.251 + * If this frame list has only one frame, return that frame. 1.252 + * Otherwise, return null. 1.253 + */ 1.254 + nsIFrame* OnlyChild() const { 1.255 + if (FirstChild() == LastChild()) { 1.256 + return FirstChild(); 1.257 + } 1.258 + return nullptr; 1.259 + } 1.260 + 1.261 + /** 1.262 + * Call SetParent(aParent) for each frame in this list. 1.263 + * @param aParent the new parent frame, must be non-null 1.264 + */ 1.265 + void ApplySetParent(nsIFrame* aParent) const; 1.266 + 1.267 + /** 1.268 + * If this frame list is non-empty then append it to aLists as the 1.269 + * aListID child list. 1.270 + * (this method is implemented in FrameChildList.h for dependency reasons) 1.271 + */ 1.272 + inline void AppendIfNonempty(nsTArray<mozilla::layout::FrameChildList>* aLists, 1.273 + mozilla::layout::FrameChildListID aListID) const; 1.274 + 1.275 + /** 1.276 + * Return the frame before this frame in visual order (after Bidi reordering). 1.277 + * If aFrame is null, return the last frame in visual order. 1.278 + */ 1.279 + nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const; 1.280 + 1.281 + /** 1.282 + * Return the frame after this frame in visual order (after Bidi reordering). 1.283 + * If aFrame is null, return the first frame in visual order. 1.284 + */ 1.285 + nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const; 1.286 + 1.287 +#ifdef DEBUG_FRAME_DUMP 1.288 + void List(FILE* out) const; 1.289 +#endif 1.290 + 1.291 + static inline const nsFrameList& EmptyList(); 1.292 + 1.293 + class Enumerator; 1.294 + 1.295 + /** 1.296 + * A class representing a slice of a frame list. 1.297 + */ 1.298 + class Slice { 1.299 + friend class Enumerator; 1.300 + 1.301 + public: 1.302 + // Implicit on purpose, so that we can easily create enumerators from 1.303 + // nsFrameList via this impicit constructor. 1.304 + Slice(const nsFrameList& aList) : 1.305 +#ifdef DEBUG 1.306 + mList(aList), 1.307 +#endif 1.308 + mStart(aList.FirstChild()), 1.309 + mEnd(nullptr) 1.310 + {} 1.311 + 1.312 + Slice(const nsFrameList& aList, nsIFrame* aStart, nsIFrame* aEnd) : 1.313 +#ifdef DEBUG 1.314 + mList(aList), 1.315 +#endif 1.316 + mStart(aStart), 1.317 + mEnd(aEnd) 1.318 + {} 1.319 + 1.320 + Slice(const Slice& aOther) : 1.321 +#ifdef DEBUG 1.322 + mList(aOther.mList), 1.323 +#endif 1.324 + mStart(aOther.mStart), 1.325 + mEnd(aOther.mEnd) 1.326 + {} 1.327 + 1.328 + private: 1.329 +#ifdef DEBUG 1.330 + const nsFrameList& mList; 1.331 +#endif 1.332 + nsIFrame* const mStart; // our starting frame 1.333 + const nsIFrame* const mEnd; // The first frame that is NOT in the slice. 1.334 + // May be null. 1.335 + }; 1.336 + 1.337 + class Enumerator { 1.338 + public: 1.339 + Enumerator(const Slice& aSlice) : 1.340 +#ifdef DEBUG 1.341 + mSlice(aSlice), 1.342 +#endif 1.343 + mFrame(aSlice.mStart), 1.344 + mEnd(aSlice.mEnd) 1.345 + {} 1.346 + 1.347 + Enumerator(const Enumerator& aOther) : 1.348 +#ifdef DEBUG 1.349 + mSlice(aOther.mSlice), 1.350 +#endif 1.351 + mFrame(aOther.mFrame), 1.352 + mEnd(aOther.mEnd) 1.353 + {} 1.354 + 1.355 + bool AtEnd() const { 1.356 + // Can't just check mEnd, because some table code goes and destroys the 1.357 + // tail of the frame list (including mEnd!) while iterating over the 1.358 + // frame list. 1.359 + return !mFrame || mFrame == mEnd; 1.360 + } 1.361 + 1.362 + /* Next() needs to know about nsIFrame, and nsIFrame will need to 1.363 + know about nsFrameList methods, so in order to inline this put 1.364 + the implementation in nsIFrame.h */ 1.365 + inline void Next(); 1.366 + 1.367 + /** 1.368 + * Get the current frame we're pointing to. Do not call this on an 1.369 + * iterator that is at end! 1.370 + */ 1.371 + nsIFrame* get() const { 1.372 + NS_PRECONDITION(!AtEnd(), "Enumerator is at end"); 1.373 + return mFrame; 1.374 + } 1.375 + 1.376 + /** 1.377 + * Get an enumerator that is just like this one, but not limited in terms of 1.378 + * the part of the list it will traverse. 1.379 + */ 1.380 + Enumerator GetUnlimitedEnumerator() const { 1.381 + return Enumerator(*this, nullptr); 1.382 + } 1.383 + 1.384 +#ifdef DEBUG 1.385 + const nsFrameList& List() const { return mSlice.mList; } 1.386 +#endif 1.387 + 1.388 + protected: 1.389 + Enumerator(const Enumerator& aOther, const nsIFrame* const aNewEnd): 1.390 +#ifdef DEBUG 1.391 + mSlice(aOther.mSlice), 1.392 +#endif 1.393 + mFrame(aOther.mFrame), 1.394 + mEnd(aNewEnd) 1.395 + {} 1.396 + 1.397 +#ifdef DEBUG 1.398 + /* Has to be an object, not a reference, since the slice could 1.399 + well be a temporary constructed from an nsFrameList */ 1.400 + const Slice mSlice; 1.401 +#endif 1.402 + nsIFrame* mFrame; // our current frame. 1.403 + const nsIFrame* const mEnd; // The first frame we should NOT enumerate. 1.404 + // May be null. 1.405 + }; 1.406 + 1.407 + /** 1.408 + * A class that can be used to enumerate links between frames. When created 1.409 + * from an nsFrameList, it points to the "link" immediately before the first 1.410 + * frame. It can then be advanced until it points to the "link" immediately 1.411 + * after the last frame. At any position, PrevFrame() and NextFrame() are 1.412 + * the frames before and after the given link. This means PrevFrame() is 1.413 + * null when the enumerator is at the beginning of the list and NextFrame() 1.414 + * is null when it's AtEnd(). 1.415 + */ 1.416 + class FrameLinkEnumerator : private Enumerator { 1.417 + public: 1.418 + friend class nsFrameList; 1.419 + 1.420 + FrameLinkEnumerator(const nsFrameList& aList) : 1.421 + Enumerator(aList), 1.422 + mPrev(nullptr) 1.423 + {} 1.424 + 1.425 + FrameLinkEnumerator(const FrameLinkEnumerator& aOther) : 1.426 + Enumerator(aOther), 1.427 + mPrev(aOther.mPrev) 1.428 + {} 1.429 + 1.430 + /* This constructor needs to know about nsIFrame, and nsIFrame will need to 1.431 + know about nsFrameList methods, so in order to inline this put 1.432 + the implementation in nsIFrame.h */ 1.433 + inline FrameLinkEnumerator(const nsFrameList& aList, nsIFrame* aPrevFrame); 1.434 + 1.435 + void operator=(const FrameLinkEnumerator& aOther) { 1.436 + NS_PRECONDITION(&List() == &aOther.List(), "Different lists?"); 1.437 + mFrame = aOther.mFrame; 1.438 + mPrev = aOther.mPrev; 1.439 + } 1.440 + 1.441 + inline void Next(); 1.442 + 1.443 + bool AtEnd() const { return Enumerator::AtEnd(); } 1.444 + 1.445 + nsIFrame* PrevFrame() const { return mPrev; } 1.446 + nsIFrame* NextFrame() const { return mFrame; } 1.447 + 1.448 + protected: 1.449 + nsIFrame* mPrev; 1.450 + }; 1.451 + 1.452 +private: 1.453 + void operator delete(void*) MOZ_DELETE; 1.454 + 1.455 +#ifdef DEBUG_FRAME_LIST 1.456 + void VerifyList() const; 1.457 +#else 1.458 + void VerifyList() const {} 1.459 +#endif 1.460 + 1.461 +protected: 1.462 + /** 1.463 + * Disconnect aFrame from its siblings. This must only be called if aFrame 1.464 + * is NOT the first or last sibling, because otherwise its nsFrameList will 1.465 + * have a stale mFirst/LastChild pointer. This precondition is asserted. 1.466 + * This function is O(1). 1.467 + */ 1.468 + static void UnhookFrameFromSiblings(nsIFrame* aFrame); 1.469 + 1.470 + nsIFrame* mFirstChild; 1.471 + nsIFrame* mLastChild; 1.472 +}; 1.473 + 1.474 +namespace mozilla { 1.475 +namespace layout { 1.476 + 1.477 +/** 1.478 + * Simple "auto_ptr" for nsFrameLists allocated from the shell arena. 1.479 + * The frame list given to the constructor will be deallocated (if non-null) 1.480 + * in the destructor. The frame list must then be empty. 1.481 + */ 1.482 +class AutoFrameListPtr { 1.483 +public: 1.484 + AutoFrameListPtr(nsPresContext* aPresContext, nsFrameList* aFrameList) 1.485 + : mPresContext(aPresContext), mFrameList(aFrameList) {} 1.486 + ~AutoFrameListPtr(); 1.487 + operator nsFrameList*() const { return mFrameList; } 1.488 + nsFrameList* operator->() const { return mFrameList; } 1.489 +private: 1.490 + nsPresContext* mPresContext; 1.491 + nsFrameList* mFrameList; 1.492 +}; 1.493 + 1.494 +namespace detail { 1.495 +union AlignedFrameListBytes { 1.496 + void* ptr; 1.497 + char bytes[sizeof(nsFrameList)]; 1.498 +}; 1.499 +extern const AlignedFrameListBytes gEmptyFrameListBytes; 1.500 +} 1.501 +} 1.502 +} 1.503 + 1.504 +/* static */ inline const nsFrameList& 1.505 +nsFrameList::EmptyList() 1.506 +{ 1.507 + return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes); 1.508 +} 1.509 + 1.510 +#endif /* nsFrameList_h___ */