ladybird/Libraries/LibWeb/CSS/StyleInvalidationData.h
Aliaksandr Kalenik 9df1372452 LibWeb: Implement sibling invalidation sets
Replace flat InvalidationSet with recursive InvalidationPlan trees
that preserve selector combinator structure. Previously, selectors
with sibling combinators (+ and ~) fell back to whole-subtree
invalidation. Now the StyleInvalidator walks the DOM following
combinator-specific rules, so ".a + .b" only invalidates the
adjacent sibling matching ".b" rather than the entire subtree.

Plans are compiled at stylesheet parse time by walking selector
compounds right-to-left. For ".a .b + .c":
```
  [.c]: plan = { invalidate_self }
        register: "c" → plan

  [.b]: wrap("+", righthand)
        plan = { sibling_rules: [match ".c", adjacent, {self}] }
        register: "b" → plan

  [.a]: wrap(" ", righthand)
        plan = { descendant_rules: [match ".b", <sibling plan>] }
        register: "a" → plan
```

Changing class "a" produces a plan that walks descendants for ".b",
checks ".b"'s adjacent sibling for ".c", and invalidates only that
element.
2026-03-09 18:35:46 +01:00

76 lines
2 KiB
C++

/*
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
#include <AK/Vector.h>
#include <LibWeb/CSS/InvalidationSet.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWeb/Forward.h>
namespace Web::CSS {
enum class ExcludePropertiesNestedInNotPseudoClass : bool {
No,
Yes,
};
enum class InsideNthChildPseudoClass {
No,
Yes,
};
struct InvalidationPlan;
struct DescendantInvalidationRule {
InvalidationSet match_set;
bool match_any { false };
NonnullRefPtr<InvalidationPlan> payload;
};
enum class SiblingInvalidationReach {
Adjacent,
Subsequent,
};
struct SiblingInvalidationRule {
SiblingInvalidationReach reach;
InvalidationSet match_set;
bool match_any { false };
NonnullRefPtr<InvalidationPlan> payload;
};
struct InvalidationPlan final : RefCounted<InvalidationPlan> {
static NonnullRefPtr<InvalidationPlan> create() { return adopt_ref(*new InvalidationPlan); }
bool is_empty() const;
void include_all_from(InvalidationPlan const&);
bool invalidate_self { false };
bool invalidate_whole_subtree { false };
Vector<DescendantInvalidationRule> descendant_rules;
Vector<SiblingInvalidationRule> sibling_rules;
};
struct StyleInvalidationData;
void build_invalidation_sets_for_simple_selector(Selector::SimpleSelector const&, InvalidationSet&, ExcludePropertiesNestedInNotPseudoClass, StyleInvalidationData&, InsideNthChildPseudoClass);
struct StyleInvalidationData {
HashMap<InvalidationSet::Property, NonnullRefPtr<InvalidationPlan>> invalidation_plans;
HashTable<FlyString> ids_used_in_has_selectors;
HashTable<FlyString> class_names_used_in_has_selectors;
HashTable<FlyString> attribute_names_used_in_has_selectors;
HashTable<FlyString> tag_names_used_in_has_selectors;
HashTable<PseudoClass> pseudo_classes_used_in_has_selectors;
void build_invalidation_sets_for_selector(Selector const& selector);
};
}