Files
ladybird/Libraries/LibWeb/CSS/InvalidationSet.h
Andreas Kling 97e2b05004 LibWeb: Merge equivalent style invalidation plans
Compare invalidation sets, rules, and plans structurally so repeated
descendant and sibling invalidation entries can be merged even when
they were built as separate payload objects.

Also deduplicate pending and active descendant invalidations in the
style invalidator so equivalent rules are not re-applied as the DOM
walk descends. This reduces :has() invalidation fanout while keeping
behavior the same.
2026-04-20 13:20:41 +02:00

86 lines
2.8 KiB
C++

/*
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <AK/Format.h>
#include <AK/Forward.h>
#include <AK/HashTable.h>
#include <AK/Traits.h>
#include <LibWeb/CSS/PseudoClass.h>
namespace Web::CSS {
class InvalidationSet {
public:
struct Property {
enum class Type : u8 {
InvalidateSelf,
InvalidateWholeSubtree,
Class,
Id,
TagName,
Attribute,
PseudoClass,
};
Type type;
Variant<FlyString, PseudoClass, Empty> value { Empty {} };
FlyString const& name() const { return value.get<FlyString>(); }
bool operator==(Property const& other) const = default;
};
void include_all_from(InvalidationSet const& other);
bool needs_invalidate_self() const { return m_needs_invalidate_self; }
void set_needs_invalidate_self() { m_needs_invalidate_self = true; }
bool needs_invalidate_whole_subtree() const { return m_needs_invalidate_whole_subtree; }
void set_needs_invalidate_whole_subtree() { m_needs_invalidate_whole_subtree = true; }
bool operator==(InvalidationSet const& other) const;
void set_needs_invalidate_class(FlyString const& name) { m_properties.set({ Property::Type::Class, name }); }
void set_needs_invalidate_id(FlyString const& name) { m_properties.set({ Property::Type::Id, name }); }
void set_needs_invalidate_tag_name(FlyString const& name) { m_properties.set({ Property::Type::TagName, name }); }
void set_needs_invalidate_attribute(FlyString const& name) { m_properties.set({ Property::Type::Attribute, name }); }
void set_needs_invalidate_pseudo_class(PseudoClass pseudo_class) { m_properties.set({ Property::Type::PseudoClass, pseudo_class }); }
bool is_empty() const;
bool has_properties() const { return !m_properties.is_empty(); }
void for_each_property(Function<IterationDecision(Property const&)> const& callback) const;
private:
bool m_needs_invalidate_self { false };
bool m_needs_invalidate_whole_subtree { false };
HashTable<Property> m_properties;
};
}
namespace AK {
template<>
struct Traits<Web::CSS::InvalidationSet::Property> : DefaultTraits<String> {
static unsigned hash(Web::CSS::InvalidationSet::Property const&);
static bool equals(Web::CSS::InvalidationSet::Property const& a, Web::CSS::InvalidationSet::Property const& b) { return a == b; }
};
template<>
struct Formatter<Web::CSS::InvalidationSet::Property> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder&, Web::CSS::InvalidationSet::Property const&);
};
template<>
struct Formatter<Web::CSS::InvalidationSet> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder&, Web::CSS::InvalidationSet const&);
};
}