LibWeb: Use invalidation sets to reduce style recalculation

Implements idea described in
https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8

Invalidation sets are used to reduce the number of elements marked for
style recalculation by collecting metadata from style rules about the
dependencies between properties that could affect an element’s style.

Currently, this optimization is only applied to style invalidation
triggered by class list mutations on an element.
This commit is contained in:
Aliaksandr Kalenik 2025-01-12 18:38:05 +03:00 committed by Andreas Kling
parent 58c78cb003
commit c5f2a88f69
Notes: github-actions[bot] 2025-01-19 18:55:55 +00:00
11 changed files with 549 additions and 3 deletions

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CSS/InvalidationSet.h>
namespace Web::CSS {
void InvalidationSet::include_all_from(InvalidationSet const& other)
{
m_needs_invalidate_self |= other.m_needs_invalidate_self;
m_needs_invalidate_whole_subtree |= other.m_needs_invalidate_whole_subtree;
for (auto const& property : other.m_properties)
m_properties.set(property);
}
bool InvalidationSet::is_empty() const
{
return !m_needs_invalidate_self && !m_needs_invalidate_whole_subtree && m_properties.is_empty();
}
void InvalidationSet::for_each_property(Function<void(Property const&)> const& callback) const
{
if (m_needs_invalidate_self)
callback({ Property::Type::InvalidateSelf });
if (m_needs_invalidate_whole_subtree)
callback({ Property::Type::InvalidateWholeSubtree });
for (auto const& property : m_properties)
callback(property);
}
}
namespace AK {
unsigned Traits<Web::CSS::InvalidationSet::Property>::hash(Web::CSS::InvalidationSet::Property const& invalidation_set_property)
{
return pair_int_hash(to_underlying(invalidation_set_property.type), invalidation_set_property.name.hash());
}
ErrorOr<void> Formatter<Web::CSS::InvalidationSet::Property>::format(FormatBuilder& builder, Web::CSS::InvalidationSet::Property const& invalidation_set_property)
{
switch (invalidation_set_property.type) {
case Web::CSS::InvalidationSet::Property::Type::InvalidateSelf: {
TRY(builder.put_string("$"sv));
return {};
}
case Web::CSS::InvalidationSet::Property::Type::Class: {
TRY(builder.put_string("."sv));
TRY(builder.put_string(invalidation_set_property.name));
return {};
}
case Web::CSS::InvalidationSet::Property::Type::Id: {
TRY(builder.put_string("#"sv));
TRY(builder.put_string(invalidation_set_property.name));
return {};
}
case Web::CSS::InvalidationSet::Property::Type::TagName: {
TRY(builder.put_string(invalidation_set_property.name));
return {};
}
case Web::CSS::InvalidationSet::Property::Type::Attribute: {
TRY(builder.put_string("["sv));
TRY(builder.put_string(invalidation_set_property.name));
TRY(builder.put_string("]"sv));
return {};
}
case Web::CSS::InvalidationSet::Property::Type::InvalidateWholeSubtree: {
TRY(builder.put_string("*"sv));
return {};
}
default:
VERIFY_NOT_REACHED();
}
}
ErrorOr<void> Formatter<Web::CSS::InvalidationSet>::format(FormatBuilder& builder, Web::CSS::InvalidationSet const& invalidation_set)
{
bool first = true;
invalidation_set.for_each_property([&](auto const& property) {
if (!first)
builder.builder().append(", "sv);
builder.builder().appendff("{}", property);
});
return {};
}
}