Skip to content

Commit 66263f1

Browse files
committed
LibWeb: Add StyleScope to keep style caches per Document/ShadowRoot
Before this change, we've been maintaining various StyleComputer caches at the document level. This made sense for old-school documents without shadow trees, since all the style information was document-wide anyway. However, documents with many shadow trees ended up suffering since any time you mutated a style sheet inside a shadow tree, *all* style caches for the entire document would get invalidated. This was particularly expensive on Reddit, which has tons of shadow trees with their own style elements. Every time we'd create one of their custom elements, we'd invalidate the document-level "rule cache" and have to rebuild it, taking about ~60ms each time (ouch). This commit introduces a new object called StyleScope. Every Document and ShadowRoot has its own StyleScope. Rule caches etc are moved from StyleComputer to StyleScope. Rule cache invalidation now happens at StyleScope level. As an example, rule cache rebuilds now take ~1ms on Reddit instead of ~60ms. This is largely a mechanical change, moving things around, but there's one key detail to be aware of: due to the :host selector, which works across the shadow DOM boundary and reaches from inside a shadow tree out into the light tree, there are various places where we have to check both the shadow tree's StyleScope *and* the document-level StyleScope in order to get all rules that may apply.
1 parent 70b5496 commit 66263f1

21 files changed

+734
-561
lines changed

Libraries/LibWeb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ set(SOURCES
215215
CSS/StyleProperty.cpp
216216
CSS/StylePropertyMapReadOnly.cpp
217217
CSS/StylePropertyMap.cpp
218+
CSS/StyleScope.cpp
218219
CSS/StyleSheet.cpp
219220
CSS/StyleSheetIdentifier.cpp
220221
CSS/StyleSheetList.cpp

Libraries/LibWeb/CSS/CSSLayerBlockRule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ String CSSLayerBlockRule::serialized() const
6969
return builder.to_string_without_validation();
7070
}
7171

