mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-28 12:10:28 +00:00
Compound selectors with a non-rightmost pseudo-class used to register their descendant and sibling invalidation plans directly under the pseudo-class property. A selector like `.item:hover * .target` ran the descendant plan for every hovered element, even when that element could not match `.item`. Store those non-rightmost plans behind a guard made from stable class and id subject features in the same compound selector. Deliberately leave pseudo-classes out of guards, since one mutation can change multiple state pseudo-classes at once. Also leave tag and attribute selectors out, since their matching depends on document and namespace case-sensitivity that the guard property does not carry. The new style-invalidation test covers the GitHub-shaped `:hover * :not(...)` case, co-invalidated `:link` and `:any-link`, partial or complex `:is()`/`:where()` alternatives, and case-sensitive SVG tag and attribute selectors.
113 lines
3.3 KiB
C++
113 lines
3.3 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,
|
|
};
|
|
|
|
enum class HasArgumentScope : u8 {
|
|
ChildrenOnly,
|
|
AllDescendants,
|
|
NextSiblingOnly,
|
|
AllFollowingSiblings,
|
|
Complex,
|
|
};
|
|
|
|
struct InvalidationPlan;
|
|
|
|
struct InvalidationGuard {
|
|
bool is_empty() const { return property_sets.is_empty(); }
|
|
bool operator==(InvalidationGuard const&) const;
|
|
|
|
// Every set is an OR group; the guard matches when every group matches.
|
|
Vector<InvalidationSet> property_sets;
|
|
};
|
|
|
|
struct GuardedInvalidationRule {
|
|
InvalidationGuard guard;
|
|
NonnullRefPtr<InvalidationPlan> payload;
|
|
|
|
bool operator==(GuardedInvalidationRule const&) const;
|
|
};
|
|
|
|
struct DescendantInvalidationRule {
|
|
InvalidationSet match_set;
|
|
bool match_any { false };
|
|
NonnullRefPtr<InvalidationPlan> payload;
|
|
|
|
bool operator==(DescendantInvalidationRule const&) const;
|
|
};
|
|
|
|
enum class SiblingInvalidationReach {
|
|
Adjacent,
|
|
Subsequent,
|
|
};
|
|
|
|
struct SiblingInvalidationRule {
|
|
SiblingInvalidationReach reach;
|
|
InvalidationSet match_set;
|
|
bool match_any { false };
|
|
NonnullRefPtr<InvalidationPlan> payload;
|
|
|
|
bool operator==(SiblingInvalidationRule const&) const;
|
|
};
|
|
|
|
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 operator==(InvalidationPlan const&) const;
|
|
|
|
bool invalidate_self { false };
|
|
bool invalidate_whole_subtree { false };
|
|
Vector<DescendantInvalidationRule> descendant_rules;
|
|
Vector<SiblingInvalidationRule> sibling_rules;
|
|
Vector<GuardedInvalidationRule> guarded_rules;
|
|
};
|
|
|
|
struct HasInvalidationMetadata {
|
|
Selector const* relative_selector { nullptr };
|
|
HasArgumentScope scope { HasArgumentScope::Complex };
|
|
|
|
bool operator==(HasInvalidationMetadata const&) const = default;
|
|
};
|
|
|
|
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;
|
|
HashMap<FlyString, Vector<HasInvalidationMetadata>> ids_used_in_has_selectors;
|
|
HashMap<FlyString, Vector<HasInvalidationMetadata>> class_names_used_in_has_selectors;
|
|
HashMap<FlyString, Vector<HasInvalidationMetadata>> attribute_names_used_in_has_selectors;
|
|
HashMap<FlyString, Vector<HasInvalidationMetadata>> tag_names_used_in_has_selectors;
|
|
HashMap<PseudoClass, Vector<HasInvalidationMetadata>> pseudo_classes_used_in_has_selectors;
|
|
bool has_selectors_sensitive_to_featureless_subtree_changes { false };
|
|
|
|
void build_invalidation_sets_for_selector(Selector const& selector);
|
|
};
|
|
|
|
}
|