LibWeb: Only invalidate shadow root when style sheet inside changes

We don't have to invalidate style for the entire document when a style
sheet changes inside of a shadow root.

To make this possible, StyleSheetList now keeps track of which
Document-or-ShadowRoot it corresponds to, instead of just tracking the
containing Document.

This avoids a lot of style recomputation on pages with lots of shadow
DOM content (like GitHub).
This commit is contained in:
Andreas Kling 2024-08-20 14:55:28 +02:00 committed by Andreas Kling
parent e71ed67069
commit 4bc3055c0f
Notes: github-actions[bot] 2024-08-20 14:11:28 +00:00
5 changed files with 37 additions and 25 deletions

View file

@ -108,9 +108,8 @@ void CSSStyleRule::set_selector_text(StringView selector_text)
m_selectors = parsed_selectors.release_value(); m_selectors = parsed_selectors.release_value();
if (auto* sheet = parent_style_sheet()) { if (auto* sheet = parent_style_sheet()) {
if (auto style_sheet_list = sheet->style_sheet_list()) { if (auto style_sheet_list = sheet->style_sheet_list()) {
auto& document = style_sheet_list->document(); style_sheet_list->document().style_computer().invalidate_rule_cache();
document.style_computer().invalidate_rule_cache(); style_sheet_list->document_or_shadow_root().invalidate_style();
document.invalidate_style();
} }
} }
} }

View file

@ -155,7 +155,7 @@ WebIDL::ExceptionOr<unsigned> CSSStyleSheet::insert_rule(StringView rule, unsign
if (m_style_sheet_list) { if (m_style_sheet_list) {
m_style_sheet_list->document().style_computer().invalidate_rule_cache(); m_style_sheet_list->document().style_computer().invalidate_rule_cache();
m_style_sheet_list->document().invalidate_style(); m_style_sheet_list->document_or_shadow_root().invalidate_style();
} }
} }
@ -176,7 +176,7 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::delete_rule(unsigned index)
if (!result.is_exception()) { if (!result.is_exception()) {
if (m_style_sheet_list) { if (m_style_sheet_list) {
m_style_sheet_list->document().style_computer().invalidate_rule_cache(); m_style_sheet_list->document().style_computer().invalidate_rule_cache();
m_style_sheet_list->document().invalidate_style(); m_style_sheet_list->document_or_shadow_root().invalidate_style();
} }
} }
return result; return result;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -107,9 +107,9 @@ void StyleSheetList::add_sheet(CSSStyleSheet& sheet)
return; return;
} }
m_document->style_computer().invalidate_rule_cache(); document().style_computer().invalidate_rule_cache();
m_document->style_computer().load_fonts_from_sheet(sheet); document().style_computer().load_fonts_from_sheet(sheet);
m_document->invalidate_style(); document_or_shadow_root().invalidate_style();
} }
void StyleSheetList::remove_sheet(CSSStyleSheet& sheet) void StyleSheetList::remove_sheet(CSSStyleSheet& sheet)
@ -123,19 +123,19 @@ void StyleSheetList::remove_sheet(CSSStyleSheet& sheet)
return; return;
} }
m_document->style_computer().invalidate_rule_cache(); m_document_or_shadow_root->document().style_computer().invalidate_rule_cache();
m_document->invalidate_style(); document_or_shadow_root().invalidate_style();
} }
JS::NonnullGCPtr<StyleSheetList> StyleSheetList::create(DOM::Document& document) JS::NonnullGCPtr<StyleSheetList> StyleSheetList::create(JS::NonnullGCPtr<DOM::Node> document_or_shadow_root)
{ {
auto& realm = document.realm(); auto& realm = document_or_shadow_root->realm();
return realm.heap().allocate<StyleSheetList>(realm, document); return realm.heap().allocate<StyleSheetList>(realm, document_or_shadow_root);
} }
StyleSheetList::StyleSheetList(DOM::Document& document) StyleSheetList::StyleSheetList(JS::NonnullGCPtr<DOM::Node> document_or_shadow_root)
: Bindings::PlatformObject(document.realm()) : Bindings::PlatformObject(document_or_shadow_root->realm())
, m_document(document) , m_document_or_shadow_root(document_or_shadow_root)
{ {
m_legacy_platform_object_flags = LegacyPlatformObjectFlags { .supports_indexed_properties = true }; m_legacy_platform_object_flags = LegacyPlatformObjectFlags { .supports_indexed_properties = true };
} }
@ -149,7 +149,7 @@ void StyleSheetList::initialize(JS::Realm& realm)
void StyleSheetList::visit_edges(Cell::Visitor& visitor) void StyleSheetList::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_document); visitor.visit(m_document_or_shadow_root);
visitor.visit(m_sheets); visitor.visit(m_sheets);
} }
@ -161,4 +161,14 @@ Optional<JS::Value> StyleSheetList::item_value(size_t index) const
return m_sheets[index].ptr(); return m_sheets[index].ptr();
} }
DOM::Document& StyleSheetList::document()
{
return m_document_or_shadow_root->document();
}
DOM::Document const& StyleSheetList::document() const
{
return m_document_or_shadow_root->document();
}
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org> * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
@ -17,7 +17,7 @@ class StyleSheetList final : public Bindings::PlatformObject {
JS_DECLARE_ALLOCATOR(StyleSheetList); JS_DECLARE_ALLOCATOR(StyleSheetList);
public: public:
[[nodiscard]] static JS::NonnullGCPtr<StyleSheetList> create(DOM::Document&); [[nodiscard]] static JS::NonnullGCPtr<StyleSheetList> create(JS::NonnullGCPtr<DOM::Node> document_or_shadow_root);
void add_a_css_style_sheet(CSS::CSSStyleSheet&); void add_a_css_style_sheet(CSS::CSSStyleSheet&);
void remove_a_css_style_sheet(CSS::CSSStyleSheet&); void remove_a_css_style_sheet(CSS::CSSStyleSheet&);
@ -37,11 +37,14 @@ public:
virtual Optional<JS::Value> item_value(size_t index) const override; virtual Optional<JS::Value> item_value(size_t index) const override;
DOM::Document& document() { return m_document; } [[nodiscard]] DOM::Document& document();
DOM::Document const& document() const { return m_document; } [[nodiscard]] DOM::Document const& document() const;
[[nodiscard]] DOM::Node& document_or_shadow_root() { return m_document_or_shadow_root; }
[[nodiscard]] DOM::Node const& document_or_shadow_root() const { return m_document_or_shadow_root; }
private: private:
explicit StyleSheetList(DOM::Document&); explicit StyleSheetList(JS::NonnullGCPtr<DOM::Node> document_or_shadow_root);
virtual void initialize(JS::Realm&) override; virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
@ -49,7 +52,7 @@ private:
void add_sheet(CSSStyleSheet&); void add_sheet(CSSStyleSheet&);
void remove_sheet(CSSStyleSheet&); void remove_sheet(CSSStyleSheet&);
JS::NonnullGCPtr<DOM::Document> m_document; JS::NonnullGCPtr<DOM::Node> m_document_or_shadow_root;
Vector<JS::NonnullGCPtr<CSSStyleSheet>> m_sheets; Vector<JS::NonnullGCPtr<CSSStyleSheet>> m_sheets;
// https://www.w3.org/TR/cssom/#preferred-css-style-sheet-set-name // https://www.w3.org/TR/cssom/#preferred-css-style-sheet-set-name

View file

@ -122,7 +122,7 @@ WebIDL::ExceptionOr<void> ShadowRoot::set_html_unsafe(StringView html)
CSS::StyleSheetList& ShadowRoot::style_sheets() CSS::StyleSheetList& ShadowRoot::style_sheets()
{ {
if (!m_style_sheets) if (!m_style_sheets)
m_style_sheets = CSS::StyleSheetList::create(document()); m_style_sheets = CSS::StyleSheetList::create(*this);
return *m_style_sheets; return *m_style_sheets;
} }