LibWeb: Skip full document style update in getComputedStyle if possible
Before calling update_style() for a getComputedStyle property access, we now check whether the target element actually needs a style update by walking the flat tree ancestor chain. If neither the element nor any of its ancestors have dirty style bits, and there are no document-level reasons to recalculate style, we skip the update_style() call entirely. We walk the flat tree (not the DOM tree) because style inheritance follows slot assignment -- slotted elements inherit from their assigned slot, not their DOM parent. This avoids unnecessary work when scripts access computed style properties on elements whose styles are already up-to-date, which is a common pattern on the web.
This commit is contained in:
parent
b270b2cacb
commit
4a7ca32af0
3 changed files with 36 additions and 1 deletions
|
|
@ -560,7 +560,7 @@ Optional<StyleProperty> CSSStyleProperties::get_direct_property(PropertyNameAndI
|
|||
// always need update_layout() to ensure both style and layout tree are up to date.
|
||||
abstract_element.document().update_layout(DOM::UpdateLayoutReason::ResolvedCSSStyleDeclarationProperty);
|
||||
layout_node = abstract_element.layout_node();
|
||||
} else {
|
||||
} else if (abstract_element.document().element_needs_style_update(abstract_element)) {
|
||||
// Just ensure styles are up to date.
|
||||
abstract_element.document().update_style();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Policy.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
|
||||
#include <LibWeb/DOM/AbstractElement.h>
|
||||
#include <LibWeb/DOM/AccessibilityTreeNode.h>
|
||||
#include <LibWeb/DOM/AdoptedStyleSheets.h>
|
||||
#include <LibWeb/DOM/Attr.h>
|
||||
|
|
@ -1741,6 +1742,39 @@ void Document::update_style()
|
|||
m_needs_full_style_update = false;
|
||||
}
|
||||
|
||||
bool Document::element_needs_style_update(AbstractElement const& abstract_element) const
|
||||
{
|
||||
// If there are document-level reasons to update style, we can't skip.
|
||||
if (m_needs_full_style_update)
|
||||
return true;
|
||||
if (m_needs_animated_style_update)
|
||||
return true;
|
||||
if (m_needs_invalidation_of_elements_affected_by_has)
|
||||
return true;
|
||||
if (m_style_invalidator->has_pending_invalidations())
|
||||
return true;
|
||||
|
||||
// Check the element itself.
|
||||
if (abstract_element.element().needs_style_update())
|
||||
return true;
|
||||
if (abstract_element.element().entire_subtree_needs_style_update())
|
||||
return true;
|
||||
|
||||
// Walk the inheritance ancestor chain. We use element_to_inherit_style_from()
|
||||
// because style inheritance follows the flat tree (slotted elements inherit
|
||||
// from their assigned slot, not their DOM parent). If any ancestor on the
|
||||
// path has its style marked dirty, the target element's computed style could
|
||||
// change via inherited properties, so we must update.
|
||||
for (auto ancestor = abstract_element.element_to_inherit_style_from(); ancestor.has_value(); ancestor = ancestor->element_to_inherit_style_from()) {
|
||||
if (ancestor->element().needs_style_update())
|
||||
return true;
|
||||
if (ancestor->element().entire_subtree_needs_style_update())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Document::update_animated_style_if_needed()
|
||||
{
|
||||
if (!m_needs_animated_style_update)
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ public:
|
|||
void obtain_theme_color();
|
||||
|
||||
void update_style();
|
||||
[[nodiscard]] bool element_needs_style_update(AbstractElement const&) const;
|
||||
void update_layout(UpdateLayoutReason);
|
||||
void update_paint_and_hit_testing_properties_if_needed();
|
||||
void update_animated_style_if_needed();
|
||||
|
|
|
|||
Loading…
Reference in a new issue