@@ -15,12 +15,12 @@ namespace Web::DOM {
1515
1616GC_DEFINE_ALLOCATOR (NodeIterator);
1717
18- NodeIterator::NodeIterator (JS ::Realm& realm, Node& root)
18+ NodeIterator::NodeIterator (JS ::Realm& realm, GC ::Ref< Node> root, NodePointer reference )
1919 : PlatformObject(realm)
2020 , m_root(root)
21- , m_reference({ root } )
21+ , m_reference(reference )
2222{
23- root. document ().register_node_iterator ({}, *this );
23+ m_root-> document ().register_node_iterator ({}, *this );
2424}
2525
2626NodeIterator::~NodeIterator () = default ;
@@ -44,17 +44,17 @@ void NodeIterator::visit_edges(Cell::Visitor& visitor)
4444 visitor.visit (m_root);
4545 visitor.visit (m_reference.node );
4646
47- if (m_traversal_pointer .has_value ())
48- visitor.visit (m_traversal_pointer ->node );
47+ if (m_candidate_reference .has_value ())
48+ visitor.visit (m_candidate_reference ->node );
4949}
5050
5151// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
5252GC ::Ref<NodeIterator> NodeIterator::create (JS ::Realm& realm, Node& root, unsigned what_to_show, GC ::Ptr<NodeFilter> filter)
5353{
5454 // 1. Let iterator be a new NodeIterator object.
55- // 2. Set iterator’s root and iterator’s reference to root.
56- // 3. Set iterator’s pointer before reference to true.
57- auto iterator = realm.create <NodeIterator>(realm, root);
55+ // 2. Set iterator’s root to root.
56+ // 3. Set iterator’s reference to (root, true) .
57+ auto iterator = realm.create <NodeIterator>(realm, root, NodePointer { root, true } );
5858
5959 // 4. Set iterator’s whatToShow to whatToShow.
6060 iterator->m_what_to_show = what_to_show;
@@ -76,65 +76,82 @@ void NodeIterator::detach()
7676// https://dom.spec.whatwg.org/#concept-nodeiterator-traverse
7777JS ::ThrowCompletionOr<GC ::Ptr<Node>> NodeIterator::traverse (Direction direction)
7878{
79- // 1. Let node be iterator’s reference.
80- // 2. Let beforeNode be iterator’s pointer before reference.
81- m_traversal_pointer = m_reference;
79+ // 1. Set iterator’s candidate reference to iterator’s reference.
80+ m_candidate_reference = m_reference;
8281
83- GC ::Ptr<Node> candidate;
82+ // 2. Let result be null.
83+ GC ::Ptr<Node> result;
8484
8585 // 3. While true:
8686 while (true ) {
87- // 4. Branch on direction :
87+ // 1. If type is "next" :
8888 if (direction == Direction::Next) {
89- // next
90- // If beforeNode is false, then set node to the first node following node in iterator’s iterator collection.
91- // If there is no such node, then return null.
92- if (!m_traversal_pointer->is_before_node ) {
93- auto * next_node = m_traversal_pointer->node ->next_in_pre_order (m_root.ptr ());
94- if (!next_node)
95- return nullptr ;
96- m_traversal_pointer->node = *next_node;
97- } else {
98- // If beforeNode is true, then set it to false.
99- m_traversal_pointer->is_before_node = false ;
89+ // 1. If iterator’s candidate reference’s pointer before is false:
90+ if (!m_candidate_reference->pointer_before ) {
91+ // 1. Let following be the first node following candidate reference’s node in iterator’s iterator collection.
92+ // If there is no such node, then break.
93+ auto * following = m_candidate_reference->node ->next_in_pre_order (m_root);
94+ if (!following)
95+ break ;
96+
97+ // 2. Set candidate reference to (following, false).
98+ m_candidate_reference = { *following, false };
10099 }
101- } else {
102- // previous
103- // If beforeNode is true, then set node to the first node preceding node in iterator’s iterator collection.
104- // If there is no such node, then return null.
105- if (m_traversal_pointer->is_before_node ) {
106- if (m_traversal_pointer->node .ptr () == m_root.ptr ())
107- return nullptr ;
108- auto * previous_node = m_traversal_pointer->node ->previous_in_pre_order ();
109- if (!previous_node)
110- return nullptr ;
111- m_traversal_pointer->node = *previous_node;
112- } else {
113- // If beforeNode is false, then set it to true.
114- m_traversal_pointer->is_before_node = true ;
100+ // 2. Otherwise, set candidate reference to (candidate reference’s node, false).
101+ else {
102+ m_candidate_reference = { m_candidate_reference->node , false };
115103 }
116104 }
105+ // 2. Otherwise:
106+ else {
107+ // 1. If iterator’s candidate reference’s pointer before is true:
108+ if (m_candidate_reference->pointer_before ) {
109+ // 1. Let preceding be the first node preceding candidate reference’s node in iterator’s iterator collection.
110+ // If there is no such node, then break.
111+ if (m_candidate_reference->node == m_root)
112+ break ;
113+ auto * preceding = m_candidate_reference->node ->previous_in_pre_order ();
114+ if (!preceding)
115+ break ;
116+
117+ // 2. Set iterator’s candidate reference to (preceding, true).
118+ m_candidate_reference = { *preceding, true };
119+ }
120+ // 2. Otherwise, set candidate reference to (candidate reference’s node, true).
121+ else {
122+ m_candidate_reference = { m_candidate_reference->node , true };
123+ }
124+ }
125+
126+ // 3. Let node be candidate reference’s node.
127+ GC ::Ref<Node> node = m_candidate_reference->node ;
117128
118- // NOTE: If the NodeFilter deletes the iterator's current traversal pointer,
119- // we will automatically retarget it. However, in that case, we're expected
120- // to return the node passed to the filter, not the adjusted traversal pointer's
121- // node after the filter returns!
122- candidate = m_traversal_pointer->node ;
129+ // 4. Let filterResult be the result of filtering node within iterator. If this throws an exception,
130+ // then set iterator’s candidate reference to null and rethrow that exception.
131+ auto filter_result = filter (node);
132+ if (filter_result.is_error ()) {
133+ m_candidate_reference.clear ();
134+ return filter_result.release_error ();
135+ }
136+
137+ // 5. If filterResult is FILTER_ACCEPT:
138+ if (filter_result.value () == NodeFilter::Result::FILTER_ACCEPT ) {
139+ // 1. Set iterator’s reference to iterator’s candidate reference.
140+ m_reference = *m_candidate_reference;
123141
124- // 2. Let result be the result of filtering node within iterator .
125- auto result = TRY ( filter (*m_traversal_pointer-> node )) ;
142+ // 2. Set result to node.
143+ result = node;
126144
127- // 3. If result is FILTER_ACCEPT, then break.
128- if (result == NodeFilter::Result::FILTER_ACCEPT )
145+ // 3. Break.
129146 break ;
147+ }
130148 }
131149
132- // 4. Set iterator’s reference to node.
133- // 5. Set iterator’s pointer before reference to beforeNode.
134- m_reference = m_traversal_pointer.release_value ();
150+ // 4. Set iterator’s candidate reference to null.
151+ m_candidate_reference.clear ();
135152
136- // 6 . Return node .
137- return candidate ;
153+ // 5 . Return result .
154+ return result ;
138155}
139156
140157// https://dom.spec.whatwg.org/#concept-traversal-filter
@@ -183,76 +200,60 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
183200// https://dom.spec.whatwg.org/#dom-nodeiterator-nextnode
184201JS ::ThrowCompletionOr<GC ::Ptr<Node>> NodeIterator::next_node ()
185202{
203+ // The nextNode() method steps are to return the result of traversing with this and "next".
186204 return traverse (Direction::Next);
187205}
188206
189207// https://dom.spec.whatwg.org/#dom-nodeiterator-previousnode
190208JS ::ThrowCompletionOr<GC ::Ptr<Node>> NodeIterator::previous_node ()
191209{
210+ // The previousNode() method steps are to return the result of traversing with this and "previous".
192211 return traverse (Direction::Previous);
193212}
194213
195- void NodeIterator::run_pre_removing_steps_with_node_pointer (Node& to_be_removed_node, NodePointer& pointer)
214+ // https://dom.spec.whatwg.org/#nodeiterator-adjust
215+ NodeIterator::NodePointer NodeIterator::adjust_node_pointer (NodePointer node_pointer, Node& to_be_removed_node)
196216{
197- // NOTE: This function tries to match the behavior of other engines, but not the DOM specification
198- // as it's a known issue that the spec doesn't match how major browsers behave.
199- // Spec bug: https://github.com/whatwg/dom/issues/907
200-
201- if (!to_be_removed_node.is_descendant_of (root ()))
202- return ;
203-
204- if (!to_be_removed_node.is_inclusive_ancestor_of (pointer.node ))
205- return ;
206-
207- if (pointer.is_before_node ) {
208- if (auto * node = to_be_removed_node.next_in_pre_order (root ())) {
209- while (node && node->is_descendant_of (to_be_removed_node))
210- node = node->next_in_pre_order (root ());
211- if (node)
212- pointer.node = *node;
213- return ;
214- }
215- if (auto * node = to_be_removed_node.previous_in_pre_order ()) {
216- if (to_be_removed_node.is_ancestor_of (pointer.node )) {
217- while (node && node->is_descendant_of (to_be_removed_node))
218- node = node->previous_in_pre_order ();
219- }
220- if (node) {
221- pointer = {
222- .node = *node,
223- .is_before_node = false ,
224- };
225- }
226- }
227- return ;
217+ // 1. If toBeRemovedNode is not an inclusive ancestor of nodePointer’s node, or toBeRemovedNode is an inclusive
218+ // ancestor of nodeIterator’s root, then return nodePointer.
219+ if (!to_be_removed_node.is_inclusive_ancestor_of (node_pointer.node ) || to_be_removed_node.is_inclusive_ancestor_of (root ()))
220+ return node_pointer;
221+
222+ // 2. If nodePointer’s pointer before is true:
223+ if (node_pointer.pointer_before ) {
224+ // 1. Let next be toBeRemovedNode’s first following node that is an inclusive descendant of nodeIterator’s root
225+ // and is not an inclusive descendant of toBeRemovedNode, if there is such a node; otherwise null.
226+ auto * next = to_be_removed_node.next_in_pre_order (root ());
227+ while (next && next->is_descendant_of (to_be_removed_node))
228+ next = next->next_in_pre_order (root ());
229+
230+ // 2. If next is non-null, then return (next, true).
231+ if (next)
232+ return { *next, true };
228233 }
229234
230- if (auto * node = to_be_removed_node.previous_in_pre_order ()) {
231- if (to_be_removed_node.is_ancestor_of (pointer.node )) {
232- while (node && node->is_descendant_of (to_be_removed_node))
233- node = node->previous_in_pre_order ();
234- }
235- if (node)
236- pointer.node = *node;
237- return ;
238- }
239- auto * node = to_be_removed_node.next_in_pre_order (root ());
240- if (to_be_removed_node.is_ancestor_of (pointer.node )) {
241- while (node && node->is_descendant_of (to_be_removed_node))
242- node = node->previous_in_pre_order ();
243- }
244- if (node)
245- pointer.node = *node;
235+ // 3. Let newNode be toBeRemovedNode’s parent, if toBeRemovedNode’s previous sibling is null; otherwise the
236+ // inclusive descendant of toBeRemovedNode’s previous sibling that appears last in tree order.
237+ // NOTE: This is exactly toBeRemovedNode’s previous node in pre-order. It is never null here, because step 1
238+ // guarantees toBeRemovedNode is a proper descendant of the root and thus has a parent.
239+ auto * new_node = to_be_removed_node.previous_in_pre_order ();
240+ VERIFY (new_node);
241+
242+ // 4. Return (newNode, false).
243+ return { *new_node, false };
246244}
247245
248246// https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps
249247void NodeIterator::run_pre_removing_steps (Node& to_be_removed_node)
250248{
251- // NOTE: If we're in the middle of traversal, we have to adjust the traversal pointer in response to node removal.
252- if (m_traversal_pointer.has_value ())
253- run_pre_removing_steps_with_node_pointer (to_be_removed_node, *m_traversal_pointer);
254-
255- run_pre_removing_steps_with_node_pointer (to_be_removed_node, m_reference);
249+ // 1. Set nodeIterator’s reference to the result of adjusting a node pointer given nodeIterator’s reference,
250+ // nodeIterator, and toBeRemovedNode.
251+ m_reference = adjust_node_pointer (m_reference, to_be_removed_node);
252+
253+ // 2. If nodeIterator’s candidate reference is non-null, then set nodeIterator’s candidate reference to the result
254+ // of adjusting a node pointer given nodeIterator’s candidate reference, nodeIterator, and toBeRemovedNode.
255+ if (m_candidate_reference.has_value ())
256+ m_candidate_reference = adjust_node_pointer (*m_candidate_reference, to_be_removed_node);
256257}
257258
258259}
0 commit comments