mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:47:34 +00:00
LibWeb: Implement "NodeIterator pre-removing steps"
These steps run when a node is about to be removed from its parent, and adjust the position of any live NodeIterators so that they don't point at a now-removed node. Note that while this commit implements what's in the DOM specification, the specification doesn't fully match what other browsers do. Spec bug: https://github.com/whatwg/dom/issues/907
This commit is contained in:
parent
acbdb95b0a
commit
9c6999ecf2
5 changed files with 91 additions and 2 deletions
|
@ -1037,6 +1037,17 @@ void Document::adopt_node(Node& node)
|
||||||
inclusive_descendant.adopted_from(old_document);
|
inclusive_descendant.adopted_from(old_document);
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Transfer NodeIterators rooted at `node` from old_document to this document.
|
||||||
|
Vector<NodeIterator&> node_iterators_to_transfer;
|
||||||
|
for (auto* node_iterator : old_document.m_node_iterators) {
|
||||||
|
if (node_iterator->root() == &node)
|
||||||
|
node_iterators_to_transfer.append(*node_iterator);
|
||||||
|
}
|
||||||
|
for (auto& node_iterator : node_iterators_to_transfer) {
|
||||||
|
old_document.m_node_iterators.remove(&node_iterator);
|
||||||
|
m_node_iterators.set(&node_iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1474,4 +1485,16 @@ NonnullRefPtr<TreeWalker> Document::create_tree_walker(Node& root, unsigned what
|
||||||
return TreeWalker::create(root, what_to_show, move(filter));
|
return TreeWalker::create(root, what_to_show, move(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::register_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
|
||||||
|
{
|
||||||
|
auto result = m_node_iterators.set(&node_iterator);
|
||||||
|
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::unregister_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
|
||||||
|
{
|
||||||
|
bool was_removed = m_node_iterators.remove(&node_iterator);
|
||||||
|
VERIFY(was_removed);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -327,6 +327,16 @@ public:
|
||||||
NonnullRefPtr<NodeIterator> create_node_iterator(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
NonnullRefPtr<NodeIterator> create_node_iterator(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||||
NonnullRefPtr<TreeWalker> create_tree_walker(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
NonnullRefPtr<TreeWalker> create_tree_walker(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||||
|
|
||||||
|
void register_node_iterator(Badge<NodeIterator>, NodeIterator&);
|
||||||
|
void unregister_node_iterator(Badge<NodeIterator>, NodeIterator&);
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
void for_each_node_iterator(Callback callback)
|
||||||
|
{
|
||||||
|
for (auto* node_iterator : m_node_iterators)
|
||||||
|
callback(*node_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Document(const AK::URL&);
|
explicit Document(const AK::URL&);
|
||||||
|
|
||||||
|
@ -430,5 +440,8 @@ private:
|
||||||
Vector<WeakPtr<CSS::MediaQueryList>> m_media_query_lists;
|
Vector<WeakPtr<CSS::MediaQueryList>> m_media_query_lists;
|
||||||
|
|
||||||
bool m_needs_layout { false };
|
bool m_needs_layout { false };
|
||||||
|
|
||||||
|
HashTable<NodeIterator*> m_node_iterators;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,7 +387,10 @@ void Node::remove(bool suppress_observers)
|
||||||
// FIXME: For each live range whose start node is parent and start offset is greater than index, decrease its start offset by 1.
|
// FIXME: For each live range whose start node is parent and start offset is greater than index, decrease its start offset by 1.
|
||||||
// FIXME: For each live range whose end node is parent and end offset is greater than index, decrease its end offset by 1.
|
// FIXME: For each live range whose end node is parent and end offset is greater than index, decrease its end offset by 1.
|
||||||
|
|
||||||
// FIXME: For each NodeIterator object iterator whose root’s node document is node’s node document, run the NodeIterator pre-removing steps given node and iterator.
|
// For each NodeIterator object iterator whose root’s node document is node’s 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);
|
||||||
|
});
|
||||||
|
|
||||||
// FIXME: Let oldPreviousSibling be node’s previous sibling. (Currently unused so not included)
|
// FIXME: Let oldPreviousSibling be node’s previous sibling. (Currently unused so not included)
|
||||||
// FIXME: Let oldNextSibling be node’s next sibling. (Currently unused so not included)
|
// FIXME: Let oldNextSibling be node’s next sibling. (Currently unused so not included)
|
||||||
|
|
|
@ -17,6 +17,12 @@ NodeIterator::NodeIterator(Node& root)
|
||||||
: m_root(root)
|
: m_root(root)
|
||||||
, m_reference(root)
|
, m_reference(root)
|
||||||
{
|
{
|
||||||
|
root.document().register_node_iterator({}, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeIterator::~NodeIterator()
|
||||||
|
{
|
||||||
|
m_root->document().unregister_node_iterator({}, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
|
// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
|
||||||
|
@ -160,4 +166,44 @@ JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::previous_node()
|
||||||
return traverse(Direction::Previous);
|
return traverse(Direction::Previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#nodeiterator-pre-removing-steps
|
||||||
|
void NodeIterator::run_pre_removing_steps(Node& to_be_removed_node)
|
||||||
|
{
|
||||||
|
// NOTE: This function implements what the DOM specification tells us to do.
|
||||||
|
// However, it's known to not match other browsers: https://github.com/whatwg/dom/issues/907
|
||||||
|
|
||||||
|
// 1. If toBeRemovedNode is not an inclusive ancestor of nodeIterator’s reference, or toBeRemovedNode is nodeIterator’s root, then return.
|
||||||
|
if (!to_be_removed_node.is_inclusive_ancestor_of(m_reference) || &to_be_removed_node == m_root)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 2. If nodeIterator’s pointer before reference is true, then:
|
||||||
|
if (m_pointer_before_reference) {
|
||||||
|
// 1. Let next be toBeRemovedNode’s first following node that is an inclusive descendant of nodeIterator’s root and is not an inclusive descendant of toBeRemovedNode, and null if there is no such node.
|
||||||
|
RefPtr<Node> next = to_be_removed_node.next_in_pre_order(m_root);
|
||||||
|
while (next && (!next->is_inclusive_descendant_of(m_root) || next->is_inclusive_descendant_of(to_be_removed_node)))
|
||||||
|
next = next->next_in_pre_order(m_root);
|
||||||
|
|
||||||
|
// 2. If next is non-null, then set nodeIterator’s reference to next and return.
|
||||||
|
if (next) {
|
||||||
|
m_reference = *next;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Otherwise, set nodeIterator’s pointer before reference to false.
|
||||||
|
m_pointer_before_reference = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set nodeIterator’s reference to toBeRemovedNode’s parent, if toBeRemovedNode’s previous sibling is null,
|
||||||
|
if (!to_be_removed_node.previous_sibling()) {
|
||||||
|
VERIFY(to_be_removed_node.parent());
|
||||||
|
m_reference = *to_be_removed_node.parent();
|
||||||
|
} else {
|
||||||
|
// ...and to the inclusive descendant of toBeRemovedNode’s previous sibling that appears last in tree order otherwise.
|
||||||
|
auto* node = to_be_removed_node.previous_sibling();
|
||||||
|
while (node->last_child())
|
||||||
|
node = node->last_child();
|
||||||
|
m_reference = *node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ class NodeIterator
|
||||||
public:
|
public:
|
||||||
using WrapperType = Bindings::NodeIteratorWrapper;
|
using WrapperType = Bindings::NodeIteratorWrapper;
|
||||||
|
|
||||||
|
virtual ~NodeIterator() override;
|
||||||
|
|
||||||
static NonnullRefPtr<NodeIterator> create(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
static NonnullRefPtr<NodeIterator> create(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||||
|
|
||||||
NonnullRefPtr<Node> root() { return m_root; }
|
NonnullRefPtr<Node> root() { return m_root; }
|
||||||
|
@ -33,6 +35,8 @@ public:
|
||||||
|
|
||||||
void detach();
|
void detach();
|
||||||
|
|
||||||
|
void run_pre_removing_steps(Node&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeIterator(Node& root);
|
NodeIterator(Node& root);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue