mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-05 13:43:29 +00:00
LibWeb: Rebuild counter style cache lazily
Stop rebuilding the counter style cache from every style update. That made unrelated restyles pay the full counter-style cost even when no relevant stylesheet state had changed. Dirty the cache when stylesheet rule caches are invalidated and rebuild it on the first counter-style lookup instead. Also make cold cache rebuilds include user stylesheets. Add regression tests covering insertRule() and replaceSync() updates that should make newly defined counter styles take effect.
This commit is contained in:
committed by
Andreas Kling
parent
0b5ef8fa22
commit
e2e3c7fcdf
Notes:
github-actions[bot]
2026-04-05 10:35:26 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/e2e3c7fcdf4 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8789
@@ -95,9 +95,7 @@ void StyleScope::build_rule_cache()
|
||||
m_selector_insights = make<SelectorInsights>();
|
||||
m_style_invalidation_data = make<StyleInvalidationData>();
|
||||
|
||||
if (auto user_style_source = document().page().user_style(); user_style_source.has_value()) {
|
||||
m_user_style_sheet = GC::make_root(parse_css_stylesheet(CSS::Parser::ParsingParams(document()), user_style_source.value()));
|
||||
}
|
||||
build_user_style_sheet_if_needed();
|
||||
|
||||
build_qualified_layer_names_cache();
|
||||
|
||||
@@ -115,6 +113,7 @@ void StyleScope::build_rule_cache()
|
||||
|
||||
void StyleScope::invalidate_rule_cache()
|
||||
{
|
||||
document().invalidate_counter_style_cache();
|
||||
m_author_rule_cache = nullptr;
|
||||
|
||||
// NOTE: We could be smarter about keeping the user rule cache, and style sheet.
|
||||
@@ -131,6 +130,15 @@ void StyleScope::invalidate_rule_cache()
|
||||
m_style_invalidation_data = nullptr;
|
||||
}
|
||||
|
||||
void StyleScope::build_user_style_sheet_if_needed()
|
||||
{
|
||||
if (m_user_style_sheet)
|
||||
return;
|
||||
|
||||
if (auto user_style_source = document().page().user_style(); user_style_source.has_value())
|
||||
m_user_style_sheet = GC::make_root(parse_css_stylesheet(CSS::Parser::ParsingParams(document()), user_style_source.value()));
|
||||
}
|
||||
|
||||
void StyleScope::build_rule_cache_if_needed() const
|
||||
{
|
||||
if (has_valid_rule_cache())
|
||||
@@ -188,8 +196,10 @@ void StyleScope::for_each_stylesheet(CascadeOrigin cascade_origin, Function<void
|
||||
callback(svg_stylesheet());
|
||||
}
|
||||
if (cascade_origin == CascadeOrigin::User) {
|
||||
if (m_user_style_sheet)
|
||||
callback(*m_user_style_sheet);
|
||||
auto& style_scope = const_cast<StyleScope&>(*this);
|
||||
style_scope.build_user_style_sheet_if_needed();
|
||||
if (style_scope.m_user_style_sheet)
|
||||
callback(*style_scope.m_user_style_sheet);
|
||||
}
|
||||
if (cascade_origin == CascadeOrigin::Author) {
|
||||
for_each_active_css_style_sheet(move(callback));
|
||||
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
[[nodiscard]] RuleCache const& get_pseudo_class_rule_cache(PseudoClass) const;
|
||||
|
||||
void for_each_stylesheet(CascadeOrigin, Function<void(CSS::CSSStyleSheet&)> const&) const;
|
||||
void build_user_style_sheet_if_needed();
|
||||
|
||||
void make_rule_cache_for_cascade_origin(CascadeOrigin, SelectorInsights&);
|
||||
|
||||
|
||||
@@ -1800,9 +1800,6 @@ void Document::update_style()
|
||||
|
||||
build_registered_properties_cache();
|
||||
|
||||
// FIXME: We don't need to rebuild this cache on every style update, just if a @counter-style rule has changed.
|
||||
build_counter_style_cache();
|
||||
|
||||
auto invalidation = update_style_recursively(*this, style_computer(), false, false, false);
|
||||
if (!invalidation.is_none())
|
||||
invalidate_display_list();
|
||||
@@ -6659,6 +6656,14 @@ void Document::for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<FlyString, NonnullRefPtr<CSS::CounterStyle const>> const& Document::registered_counter_styles() const
|
||||
{
|
||||
if (m_needs_counter_style_cache_update)
|
||||
const_cast<Document&>(*this).build_counter_style_cache();
|
||||
|
||||
return m_registered_counter_styles;
|
||||
}
|
||||
|
||||
double Document::ensure_element_shared_css_random_base_value(CSS::RandomCachingKey const& random_caching_key)
|
||||
{
|
||||
return m_element_shared_css_random_base_value_cache.ensure(random_caching_key, []() {
|
||||
@@ -8296,6 +8301,8 @@ void Document::build_counter_style_cache()
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
m_needs_counter_style_cache_update = false;
|
||||
}
|
||||
|
||||
StringView to_string(SetNeedsLayoutReason reason)
|
||||
|
||||
@@ -1044,11 +1044,13 @@ public:
|
||||
Optional<CSS::CustomPropertyRegistration const&> get_registered_custom_property(FlyString const& name) const;
|
||||
NonnullRefPtr<CSS::StyleValue const> custom_property_initial_value(FlyString const& name) const;
|
||||
|
||||
HashMap<FlyString, NonnullRefPtr<CSS::CounterStyle const>> const& registered_counter_styles() const { return m_registered_counter_styles; }
|
||||
HashMap<FlyString, NonnullRefPtr<CSS::CounterStyle const>> const& registered_counter_styles() const;
|
||||
|
||||
CSS::StyleScope const& style_scope() const { return m_style_scope; }
|
||||
CSS::StyleScope& style_scope() { return m_style_scope; }
|
||||
|
||||
void invalidate_counter_style_cache() { m_needs_counter_style_cache_update = true; }
|
||||
|
||||
void exit_pointer_lock();
|
||||
|
||||
Optional<CSS::SelectorList> const* cached_query_selector_result(String const& selector_text) const;
|
||||
@@ -1487,6 +1489,7 @@ private:
|
||||
HashMap<FlyString, CSS::CustomPropertyRegistration> m_registered_property_set;
|
||||
HashMap<FlyString, CSS::CustomPropertyRegistration> m_cached_registered_properties_from_css_property_rules;
|
||||
|
||||
bool m_needs_counter_style_cache_update { true };
|
||||
HashMap<FlyString, NonnullRefPtr<CSS::CounterStyle const>> m_registered_counter_styles;
|
||||
|
||||
CSS::StyleScope m_style_scope;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline
|
||||
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 34 0+0+0] [BFC] children: not-inline
|
||||
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 18 0+0+8] children: not-inline
|
||||
BlockContainer <div> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] children: inline
|
||||
InlineNode <(anonymous)> at [8,8] [0+0+0 39.6875 0+0+0] [0+0+0 18 0+0+0]
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [8,8 39.6875x18] baseline: 13.796875
|
||||
"*****"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> at [8,26] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
TextNode <#text> (not painted)
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x18]
|
||||
PaintableWithLines (BlockContainer<DIV>) [8,8 784x18]
|
||||
PaintableWithLines (InlineNode(anonymous)) [8,8 39.6875x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,26 784x0]
|
||||
|
||||
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
|
||||
SC for BlockContainer<HTML> [0,0 800x34] [children: 0] (z-index: auto)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] [BFC] children: not-inline
|
||||
BlockContainer <html> at [0,0] [0+0+0 800 0+0+0] [0+0+0 34 0+0+0] [BFC] children: not-inline
|
||||
BlockContainer <body> at [8,8] [8+0+0 784 0+0+8] [8+0+0 18 0+0+8] children: not-inline
|
||||
BlockContainer <div> at [8,8] [0+0+0 784 0+0+0] [0+0+0 18 0+0+0] children: inline
|
||||
InlineNode <(anonymous)> at [8,8] [0+0+0 39.6875 0+0+0] [0+0+0 18 0+0+0]
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [8,8 39.6875x18] baseline: 13.796875
|
||||
"*****"
|
||||
TextNode <#text> (not painted)
|
||||
BlockContainer <(anonymous)> at [8,26] [0+0+0 784 0+0+0] [0+0+0 0 0+0+0] children: inline
|
||||
TextNode <#text> (not painted)
|
||||
TextNode <#text> (not painted)
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x18]
|
||||
PaintableWithLines (BlockContainer<DIV>) [8,8 784x18]
|
||||
PaintableWithLines (InlineNode(anonymous)) [8,8 39.6875x18]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,26 784x0]
|
||||
|
||||
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
|
||||
SC for BlockContainer<HTML> [0,0 800x34] [children: 0] (z-index: auto)
|
||||
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<style id="style">
|
||||
div::after {
|
||||
content: counter(a, dynamic-counter-style);
|
||||
}
|
||||
</style>
|
||||
<div style="counter-reset: a 5"></div>
|
||||
<script>
|
||||
style.sheet.insertRule("@counter-style dynamic-counter-style { symbols: \"*\"; }", 0);
|
||||
document.body.offsetWidth;
|
||||
</script>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<div style="counter-reset: a 5"></div>
|
||||
<script>
|
||||
let sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync(`
|
||||
div::after {
|
||||
content: counter(a, dynamic-counter-style);
|
||||
}
|
||||
`);
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
|
||||
sheet.replaceSync(`
|
||||
@counter-style dynamic-counter-style {
|
||||
symbols: "*";
|
||||
}
|
||||
|
||||
div::after {
|
||||
content: counter(a, dynamic-counter-style);
|
||||
}
|
||||
`);
|
||||
|
||||
document.body.offsetWidth;
|
||||
</script>
|
||||
Reference in New Issue
Block a user