72-
FlyString CSSLayerBlockRule::internal_qualified_name(Badge<StyleComputer>) const
72+
FlyString CSSLayerBlockRule::internal_qualified_name(Badge<StyleScope>) const
7373
{
7474
auto const& parent_name = parent_layer_internal_qualified_name();
7575
if (parent_name.is_empty())

Libraries/LibWeb/CSS/CSSLayerBlockRule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class CSSLayerBlockRule final : public CSSGroupingRule {
2424

2525
FlyString const& name() const { return m_name; }
2626
FlyString const& internal_name() const { return m_name_internal; }
27-
FlyString internal_qualified_name(Badge<StyleComputer>) const;
27+
FlyString internal_qualified_name(Badge<StyleScope>) const;
2828

2929
private:
3030
CSSLayerBlockRule(JS::Realm&, FlyString name, CSSRuleList&);

Libraries/LibWeb/CSS/CSSLayerStatementRule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ String CSSLayerStatementRule::serialized() const
4040
return builder.to_string_without_validation();
4141
}
4242

43-
Vector<FlyString> CSSLayerStatementRule::internal_qualified_name_list(Badge<StyleComputer>) const
43+
Vector<FlyString> CSSLayerStatementRule::internal_qualified_name_list(Badge<StyleScope>) const
4444
{
4545
Vector<FlyString> qualified_layer_names;
4646

Libraries/LibWeb/CSS/CSSLayerStatementRule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CSSLayerStatementRule final : public CSSRule {
2222

2323
// FIXME: Should be FrozenArray
2424
ReadonlySpan<FlyString> name_list() const { return m_name_list; }
25-
Vector<FlyString> internal_qualified_name_list(Badge<StyleComputer>) const;
25+
Vector<FlyString> internal_qualified_name_list(Badge<StyleScope>) const;
2626

2727
private:
2828
CSSLayerStatementRule(JS::Realm&, Vector<FlyString> name_list);

Libraries/LibWeb/CSS/CSSStyleSheet.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ void CSSStyleSheet::invalidate_owners(DOM::StyleInvalidationReason reason)
353353
m_did_match = {};
354354
for (auto& document_or_shadow_root : m_owning_documents_or_shadow_roots) {
355355
document_or_shadow_root->invalidate_style(reason);
356-
document_or_shadow_root->document().style_computer().invalidate_rule_cache();
356+
auto& style_scope = document_or_shadow_root->is_shadow_root() ? as<DOM::ShadowRoot>(*document_or_shadow_root).style_scope() : document_or_shadow_root->document().style_scope();
357+
style_scope.invalidate_rule_cache();
357358
}
358359
}
359360

Libraries/LibWeb/CSS/StyleComputer.cpp

Lines changed: 26 additions & 383 deletions
Large diffs are not rendered by default.

Libraries/LibWeb/CSS/StyleComputer.h

Lines changed: 5 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <LibWeb/CSS/CascadedProperties.h>
2121
#include <LibWeb/CSS/Selector.h>
2222
#include <LibWeb/CSS/StyleInvalidationData.h>
23+
#include <LibWeb/CSS/StyleScope.h>
2324
#include <LibWeb/Export.h>
2425
#include <LibWeb/Forward.h>
2526
#include <LibWeb/Loader/ResourceLoader.h>
@@ -75,26 +76,6 @@ class CountingBloomFilter {
7576
CounterType m_buckets[bucket_count];
7677
};
7778

78-
struct MatchingRule {
79-
GC::Ptr<DOM::ShadowRoot const> shadow_root;
80-
GC::Ptr<CSSRule const> rule; // Either CSSStyleRule or CSSNestedDeclarations
81-
GC::Ptr<CSSStyleSheet const> sheet;
82-
Optional<FlyString> default_namespace;
83-
Selector const& selector;
84-
size_t style_sheet_index { 0 };
85-
size_t rule_index { 0 };
86-
87-
u32 specificity { 0 };
88-
CascadeOrigin cascade_origin;
89-
bool contains_pseudo_element { false };
90-
bool slotted { false };
91-
92-
// Helpers to deal with the fact that `rule` might be a CSSStyleRule or a CSSNestedDeclarations
93-
CSSStyleProperties const& declaration() const;
94-
SelectorList const& absolutized_selectors() const;
95-
FlyString const& qualified_layer_name() const;
96-
};
97-
9879
struct FontFaceKey;
9980

10081
struct OwnFontFaceKey {
@@ -120,22 +101,6 @@ struct FontMatchingAlgorithmCacheKey {
120101
[[nodiscard]] bool operator==(FontMatchingAlgorithmCacheKey const& other) const = default;
121102
};
122103

123-
struct RuleCache {
124-
HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
125-
HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
126-
HashMap<FlyString, Vector<MatchingRule>> rules_by_tag_name;
127-
HashMap<FlyString, Vector<MatchingRule>, AK::ASCIICaseInsensitiveFlyStringTraits> rules_by_attribute_name;
128-
Array<Vector<MatchingRule>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount)> rules_by_pseudo_element;
129-
Vector<MatchingRule> root_rules;
130-
Vector<MatchingRule> slotted_rules;
131-
Vector<MatchingRule> other_rules;
132-
133-
HashMap<FlyString, NonnullRefPtr<Animations::KeyframeEffect::KeyFrameSet>> rules_by_animation_keyframes;
134-
135-
void add_rule(MatchingRule const&, Optional<PseudoElement>, bool contains_root_pseudo_class);
136-
void for_each_matching_rules(DOM::AbstractElement, Function<IterationDecision(Vector<MatchingRule> const&)> callback) const;
137-
};
138-
139104
class FontLoader;
140105

141106
class WEB_API StyleComputer final : public GC::Cell {
@@ -164,15 +129,10 @@ class WEB_API StyleComputer final : public GC::Cell {
164129
[[nodiscard]] GC::Ref<ComputedProperties> compute_style(DOM::AbstractElement, Optional<bool&> did_change_custom_properties = {}) const;
165130
[[nodiscard]] GC::Ptr<ComputedProperties> compute_pseudo_element_style_if_needed(DOM::AbstractElement, Optional<bool&> did_change_custom_properties) const;
166131

167-
[[nodiscard]] RuleCache const& get_pseudo_class_rule_cache(PseudoClass) const;
168-
169132
[[nodiscard]] Vector<MatchingRule const*> collect_matching_rules(DOM::AbstractElement, CascadeOrigin, PseudoClassBitmap& attempted_pseudo_class_matches, Optional<FlyString const> qualified_layer_name = {}) const;
170133

171-
InvalidationSet invalidation_set_for_properties(Vector<InvalidationSet::Property> const&) const;
172-
bool invalidation_property_used_in_has_selector(InvalidationSet::Property const&) const;
173-
174-
[[nodiscard]] bool has_valid_rule_cache() const { return m_author_rule_cache; }
175-
void invalidate_rule_cache();
134+
InvalidationSet invalidation_set_for_properties(Vector<InvalidationSet::Property> const&, StyleScope const&) const;
135+
bool invalidation_property_used_in_has_selector(InvalidationSet::Property const&, StyleScope const&) const;
176136

177137
Gfx::Font const& initial_font() const;
178138

@@ -193,9 +153,6 @@ class WEB_API StyleComputer final : public GC::Cell {
193153

194154
void collect_animation_into(DOM::AbstractElement, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties&) const;
195155

196-
[[nodiscard]] bool may_have_has_selectors() const;
197-
[[nodiscard]] bool have_has_selectors() const;
198-
199156
size_t number_of_css_font_faces_with_loading_in_progress() const;
200157

201158
[[nodiscard]] GC::Ref<ComputedProperties> compute_properties(DOM::AbstractElement, CascadedProperties&) const;
@@ -242,10 +199,10 @@ class WEB_API StyleComputer final : public GC::Cell {
242199
Vector<LayerMatchingRules> author_rules;
243200
};
244201

245-
[[nodiscard]] MatchingRuleSet build_matching_rule_set(DOM::AbstractElement, PseudoClassBitmap& attempted_pseudo_class_matches, bool& did_match_any_pseudo_element_rules, ComputeStyleMode) const;
202+
[[nodiscard]] MatchingRuleSet build_matching_rule_set(DOM::AbstractElement, PseudoClassBitmap& attempted_pseudo_class_matches, bool& did_match_any_pseudo_element_rules, ComputeStyleMode, StyleScope const&) const;
246203

247204
LogicalAliasMappingContext compute_logical_alias_mapping_context(DOM::AbstractElement, ComputeStyleMode, MatchingRuleSet const&) const;
248-
[[nodiscard]] GC::Ptr<ComputedProperties> compute_style_impl(DOM::AbstractElement, ComputeStyleMode, Optional<bool&> did_change_custom_properties) const;
205+
[[nodiscard]] GC::Ptr<ComputedProperties> compute_style_impl(DOM::AbstractElement, ComputeStyleMode, Optional<bool&> did_change_custom_properties, StyleScope const&) const;
249206
[[nodiscard]] GC::Ref<CascadedProperties> compute_cascaded_values(DOM::AbstractElement, bool did_match_any_pseudo_element_rules, ComputeStyleMode, MatchingRuleSet const&, Optional<LogicalAliasMappingContext>, ReadonlySpan<PropertyID> properties_to_cascade) const;
250207
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, Gfx::FontVariationSettings const& variations, bool inclusive);
251208
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, Gfx::FontVariationSettings const& variations, bool inclusive);
@@ -257,16 +214,10 @@ class WEB_API StyleComputer final : public GC::Cell {
257214
void resolve_effective_overflow_values(ComputedProperties&) const;
258215
void transform_box_type_if_needed(ComputedProperties&, DOM::AbstractElement) const;
259216

260-
template<typename Callback>
261-
void for_each_stylesheet(CascadeOrigin, Callback) const;
262-
263217
[[nodiscard]] CSSPixelRect viewport_rect() const { return m_viewport_rect; }
264218

265219
[[nodiscard]] Length::FontMetrics calculate_root_element_font_metrics(ComputedProperties const&) const;
266220

267-
Vector<FlyString> m_qualified_layer_names_in_order;
268-
void build_qualified_layer_names_cache();
269-
270221
void cascade_declarations(
271222
CascadedProperties&,
272223
DOM::AbstractElement,
@@ -277,39 +228,10 @@ class WEB_API StyleComputer final : public GC::Cell {
277228
Optional<LogicalAliasMappingContext>,
278229
ReadonlySpan<PropertyID> properties_to_cascade) const;
279230

280-
void build_rule_cache();
281-
void build_rule_cache_if_needed() const;
282-
283231
GC::Ref<DOM::Document> m_document;
284232

285-
struct SelectorInsights {
286-
bool has_has_selectors { false };
287-
};
288-
289-
struct RuleCaches {
290-
RuleCache main;
291-
HashMap<FlyString, NonnullOwnPtr<RuleCache>> by_layer;
292-
};
293-
294-
struct RuleCachesForDocumentAndShadowRoots {
295-
RuleCaches for_document;
296-
HashMap<GC::Ref<DOM::ShadowRoot const>, NonnullOwnPtr<RuleCaches>> for_shadow_roots;
297-
};
298-
299-
void make_rule_cache_for_cascade_origin(CascadeOrigin, SelectorInsights&);
300-
301233
[[nodiscard]] RuleCache const* rule_cache_for_cascade_origin(CascadeOrigin, Optional<FlyString const> qualified_layer_name, GC::Ptr<DOM::ShadowRoot const>) const;
302234

303-
static void collect_selector_insights(Selector const&, SelectorInsights&);
304-
305-
OwnPtr<SelectorInsights> m_selector_insights;
306-
Array<OwnPtr<RuleCache>, to_underlying(PseudoClass::__Count)> m_pseudo_class_rule_cache;
307-
OwnPtr<StyleInvalidationData> m_style_invalidation_data;
308-
OwnPtr<RuleCachesForDocumentAndShadowRoots> m_author_rule_cache;
309-
OwnPtr<RuleCachesForDocumentAndShadowRoots> m_user_rule_cache;
310-
OwnPtr<RuleCachesForDocumentAndShadowRoots> m_user_agent_rule_cache;
311-
GC::Ptr<CSSStyleSheet> m_user_style_sheet;
312-
313235
using FontLoaderList = Vector<GC::Ref<FontLoader>>;
314236
HashMap<OwnFontFaceKey, FontLoaderList> m_loaded_fonts;
315237

0 commit comments

Comments
 (0)