mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibWeb: Avoid full tree traversal in Node::compare_document_position()
Introduce optimization that determines if one node is preceding, following, ancestor or descdendant of another node by comparing ancestors chains of both nodes which is much cheaper than using Node::is_before() that can walk whole subtree in the worst case.
This commit is contained in:
parent
884d8b14ac
commit
e5618b17c4
Notes:
sideshowbarker
2024-07-17 08:38:37 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/e5618b17c4 Pull-request: https://github.com/SerenityOS/serenity/pull/19003 Reviewed-by: https://github.com/awesomekling
1 changed files with 39 additions and 5 deletions
|
@ -968,8 +968,8 @@ u16 Node::compare_document_position(JS::GCPtr<Node> other)
|
|||
Node* node2 = this;
|
||||
|
||||
// 3. Let attr1 and attr2 be null.
|
||||
Attr* attr1;
|
||||
Attr* attr2;
|
||||
Attr* attr1 = nullptr;
|
||||
Attr* attr2 = nullptr;
|
||||
|
||||
// 4. If node1 is an attribute, then set attr1 to node1 and node1 to attr1’s element.
|
||||
if (is<Attr>(node1)) {
|
||||
|
@ -996,16 +996,50 @@ u16 Node::compare_document_position(JS::GCPtr<Node> other)
|
|||
if ((node1 == nullptr || node2 == nullptr) || (&node1->root() != &node2->root()))
|
||||
return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | (node1 > node2 ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING);
|
||||
|
||||
Vector<Node*> node1_ancestors;
|
||||
for (auto* node = node1; node; node = node->parent())
|
||||
node1_ancestors.append(node);
|
||||
|
||||
Vector<Node*> node2_ancestors;
|
||||
for (auto* node = node2; node; node = node->parent())
|
||||
node2_ancestors.append(node);
|
||||
|
||||
auto it_node1_ancestors = node1_ancestors.rbegin();
|
||||
auto it_node2_ancestors = node2_ancestors.rbegin();
|
||||
// Walk ancestor chains of both nodes starting from root
|
||||
while (it_node1_ancestors != node1_ancestors.rend() && it_node2_ancestors != node2_ancestors.rend()) {
|
||||
auto* ancestor1 = *it_node1_ancestors;
|
||||
auto* ancestor2 = *it_node2_ancestors;
|
||||
|
||||
// If ancestors of nodes at the same level in the tree are different then preceding node is the one with lower sibling position
|
||||
if (ancestor1 != ancestor2) {
|
||||
auto* node = ancestor1;
|
||||
while (node) {
|
||||
if (node == ancestor2)
|
||||
return DOCUMENT_POSITION_PRECEDING;
|
||||
node = node->next_sibling();
|
||||
}
|
||||
return DOCUMENT_POSITION_FOLLOWING;
|
||||
}
|
||||
|
||||
it_node1_ancestors++;
|
||||
it_node2_ancestors++;
|
||||
}
|
||||
|
||||
// NOTE: If nodes in ancestors chains are the same but one chain is longer, then one node is ancestor of another.
|
||||
// The node with shorter ancestors chain is the ancestor.
|
||||
// The node with longer ancestors chain is the descendant.
|
||||
|
||||
// 7. If node1 is an ancestor of node2 and attr1 is null, or node1 is node2 and attr2 is non-null, then return the result of adding DOCUMENT_POSITION_CONTAINS to DOCUMENT_POSITION_PRECEDING.
|
||||
if ((node1->is_ancestor_of(*node2) && !attr1) || (node1 == node2 && attr2))
|
||||
if ((node1_ancestors.size() < node2_ancestors.size() && !attr1) || (node1 == node2 && attr2))
|
||||
return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING;
|
||||
|
||||
// 8. If node1 is a descendant of node2 and attr2 is null, or node1 is node2 and attr1 is non-null, then return the result of adding DOCUMENT_POSITION_CONTAINED_BY to DOCUMENT_POSITION_FOLLOWING.
|
||||
if ((node2->is_ancestor_of(*node1) && !attr2) || (node1 == node2 && attr1))
|
||||
if ((node1_ancestors.size() > node2_ancestors.size() && !attr2) || (node1 == node2 && attr1))
|
||||
return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING;
|
||||
|
||||
// 9. If node1 is preceding node2, then return DOCUMENT_POSITION_PRECEDING.
|
||||
if (node1->is_before(*node2))
|
||||
if (node1_ancestors.size() < node2_ancestors.size())
|
||||
return DOCUMENT_POSITION_PRECEDING;
|
||||
|
||||
// 10. Return DOCUMENT_POSITION_FOLLOWING.
|
||||
|
|
Loading…
Reference in a new issue