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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef ChildIterator_h 8 #define ChildIterator_h 9 10 #include "nsIContent.h" 11 #include "nsIContentInlines.h" 12 #include <stdint.h> 13 14 /** 15 * Iterates over the children on a node. If a child is an insertion point, 16 * iterates over the children inserted there instead, or the default content 17 * if no children are inserted there. 18 * 19 * The FlattenedChildIterator expands any anonymous content bound from an XBL 20 * binding's <xbl:content> element. 21 */ 22 23 class nsIContent; 24 25 namespace mozilla { 26 namespace dom { 27 28 // This class iterates normal DOM child nodes of a given DOM node with 29 // <xbl:children> nodes replaced by the elements that have been filtered into 30 // that insertion point. Any bindings on the given element are ignored for 31 // purposes of determining which insertion point children are filtered into. The 32 // iterator can be initialized to start at the end by providing false for 33 // aStartAtBeginning in order to start iterating in reverse from the last child. 34 class ExplicitChildIterator { 35 public: 36 explicit ExplicitChildIterator(const nsIContent* aParent, 37 bool aStartAtBeginning = true); 38 39 nsIContent* GetNextChild(); 40 41 // Looks for aChildToFind respecting insertion points until aChildToFind is 42 // found. This version can take shortcuts that the two-argument version 43 // can't, so can be faster (and in fact can be O(1) instead of O(N) in many 44 // cases). 45 bool Seek(const nsIContent* aChildToFind); 46 47 // Looks for aChildToFind respecting insertion points until aChildToFind is 48 // found. or aBound is found. If aBound is nullptr then the seek is unbounded. 49 // Returns whether aChildToFind was found as an explicit child prior to 50 // encountering aBound. Seek(const nsIContent * aChildToFind,nsIContent * aBound)51 bool Seek(const nsIContent* aChildToFind, nsIContent* aBound) { 52 // It would be nice to assert that we find aChildToFind, but bz thinks that 53 // we might not find aChildToFind when called from ContentInserted 54 // if first-letter frames are about. 55 56 // We can't easily take shortcuts here because we'd have to have a way to 57 // compare aChildToFind to aBound. 58 nsIContent* child; 59 do { 60 child = GetNextChild(); 61 } while (child && child != aChildToFind && child != aBound); 62 63 return child == aChildToFind; 64 } 65 66 // Returns the current target of this iterator (which might be an explicit 67 // child of the node, fallback content of an insertion point or 68 // a node distributed to an insertion point. 69 nsIContent* Get() const; 70 71 // The inverse of GetNextChild. Properly steps in and out of insertion 72 // points. 73 nsIContent* GetPreviousChild(); 74 75 protected: 76 // The parent of the children being iterated. For the FlattenedChildIterator, 77 // if there is a binding attached to the original parent, mParent points to 78 // the <xbl:content> element for the binding. 79 const nsIContent* mParent; 80 81 // If parent is a slot element, this points to the parent as HTMLSlotElement, 82 // otherwise, it's null. 83 const HTMLSlotElement* mParentAsSlot; 84 85 // The current child. When we encounter an insertion point, 86 // mChild remains as the insertion point whose content we're iterating (and 87 // our state is controled by mDefaultChild or mIndexInInserted depending on 88 // whether the insertion point expands to its default content or not). 89 nsIContent* mChild; 90 91 // If non-null, this points to the current default content for the current 92 // insertion point that we're iterating (i.e. mChild, which must be an 93 // nsXBLChildrenElement or HTMLContentElement). Once this transitions back 94 // to null, we continue iterating at mChild's next sibling. 95 nsIContent* mDefaultChild; 96 97 // A flag to let us know that we haven't started iterating yet. 98 bool mIsFirst; 99 100 // If not zero, we're iterating inserted children for an insertion point. This 101 // is an index into mChild's inserted children array (mChild must be an 102 // nsXBLChildrenElement). The index is one past the "current" child (as 103 // opposed to mChild which represents the "current" child). 104 uint32_t mIndexInInserted; 105 }; 106 107 // Iterates over the flattened children of a node, which accounts for anonymous 108 // children and nodes moved by insertion points. If a node has anonymous 109 // children, those are iterated over. The iterator can be initialized to start 110 // at the end by providing false for aStartAtBeginning in order to start 111 // iterating in reverse from the last child. 112 class FlattenedChildIterator : public ExplicitChildIterator { 113 public: 114 explicit FlattenedChildIterator(const nsIContent* aParent, 115 bool aStartAtBeginning = true) ExplicitChildIterator(aParent,aStartAtBeginning)116 : ExplicitChildIterator(aParent, aStartAtBeginning), 117 mOriginalContent(aParent) { 118 Init(false); 119 } 120 ShadowDOMInvolved()121 bool ShadowDOMInvolved() { return mShadowDOMInvolved; } 122 Parent()123 const nsIContent* Parent() const { return mOriginalContent; } 124 125 private: 126 void Init(bool aIgnoreXBL); 127 128 protected: 129 /** 130 * This constructor is a hack to help AllChildrenIterator which sometimes 131 * doesn't want to consider XBL. 132 */ 133 FlattenedChildIterator(const nsIContent* aParent, uint32_t aFlags, 134 bool aStartAtBeginning = true) ExplicitChildIterator(aParent,aStartAtBeginning)135 : ExplicitChildIterator(aParent, aStartAtBeginning), 136 mOriginalContent(aParent) { 137 bool ignoreXBL = aFlags & nsIContent::eAllButXBL; 138 Init(ignoreXBL); 139 } 140 141 const nsIContent* mOriginalContent; 142 143 private: 144 // For certain optimizations, nsCSSFrameConstructor needs to know if the child 145 // list of the element that we're iterating matches its .childNodes. 146 bool mShadowDOMInvolved = false; 147 }; 148 149 /** 150 * AllChildrenIterator traverses the children of an element including before / 151 * after content and optionally XBL children. The iterator can be initialized 152 * to start at the end by providing false for aStartAtBeginning in order to 153 * start iterating in reverse from the last child. 154 * 155 * Note: it assumes that no mutation of the DOM or frame tree takes place during 156 * iteration, and will break horribly if that is not true. 157 */ 158 class AllChildrenIterator : private FlattenedChildIterator { 159 public: 160 AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags, 161 bool aStartAtBeginning = true) FlattenedChildIterator(aNode,aFlags,aStartAtBeginning)162 : FlattenedChildIterator(aNode, aFlags, aStartAtBeginning), 163 mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0), 164 mFlags(aFlags), 165 mPhase(aStartAtBeginning ? eAtBegin : eAtEnd) {} 166 167 #ifdef DEBUG 168 AllChildrenIterator(AllChildrenIterator&&) = default; 169 170 AllChildrenIterator& operator=(AllChildrenIterator&& aOther) { 171 // Explicitly call the destructor to ensure the assertion in the destructor 172 // is checked. 173 this->~AllChildrenIterator(); 174 new (this) AllChildrenIterator(std::move(aOther)); 175 return *this; 176 } 177 ~AllChildrenIterator()178 ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); } 179 #endif 180 181 // Returns the current target the iterator is at, or null if the iterator 182 // doesn't point to any child node (either eAtBegin or eAtEnd phase). 183 nsIContent* Get() const; 184 185 // Seeks the given node in children of a parent element, starting from 186 // the current iterator's position, and sets the iterator at the given child 187 // node if it was found. 188 bool Seek(const nsIContent* aChildToFind); 189 190 nsIContent* GetNextChild(); 191 nsIContent* GetPreviousChild(); 192 193 enum IteratorPhase { 194 eAtBegin, 195 eAtMarkerKid, 196 eAtBeforeKid, 197 eAtExplicitKids, 198 eAtAnonKids, 199 eAtAfterKid, 200 eAtEnd 201 }; Phase()202 IteratorPhase Phase() const { return mPhase; } 203 204 private: 205 // Helpers. 206 void AppendNativeAnonymousChildren(); 207 208 // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index 209 // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is 210 // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If 211 // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after 212 // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator 213 // is somewhere before the first native anon child. 214 nsTArray<nsIContent*> mAnonKids; 215 uint32_t mAnonKidsIdx; 216 217 uint32_t mFlags; 218 IteratorPhase mPhase; 219 #ifdef DEBUG 220 // XXX we should really assert there are no frame tree changes as well, but 221 // there's no easy way to do that. 222 nsMutationGuard mMutationGuard; 223 #endif 224 }; 225 226 /** 227 * StyleChildrenIterator traverses the children of the element from the 228 * perspective of the style system, particularly the children we need to 229 * traverse during restyle. 230 * 231 * At present, this is identical to AllChildrenIterator with 232 * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have 233 * detect and skip any native anonymous children that are used to implement some 234 * special magic in here that went away, but we keep the separate class so 235 * we can reintroduce special magic back if needed. 236 * 237 * Note: it assumes that no mutation of the DOM or frame tree takes place during 238 * iteration, and will break horribly if that is not true. 239 * 240 * We require this to be memmovable since Rust code can create and move 241 * StyleChildrenIterators. 242 */ 243 class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator 244 : private AllChildrenIterator { 245 public: GetParent(const nsIContent & aContent)246 static nsIContent* GetParent(const nsIContent& aContent) { 247 nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle(); 248 return node && node->IsContent() ? node->AsContent() : nullptr; 249 } 250 251 explicit StyleChildrenIterator(const nsIContent* aContent, 252 bool aStartAtBeginning = true) 253 : AllChildrenIterator( 254 aContent, 255 nsIContent::eAllChildren | 256 nsIContent::eSkipDocumentLevelNativeAnonymousContent, 257 aStartAtBeginning) { 258 MOZ_COUNT_CTOR(StyleChildrenIterator); 259 } 260 StyleChildrenIterator(StyleChildrenIterator && aOther)261 StyleChildrenIterator(StyleChildrenIterator&& aOther) 262 : AllChildrenIterator(std::move(aOther)) { 263 MOZ_COUNT_CTOR(StyleChildrenIterator); 264 } 265 266 StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther) = default; 267 268 MOZ_COUNTED_DTOR(StyleChildrenIterator) 269 270 using AllChildrenIterator::GetNextChild; 271 using AllChildrenIterator::GetPreviousChild; 272 using AllChildrenIterator::Seek; 273 }; 274 275 } // namespace dom 276 } // namespace mozilla 277 278 #endif 279