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
parent 6507d23e29
commit 77b8349ae1
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);
}
// https://dom.spec.whatwg.org/#concept-node-remove
void Node::remove(bool suppress_observers)
// https://dom.spec.whatwg.org/#live-range-pre-remove-steps
void Node::live_range_pre_remove()
{
// 1. Let parent be nodes parent
// 1. Let parent be nodes parent.
auto* parent = this->parent();
// 2. Assert: parent is non-null.
// 2. Assert: parent is not null.
VERIFY(parent);
// 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)
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) {
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();
// 10. Let oldNextSibling be nodes next sibling.
// 6. Let oldNextSibling be nodes next sibling.
GC::Ptr<Node> old_next_sibling = next_sibling();
if (is_connected()) {
@ -916,17 +929,17 @@ void Node::remove(bool suppress_observers)
parent->set_needs_layout_tree_update(true);
}
// 11. Remove node from its parents children.
// 7. Remove node from its parents children.
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))
assign_slottables(*assigned_slot);
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
// signal a slot change for parent.
// 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.
if (parent_root.is_shadow_root() && is<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);
}
// 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;
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);
}
// 15. Run the removing steps with node and parent.
// 11. Run the removing steps with node and parent.
removed_from(parent, parent_root);
// 16. Let isParentConnected be parents connected.
// 12. Let isParentConnected be parents 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.
// 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.
@ -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) {
// 1. Run the removing steps with descendant
descendant.removed_from(nullptr, parent_root);
@ -988,7 +1001,7 @@ void Node::remove(bool suppress_observers)
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
// 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()) {
@ -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) {
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);
document().bump_dom_tree_version();

View file

@ -567,6 +567,8 @@ protected:
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 live_range_pre_remove();
void insert_before_impl(GC::Ref<Node>, GC::Ptr<Node> child);
void append_child_impl(GC::Ref<Node>);
void remove_child_impl(GC::Ref<Node>);