mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 12:35:14 +00:00
LibWeb: Defer entire-subtree style invalidations
Instead of traversing the entire DOM subtrees and marking nodes for style update, this patch adds a new mechanism where we can mark a subtree root as "entire subtree needs style update". A new pass in Document::update_style() then takes care of coalescing all these invalidations in a single traversal of the DOM. This shaves *minutes* of loading time off of https://wpt.fyi/ subpages.
This commit is contained in:
parent
84c4702b10
commit
f35152cf61
Notes:
github-actions[bot]
2025-01-26 21:59:34 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/f35152cf61d Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3371
3 changed files with 45 additions and 18 deletions
|
@ -1382,6 +1382,34 @@ void Document::update_layout()
|
|||
return invalidation;
|
||||
}
|
||||
|
||||
// This function makes a full pass over the entire DOM and converts "entire subtree needs style update"
|
||||
// into "needs style update" for each inclusive descendant where it's found.
|
||||
static void perform_pending_style_invalidations(Node& node, bool invalidate_entire_subtree)
|
||||
{
|
||||
invalidate_entire_subtree |= node.entire_subtree_needs_style_update();
|
||||
|
||||
if (invalidate_entire_subtree) {
|
||||
node.set_needs_style_update_internal(true);
|
||||
if (node.has_child_nodes())
|
||||
node.set_child_needs_style_update(true);
|
||||
}
|
||||
|
||||
for (auto* child = node.first_child(); child; child = child->next_sibling()) {
|
||||
perform_pending_style_invalidations(*child, invalidate_entire_subtree);
|
||||
}
|
||||
|
||||
if (node.is_element()) {
|
||||
auto& element = static_cast<Element&>(node);
|
||||
if (auto shadow_root = element.shadow_root()) {
|
||||
perform_pending_style_invalidations(*shadow_root, invalidate_entire_subtree);
|
||||
if (invalidate_entire_subtree)
|
||||
node.set_child_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
||||
node.set_entire_subtree_needs_style_update(false);
|
||||
}
|
||||
|
||||
void Document::update_style()
|
||||
{
|
||||
if (!browsing_context())
|
||||
|
@ -1396,6 +1424,8 @@ void Document::update_style()
|
|||
if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
|
||||
return;
|
||||
|
||||
perform_pending_style_invalidations(*this, false);
|
||||
|
||||
// NOTE: If this is a document hosting <template> contents, style update is unnecessary.
|
||||
if (m_created_for_appropriate_template_contents)
|
||||
return;
|
||||
|
|
|
@ -429,6 +429,12 @@ void Node::invalidate_style(StyleInvalidationReason reason)
|
|||
return;
|
||||
}
|
||||
|
||||
// If any ancestor is already marked for an entire subtree update, there's no need to do anything here.
|
||||
for (auto* ancestor = this; ancestor; ancestor = ancestor->parent_or_shadow_host()) {
|
||||
if (ancestor->entire_subtree_needs_style_update())
|
||||
return;
|
||||
}
|
||||
|
||||
// When invalidating style for a node, we actually invalidate:
|
||||
// - the node itself
|
||||
// - all of its descendants
|
||||
|
@ -436,37 +442,23 @@ void Node::invalidate_style(StyleInvalidationReason reason)
|
|||
// - all of its subsequent siblings and their descendants
|
||||
// FIXME: This is a lot of invalidation and we should implement more sophisticated invalidation to do less work!
|
||||
|
||||
auto invalidate_entire_subtree = [&](Node& subtree_root) {
|
||||
subtree_root.for_each_in_inclusive_subtree([&](Node& node) {
|
||||
node.m_needs_style_update = true;
|
||||
if (node.has_children())
|
||||
node.m_child_needs_style_update = true;
|
||||
if (auto shadow_root = node.is_element() ? static_cast<DOM::Element&>(node).shadow_root() : nullptr) {
|
||||
node.m_child_needs_style_update = true;
|
||||
shadow_root->m_needs_style_update = true;
|
||||
if (shadow_root->has_children())
|
||||
shadow_root->m_child_needs_style_update = true;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
};
|
||||
|
||||
invalidate_entire_subtree(*this);
|
||||
set_entire_subtree_needs_style_update(true);
|
||||
|
||||
if (reason == StyleInvalidationReason::NodeInsertBefore || reason == StyleInvalidationReason::NodeRemove) {
|
||||
for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
|
||||
if (sibling->is_element())
|
||||
invalidate_entire_subtree(*sibling);
|
||||
sibling->set_entire_subtree_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
|
||||
if (sibling->is_element())
|
||||
invalidate_entire_subtree(*sibling);
|
||||
sibling->set_entire_subtree_needs_style_update(true);
|
||||
}
|
||||
|
||||
for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host())
|
||||
ancestor->m_child_needs_style_update = true;
|
||||
|
||||
document().schedule_style_update();
|
||||
}
|
||||
|
||||
|
|
|
@ -290,6 +290,7 @@ public:
|
|||
|
||||
bool needs_style_update() const { return m_needs_style_update; }
|
||||
void set_needs_style_update(bool);
|
||||
void set_needs_style_update_internal(bool) { m_needs_style_update = true; }
|
||||
|
||||
bool needs_inherited_style_update() const { return m_needs_inherited_style_update; }
|
||||
void set_needs_inherited_style_update(bool);
|
||||
|
@ -297,6 +298,9 @@ public:
|
|||
bool child_needs_style_update() const { return m_child_needs_style_update; }
|
||||
void set_child_needs_style_update(bool b) { m_child_needs_style_update = b; }
|
||||
|
||||
[[nodiscard]] bool entire_subtree_needs_style_update() const { return m_entire_subtree_needs_style_update; }
|
||||
void set_entire_subtree_needs_style_update(bool b) { m_entire_subtree_needs_style_update = b; }
|
||||
|
||||
enum class ForceSelfStyleInvalidation : bool {
|
||||
Yes,
|
||||
No
|
||||
|
@ -807,6 +811,7 @@ protected:
|
|||
bool m_needs_style_update { false };
|
||||
bool m_needs_inherited_style_update { false };
|
||||
bool m_child_needs_style_update { false };
|
||||
bool m_entire_subtree_needs_style_update { false };
|
||||
|
||||
UniqueNodeID m_unique_id;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue