LibWeb/DOM: Factor out 'live range pre remove steps' AO

This was a refactoring made in the DOM spec as part of the
introduction of the move before API.
This commit is contained in:
Shannon Booth 2025-03-08 03:45:12 +13:00 committed by Andrew Kaster
commit a47c4dbc63
Notes: github-actions[bot] 2025-04-26 14:46:49 +00:00
2 changed files with 34 additions and 19 deletions

View file

@ -858,13 +858,13 @@ WebIDL::ExceptionOr<GC::Ref<Node>> Node::append_child(GC::Ref<Node> node)
return pre_insert(node, nullptr); return pre_insert(node, nullptr);
} }
// https://dom.spec.whatwg.org/#concept-node-remove // https://dom.spec.whatwg.org/#live-range-pre-remove-steps
void Node::remove(bool suppress_observers) void Node::live_range_pre_remove()
{ {
// 1. Let parent be nodes parent // 1. Let parent be nodes parent.
auto* parent = this->parent(); auto* parent = this->parent();
// 2. Assert: parent is non-null. // 2. Assert: parent is not null.
VERIFY(parent); VERIFY(parent);
// 3. Let index be nodes index. // 3. Let index be nodes index.
@ -893,16 +893,29 @@ void Node::remove(bool suppress_observers)
if (range->end_container() == parent && range->end_offset() > index) if (range->end_container() == parent && range->end_offset() > index)
range->decrease_end_offset({}, 1); range->decrease_end_offset({}, 1);
} }
}
// 8. For each NodeIterator object iterator whose roots node document is nodes node document, run the NodeIterator pre-removing steps given node and iterator. // https://dom.spec.whatwg.org/#concept-node-remove
void Node::remove(bool suppress_observers)
{
// 1. Let parent be nodes parent
auto* parent = this->parent();
// 2. Assert: parent is non-null.
VERIFY(parent);
// 3. Run the live range pre-remove steps, given node.
live_range_pre_remove();
// 4. For each NodeIterator object iterator whose roots node document is nodes node document, run the NodeIterator pre-removing steps given node and iterator.
document().for_each_node_iterator([&](NodeIterator& node_iterator) { document().for_each_node_iterator([&](NodeIterator& node_iterator) {
node_iterator.run_pre_removing_steps(*this); node_iterator.run_pre_removing_steps(*this);
}); });
// 9. Let oldPreviousSibling be nodes previous sibling. // 5. Let oldPreviousSibling be nodes previous sibling.
GC::Ptr<Node> old_previous_sibling = previous_sibling(); GC::Ptr<Node> old_previous_sibling = previous_sibling();
// 10. Let oldNextSibling be nodes next sibling. // 6. Let oldNextSibling be nodes next sibling.
GC::Ptr<Node> old_next_sibling = next_sibling(); GC::Ptr<Node> old_next_sibling = next_sibling();
if (is_connected()) { if (is_connected()) {
@ -916,16 +929,16 @@ void Node::remove(bool suppress_observers)
parent->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeRemove); parent->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeRemove);
} }
// 11. Remove node from its parents children. // 7. Remove node from its parents children.
parent->remove_child_impl(*this); parent->remove_child_impl(*this);
// 12. If node is assigned, then run assign slottables for nodes assigned slot. // 8. If node is assigned, then run assign slottables for nodes assigned slot.
if (auto assigned_slot = assigned_slot_for_node(*this)) if (auto assigned_slot = assigned_slot_for_node(*this))
assign_slottables(*assigned_slot); assign_slottables(*assigned_slot);
auto& parent_root = parent->root(); auto& parent_root = parent->root();
// 13. If parents root is a shadow root, and parent is a slot whose assigned nodes is the empty list, then run // 9. If parents root is a shadow root, and parent is a slot whose assigned nodes is the empty list, then run
// signal a slot change for parent. // signal a slot change for parent.
if (parent_root.is_shadow_root() && is<HTML::HTMLSlotElement>(parent)) { if (parent_root.is_shadow_root() && is<HTML::HTMLSlotElement>(parent)) {
auto& slot = static_cast<HTML::HTMLSlotElement&>(*parent); auto& slot = static_cast<HTML::HTMLSlotElement&>(*parent);
@ -934,7 +947,7 @@ void Node::remove(bool suppress_observers)
signal_a_slot_change(slot); signal_a_slot_change(slot);
} }
// 14. If node has an inclusive descendant that is a slot, then: // 10. If node has an inclusive descendant that is a slot, then:
auto has_descendent_slot = false; auto has_descendent_slot = false;
for_each_in_inclusive_subtree_of_type<HTML::HTMLSlotElement>([&](auto const&) { for_each_in_inclusive_subtree_of_type<HTML::HTMLSlotElement>([&](auto const&) {
@ -950,13 +963,13 @@ void Node::remove(bool suppress_observers)
assign_slottables_for_a_tree(*this); assign_slottables_for_a_tree(*this);
} }
// 15. Run the removing steps with node and parent. // 11. Run the removing steps with node and parent.
removed_from(parent, parent_root); removed_from(parent, parent_root);
// 16. Let isParentConnected be parents connected. // 12. Let isParentConnected be parents connected.
bool is_parent_connected = parent->is_connected(); bool is_parent_connected = parent->is_connected();
// 17. If node is custom and isParentConnected is true, then enqueue a custom element callback reaction with node, // 13. If node is custom and isParentConnected is true, then enqueue a custom element callback reaction with node,
// callback name "disconnectedCallback", and an empty argument list. // callback name "disconnectedCallback", and an empty argument list.
// Spec Note: It is intentional for now that custom elements do not get parent passed. // Spec Note: It is intentional for now that custom elements do not get parent passed.
// This might change in the future if there is a need. // This might change in the future if there is a need.
@ -969,7 +982,7 @@ void Node::remove(bool suppress_observers)
} }
} }
// 18. For each shadow-including descendant descendant of node, in shadow-including tree order, then: // 14. For each shadow-including descendant descendant of node, in shadow-including tree order, then:
for_each_shadow_including_descendant([&](Node& descendant) { for_each_shadow_including_descendant([&](Node& descendant) {
// 1. Run the removing steps with descendant // 1. Run the removing steps with descendant
descendant.removed_from(nullptr, parent_root); descendant.removed_from(nullptr, parent_root);
@ -988,7 +1001,7 @@ void Node::remove(bool suppress_observers)
return TraversalDecision::Continue; return TraversalDecision::Continue;
}); });
// 19. For each inclusive ancestor inclusiveAncestor of parent, and then for each registered of inclusiveAncestors registered observer list, // 15. For each inclusive ancestor inclusiveAncestor of parent, and then for each registered of inclusiveAncestors registered observer list,
// if registereds options["subtree"] is true, then append a new transient registered observer // if registereds options["subtree"] is true, then append a new transient registered observer
// whose observer is registereds observer, options is registereds options, and source is registered to nodes registered observer list. // whose observer is registereds observer, options is registereds options, and source is registered to nodes registered observer list.
for (auto* inclusive_ancestor = parent; inclusive_ancestor; inclusive_ancestor = inclusive_ancestor->parent()) { for (auto* inclusive_ancestor = parent; inclusive_ancestor; inclusive_ancestor = inclusive_ancestor->parent()) {
@ -1002,12 +1015,12 @@ void Node::remove(bool suppress_observers)
} }
} }
// 20. If suppress observers flag is unset, then queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling. // 16. If suppress observers flag is unset, then queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling.
if (!suppress_observers) { if (!suppress_observers) {
parent->queue_tree_mutation_record({}, { *this }, old_previous_sibling.ptr(), old_next_sibling.ptr()); parent->queue_tree_mutation_record({}, { *this }, old_previous_sibling.ptr(), old_next_sibling.ptr());
} }
// 21. Run the children changed steps for parent. // 17. Run the children changed steps for parent.
parent->children_changed(nullptr); parent->children_changed(nullptr);
document().bump_dom_tree_version(); document().bump_dom_tree_version();

View file

@ -589,6 +589,8 @@ protected:
private: private:
void queue_tree_mutation_record(Vector<GC::Root<Node>> added_nodes, Vector<GC::Root<Node>> removed_nodes, Node* previous_sibling, Node* next_sibling); void queue_tree_mutation_record(Vector<GC::Root<Node>> added_nodes, Vector<GC::Root<Node>> removed_nodes, Node* previous_sibling, Node* next_sibling);
void live_range_pre_remove();
void insert_before_impl(GC::Ref<Node>, GC::Ptr<Node> child); void insert_before_impl(GC::Ref<Node>, GC::Ptr<Node> child);
void append_child_impl(GC::Ref<Node>); void append_child_impl(GC::Ref<Node>);
void remove_child_impl(GC::Ref<Node>); void remove_child_impl(GC::Ref<Node>);