ChildIterator.h (7324B)
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 <stdint.h> 11 12 #include "nsIContent.h" 13 #include "nsIContentInlines.h" 14 15 class nsIContent; 16 17 namespace mozilla::dom { 18 19 // Iterates over the flattened children of a node, that is, the regular DOM 20 // child nodes of a given DOM node, with assigned nodes as slot children, and 21 // shadow host children replaced by their shadow root. 22 // 23 // The iterator can be initialized to start at the end by providing false for 24 // aStartAtBeginning in order to start iterating in reverse from the last child. 25 class FlattenedChildIterator { 26 public: 27 explicit FlattenedChildIterator(const nsIContent* aParent, 28 bool aStartAtBeginning = true); 29 30 nsIContent* GetNextChild(); 31 32 // Looks for aChildToFind respecting insertion points until aChildToFind is 33 // found. This can be O(1) instead of O(N) in many cases. 34 bool Seek(const nsIContent* aChildToFind); 35 36 // Returns the current target of this iterator (which might be an explicit 37 // child of the node, or a node assigned to a slot. 38 nsIContent* Get() const { return mChild; } 39 40 // Returns the original parent we were initialized with. 41 const nsIContent* Parent() const { return mOriginalParent; } 42 43 // The inverse of GetNextChild. Properly steps in and out of insertion 44 // points. 45 nsIContent* GetPreviousChild(); 46 47 bool ShadowDOMInvolved() const { return mShadowDOMInvolved; } 48 49 static uint32_t GetLength(const nsINode* aParent); 50 static Maybe<uint32_t> GetIndexOf(const nsINode* aParent, 51 const nsINode* aPossibleChild); 52 53 protected: 54 // The parent of the children being iterated. For shadow hosts this will point 55 // to its shadow root. 56 const nsIContent* mParent; 57 58 // If parent is a slot element with assigned slots, this points to the parent 59 // as HTMLSlotElement, otherwise, it's null. 60 const HTMLSlotElement* mParentAsSlot = nullptr; 61 62 const nsIContent* mOriginalParent = nullptr; 63 64 // The current child. 65 nsIContent* mChild = nullptr; 66 67 // A flag to let us know that we haven't started iterating yet. 68 bool mIsFirst = false; 69 70 // The index of the current element in the slot assigned nodes. One-past the 71 // end to represent the last position. 72 uint32_t mIndexInInserted = 0u; 73 74 // For certain optimizations, nsCSSFrameConstructor needs to know if the child 75 // list of the element that we're iterating matches its .childNodes. 76 bool mShadowDOMInvolved = false; 77 }; 78 79 /** 80 * AllChildrenIterator traverses the children of an element including before / 81 * after content and shadow DOM. The iterator can be initialized to start at 82 * the end by providing false for aStartAtBeginning in order to start iterating 83 * in reverse from the last child. 84 * 85 * Note: it assumes that no mutation of the DOM or frame tree takes place during 86 * iteration, and will break horribly if that is not true. 87 */ 88 class AllChildrenIterator : private FlattenedChildIterator { 89 public: 90 AllChildrenIterator(const nsIContent* aNode, uint32_t aFlags, 91 bool aStartAtBeginning = true) 92 : FlattenedChildIterator(aNode, aStartAtBeginning), 93 mAnonKidsIdx(aStartAtBeginning ? UINT32_MAX : 0), 94 mFlags(aFlags), 95 mPhase(aStartAtBeginning ? Phase::AtBegin : Phase::AtEnd) {} 96 97 #ifdef DEBUG 98 AllChildrenIterator(AllChildrenIterator&&) = default; 99 100 AllChildrenIterator& operator=(AllChildrenIterator&& aOther) { 101 // Explicitly call the destructor to ensure the assertion in the destructor 102 // is checked. 103 this->~AllChildrenIterator(); 104 new (this) AllChildrenIterator(std::move(aOther)); 105 return *this; 106 } 107 108 ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); } 109 #endif 110 111 // Returns the current target the iterator is at, or null if the iterator 112 // doesn't point to any child node (either eAtBegin or eAtEnd phase). 113 nsIContent* Get() const; 114 115 // Seeks the given node in children of a parent element, starting from 116 // the current iterator's position, and sets the iterator at the given child 117 // node if it was found. 118 bool Seek(const nsIContent* aChildToFind); 119 120 nsIContent* GetNextChild(); 121 nsIContent* GetPreviousChild(); 122 123 private: 124 enum class Phase : uint8_t { 125 AtBegin, 126 AtBackdropKid, 127 AtMarkerKid, 128 AtBeforeKid, 129 AtFlatTreeKids, 130 AtAnonKids, 131 AtAfterKid, 132 AtEnd 133 }; 134 135 // Helpers. 136 void AppendNativeAnonymousChildren(); 137 138 // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index 139 // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is 140 // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If 141 // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after 142 // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator 143 // is somewhere before the first native anon child. 144 nsTArray<nsIContent*> mAnonKids; 145 uint32_t mAnonKidsIdx; 146 147 uint32_t mFlags; 148 Phase mPhase; 149 #ifdef DEBUG 150 // XXX we should really assert there are no frame tree changes as well, but 151 // there's no easy way to do that. 152 nsMutationGuard mMutationGuard; 153 #endif 154 }; 155 156 /** 157 * StyleChildrenIterator traverses the children of the element from the 158 * perspective of the style system, particularly the children we need to 159 * traverse during restyle. 160 * 161 * At present, this is identical to AllChildrenIterator with 162 * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have 163 * detect and skip any native anonymous children that are used to implement some 164 * special magic in here that went away, but we keep the separate class so 165 * we can reintroduce special magic back if needed. 166 * 167 * Note: it assumes that no mutation of the DOM or frame tree takes place during 168 * iteration, and will break horribly if that is not true. 169 * 170 * We require this to be memmovable since Rust code can create and move 171 * StyleChildrenIterators. 172 */ 173 class MOZ_NEEDS_MEMMOVABLE_MEMBERS StyleChildrenIterator 174 : private AllChildrenIterator { 175 public: 176 static nsIContent* GetParent(const nsIContent& aContent) { 177 nsINode* node = aContent.GetFlattenedTreeParentNodeForStyle(); 178 return node && node->IsContent() ? node->AsContent() : nullptr; 179 } 180 181 explicit StyleChildrenIterator(const nsIContent* aContent, 182 bool aStartAtBeginning = true) 183 : AllChildrenIterator( 184 aContent, 185 nsIContent::eAllChildren | 186 nsIContent::eSkipDocumentLevelNativeAnonymousContent, 187 aStartAtBeginning) { 188 MOZ_COUNT_CTOR(StyleChildrenIterator); 189 } 190 191 StyleChildrenIterator(StyleChildrenIterator&& aOther) 192 : AllChildrenIterator(std::move(aOther)) { 193 MOZ_COUNT_CTOR(StyleChildrenIterator); 194 } 195 196 StyleChildrenIterator& operator=(StyleChildrenIterator&& aOther) = default; 197 198 MOZ_COUNTED_DTOR(StyleChildrenIterator) 199 200 using AllChildrenIterator::GetNextChild; 201 using AllChildrenIterator::GetPreviousChild; 202 using AllChildrenIterator::Seek; 203 }; 204 205 } // namespace mozilla::dom 206 207 #endif