mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 16:28:11 +00:00
LibWeb: Implement all "childList" mutation records for MutationObserver
This commit is contained in:
parent
c9ba5531e0
commit
56cfd5ced8
3 changed files with 135 additions and 22 deletions
|
@ -11,6 +11,7 @@
|
|||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibWeb/Bindings/EventWrapper.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/Bindings/NodeWrapper.h>
|
||||
#include <LibWeb/Bindings/NodeWrapperFactory.h>
|
||||
#include <LibWeb/DOM/Comment.h>
|
||||
|
@ -21,9 +22,11 @@
|
|||
#include <LibWeb/DOM/EventDispatcher.h>
|
||||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
#include <LibWeb/DOM/LiveNodeList.h>
|
||||
#include <LibWeb/DOM/MutationType.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/ProcessingInstruction.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/DOM/StaticNodeList.h>
|
||||
#include <LibWeb/HTML/BrowsingContextContainer.h>
|
||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
||||
|
@ -345,7 +348,7 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
// 1. Let nodes be node’s children, if node is a DocumentFragment node; otherwise « node ».
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
if (is<DocumentFragment>(*node))
|
||||
nodes = verify_cast<DocumentFragment>(*node).children_as_vector();
|
||||
nodes = node->children_as_vector();
|
||||
else
|
||||
nodes.append(node);
|
||||
|
||||
|
@ -361,8 +364,9 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
// 1. Remove its children with the suppress observers flag set.
|
||||
node->remove_all_children(true);
|
||||
|
||||
// FIXME: 2. Queue a tree mutation record for node with « », nodes, null, and null.
|
||||
// 2. Queue a tree mutation record for node with « », nodes, null, and null.
|
||||
// NOTE: This step intentionally does not pay attention to the suppress observers flag.
|
||||
node->queue_tree_mutation_record(StaticNodeList::create({}), StaticNodeList::create(nodes), nullptr, nullptr);
|
||||
}
|
||||
|
||||
// 5. If child is non-null, then:
|
||||
|
@ -380,7 +384,12 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: 6. Let previousSibling be child’s previous sibling or parent’s last child if child is null. (Currently unused so not included)
|
||||
// 6. Let previousSibling be child’s previous sibling or parent’s last child if child is null.
|
||||
RefPtr<Node> previous_sibling;
|
||||
if (child)
|
||||
previous_sibling = child->previous_sibling();
|
||||
else
|
||||
previous_sibling = last_child();
|
||||
|
||||
// 7. For each node in nodes, in tree order:
|
||||
// FIXME: In tree order
|
||||
|
@ -418,9 +427,8 @@ void Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool supp
|
|||
}
|
||||
|
||||
// 8. If suppress observers flag is unset, then queue a tree mutation record for parent with nodes, « », previousSibling, and child.
|
||||
if (!suppress_observers) {
|
||||
// FIXME: queue a tree mutation record for parent with nodes, « », previousSibling, and child.
|
||||
}
|
||||
if (!suppress_observers)
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create({}), previous_sibling, child);
|
||||
|
||||
// 9. Run the children changed steps for parent.
|
||||
children_changed();
|
||||
|
@ -517,8 +525,11 @@ void Node::remove(bool suppress_observers)
|
|||
node_iterator.run_pre_removing_steps(*this);
|
||||
});
|
||||
|
||||
// FIXME: 9. Let oldPreviousSibling be node’s previous sibling. (Currently unused so not included)
|
||||
// FIXME: 10. Let oldNextSibling be node’s next sibling. (Currently unused so not included)
|
||||
// 9. Let oldPreviousSibling be node’s previous sibling.
|
||||
RefPtr<Node> old_previous_sibling = previous_sibling();
|
||||
|
||||
// 10. Let oldNextSibling be node’s next sibling.
|
||||
RefPtr<Node> old_next_sibling = next_sibling();
|
||||
|
||||
// 11. Remove node from its parent’s children.
|
||||
parent->TreeNode::remove_child(*this);
|
||||
|
@ -558,7 +569,9 @@ 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.
|
||||
if (!suppress_observers) {
|
||||
// FIXME: queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling.
|
||||
NonnullRefPtrVector<Node> removed_nodes;
|
||||
removed_nodes.append(*this);
|
||||
queue_tree_mutation_record(StaticNodeList::create({}), StaticNodeList::create(move(removed_nodes)), old_previous_sibling, old_next_sibling);
|
||||
}
|
||||
|
||||
// 21. Run the children changed steps for parent.
|
||||
|
@ -617,30 +630,40 @@ ExceptionOr<NonnullRefPtr<Node>> Node::replace_child(NonnullRefPtr<Node> node, N
|
|||
}
|
||||
|
||||
// 7. Let referenceChild be child’s next sibling.
|
||||
auto reference_child = child->next_sibling();
|
||||
RefPtr<Node> reference_child = child->next_sibling();
|
||||
|
||||
// 8. If referenceChild is node, then set referenceChild to node’s next sibling.
|
||||
if (reference_child == node)
|
||||
reference_child = node->next_sibling();
|
||||
|
||||
// FIXME: 9. Let previousSibling be child’s previous sibling. (Currently unused so not included)
|
||||
// FIXME: 10. Let removedNodes be the empty set. (Currently unused so not included)
|
||||
// 9. Let previousSibling be child’s previous sibling.
|
||||
RefPtr<Node> previous_sibling = child->previous_sibling();
|
||||
|
||||
// 10. Let removedNodes be the empty set.
|
||||
NonnullRefPtrVector<Node> removed_nodes;
|
||||
|
||||
// 11. If child’s parent is non-null, then:
|
||||
// NOTE: The above can only be false if child is node.
|
||||
if (child->parent()) {
|
||||
// FIXME: 1. Set removedNodes to « child ».
|
||||
// 1. Set removedNodes to « child ».
|
||||
removed_nodes.append(child);
|
||||
|
||||
// 2. Remove child with the suppress observers flag set.
|
||||
child->remove(true);
|
||||
}
|
||||
|
||||
// FIXME: 12. Let nodes be node’s children if node is a DocumentFragment node; otherwise « node ». (Currently unused so not included)
|
||||
// 12. Let nodes be node’s children if node is a DocumentFragment node; otherwise « node ».
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
if (is<DocumentFragment>(*node))
|
||||
nodes = node->children_as_vector();
|
||||
else
|
||||
nodes.append(node);
|
||||
|
||||
// 13. Insert node into parent before referenceChild with the suppress observers flag set.
|
||||
insert_before(node, reference_child, true);
|
||||
|
||||
// FIXME: 14. Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
|
||||
// 14. Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(nodes)), StaticNodeList::create(move(removed_nodes)), previous_sibling, reference_child);
|
||||
|
||||
// 15. Return child.
|
||||
return child;
|
||||
|
@ -1085,10 +1108,20 @@ bool Node::is_shadow_including_inclusive_ancestor_of(Node const& other) const
|
|||
// https://dom.spec.whatwg.org/#concept-node-replace-all
|
||||
void Node::replace_all(RefPtr<Node> node)
|
||||
{
|
||||
// FIXME: 1. Let removedNodes be parent’s children. (Current unused so not included)
|
||||
// FIXME: 2. Let addedNodes be the empty set. (Currently unused so not included)
|
||||
// FIXME: 3. If node is a DocumentFragment node, then set addedNodes to node’s children.
|
||||
// FIXME: 4. Otherwise, if node is non-null, set addedNodes to « node ».
|
||||
// 1. Let removedNodes be parent’s children.
|
||||
auto removed_nodes = children_as_vector();
|
||||
|
||||
// 2. Let addedNodes be the empty set.
|
||||
NonnullRefPtrVector<Node> added_nodes;
|
||||
|
||||
// 3. If node is a DocumentFragment node, then set addedNodes to node’s children.
|
||||
if (node && is<DocumentFragment>(*node)) {
|
||||
added_nodes = node->children_as_vector();
|
||||
}
|
||||
// 4. Otherwise, if node is non-null, set addedNodes to « node ».
|
||||
else if (node) {
|
||||
added_nodes.append(*node);
|
||||
}
|
||||
|
||||
// 5. Remove all parent’s children, in tree order, with the suppress observers flag set.
|
||||
remove_all_children(true);
|
||||
|
@ -1097,7 +1130,9 @@ void Node::replace_all(RefPtr<Node> node)
|
|||
if (node)
|
||||
insert_before(*node, nullptr, true);
|
||||
|
||||
// FIXME: 7. If either addedNodes or removedNodes is not empty, then queue a tree mutation record for parent with addedNodes, removedNodes, null, and null.
|
||||
// 7. If either addedNodes or removedNodes is not empty, then queue a tree mutation record for parent with addedNodes, removedNodes, null, and null.
|
||||
if (!added_nodes.is_empty() || !removed_nodes.is_empty())
|
||||
queue_tree_mutation_record(StaticNodeList::create(move(added_nodes)), StaticNodeList::create(move(removed_nodes)), nullptr, nullptr);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#string-replace-all
|
||||
|
@ -1282,4 +1317,74 @@ Painting::PaintableBox const* Node::paint_box() const
|
|||
return static_cast<Layout::Box const&>(*layout_node()).paint_box();
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#queue-a-mutation-record
|
||||
void Node::queue_mutation_record(FlyString const& type, String attribute_name, String attribute_namespace, String old_value, NonnullRefPtr<NodeList> added_nodes, NonnullRefPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling)
|
||||
{
|
||||
// 1. Let interestedObservers be an empty map.
|
||||
// mutationObserver -> mappedOldValue
|
||||
OrderedHashMap<NonnullRefPtr<MutationObserver>, String> interested_observers;
|
||||
|
||||
// 2. Let nodes be the inclusive ancestors of target.
|
||||
NonnullRefPtrVector<Node> nodes;
|
||||
nodes.append(*this);
|
||||
|
||||
for (auto* parent_node = parent(); parent_node; parent_node = parent_node->parent())
|
||||
nodes.append(*parent_node);
|
||||
|
||||
// 3. For each node in nodes, and then for each registered of node’s registered observer list:
|
||||
for (auto& node : nodes) {
|
||||
for (auto& registered_observer : node.m_registered_observer_list) {
|
||||
// 1. Let options be registered’s options.
|
||||
auto& options = registered_observer.options;
|
||||
|
||||
// 2. If none of the following are true
|
||||
// - node is not target and options["subtree"] is false
|
||||
// - type is "attributes" and options["attributes"] either does not exist or is false
|
||||
// - type is "attributes", options["attributeFilter"] exists, and options["attributeFilter"] does not contain name or namespace is non-null
|
||||
// - type is "characterData" and options["characterData"] either does not exist or is false
|
||||
// - type is "childList" and options["childList"] is false
|
||||
// then:
|
||||
if (!(&node != this && !options.subtree)
|
||||
&& !(type == MutationType::attributes && (!options.attributes.has_value() || !options.attributes.value()))
|
||||
&& !(type == MutationType::attributes && options.attribute_filter.has_value() && (!attribute_namespace.is_null() || !options.attribute_filter->contains_slow(attribute_name)))
|
||||
&& !(type == MutationType::characterData && (!options.character_data.has_value() || !options.character_data.value()))
|
||||
&& !(type == MutationType::childList && !options.child_list)) {
|
||||
// 1. Let mo be registered’s observer.
|
||||
auto mutation_observer = registered_observer.observer;
|
||||
|
||||
// 2. If interestedObservers[mo] does not exist, then set interestedObservers[mo] to null.
|
||||
if (!interested_observers.contains(mutation_observer))
|
||||
interested_observers.set(mutation_observer, {});
|
||||
|
||||
// 3. If either type is "attributes" and options["attributeOldValue"] is true, or type is "characterData" and options["characterDataOldValue"] is true, then set interestedObservers[mo] to oldValue.
|
||||
if ((type == MutationType::attributes && options.attribute_old_value.has_value() && options.attribute_old_value.value()) || (type == MutationType::characterData && options.character_data_old_value.has_value() && options.character_data_old_value.value()))
|
||||
interested_observers.set(mutation_observer, old_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. For each observer → mappedOldValue of interestedObservers:
|
||||
for (auto& interested_observer : interested_observers) {
|
||||
// 1. Let record be a new MutationRecord object with its type set to type, target set to target, attributeName set to name, attributeNamespace set to namespace, oldValue set to mappedOldValue,
|
||||
// addedNodes set to addedNodes, removedNodes set to removedNodes, previousSibling set to previousSibling, and nextSibling set to nextSibling.
|
||||
auto record = MutationRecord::create(type, *this, added_nodes, removed_nodes, previous_sibling, next_sibling, attribute_name, attribute_namespace, /* mappedOldValue */ interested_observer.value);
|
||||
|
||||
// 2. Enqueue record to observer’s record queue.
|
||||
interested_observer.key->enqueue_record({}, move(record));
|
||||
}
|
||||
|
||||
// 5. Queue a mutation observer microtask.
|
||||
Bindings::queue_mutation_observer_microtask(document());
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#queue-a-tree-mutation-record
|
||||
void Node::queue_tree_mutation_record(NonnullRefPtr<NodeList> added_nodes, NonnullRefPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling)
|
||||
{
|
||||
// 1. Assert: either addedNodes or removedNodes is not empty.
|
||||
VERIFY(added_nodes->length() > 0 || removed_nodes->length() > 0);
|
||||
|
||||
// 2. Queue a mutation record of "childList" for target with null, null, null, addedNodes, removedNodes, previousSibling, and nextSibling.
|
||||
queue_mutation_record(MutationType::childList, {}, {}, {}, move(added_nodes), move(removed_nodes), previous_sibling, next_sibling);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue