LibWeb: Cache and reuse inline style for text input shadow trees

Instead of reparsing the style attributes every time we instantiate
the internal shadow tree for a text input element, we now parse them
once (in the internal CSS realm) and reuse them for all elements.

Roughly a ~10% speedup on Speedometer 2.1 :^)
This commit is contained in:
Andreas Kling 2025-03-25 11:48:54 +00:00 committed by Andreas Kling
commit d856858015
Notes: github-actions[bot] 2025-03-25 23:57:59 +00:00
3 changed files with 69 additions and 28 deletions

View file

@ -925,6 +925,12 @@ GC::Ref<CSS::CSSStyleProperties> Element::style_for_bindings()
return *m_inline_style; return *m_inline_style;
} }
void Element::set_inline_style(GC::Ptr<CSS::CSSStyleProperties> style)
{
m_inline_style = style;
set_needs_style_update(true);
}
// https://dom.spec.whatwg.org/#element-html-uppercased-qualified-name // https://dom.spec.whatwg.org/#element-html-uppercased-qualified-name
void Element::make_html_uppercased_qualified_name() void Element::make_html_uppercased_qualified_name()
{ {

View file

@ -213,6 +213,7 @@ public:
GC::Ptr<CSS::CSSStyleProperties> inline_style() { return m_inline_style; } GC::Ptr<CSS::CSSStyleProperties> inline_style() { return m_inline_style; }
GC::Ptr<CSS::CSSStyleProperties const> inline_style() const { return m_inline_style; } GC::Ptr<CSS::CSSStyleProperties const> inline_style() const { return m_inline_style; }
void set_inline_style(GC::Ptr<CSS::CSSStyleProperties>);
GC::Ref<CSS::CSSStyleProperties> style_for_bindings(); GC::Ref<CSS::CSSStyleProperties> style_for_bindings();

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org> * Copyright (c) 2018-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022, Adam Hodgen <ant1441@gmail.com> * Copyright (c) 2022, Adam Hodgen <ant1441@gmail.com>
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org> * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org> * Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
@ -19,6 +19,7 @@
#include <LibWeb/Bindings/HTMLInputElementPrototype.h> #include <LibWeb/Bindings/HTMLInputElementPrototype.h>
#include <LibWeb/Bindings/PrincipalHostDefined.h> #include <LibWeb/Bindings/PrincipalHostDefined.h>
#include <LibWeb/CSS/ComputedProperties.h> #include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h> #include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h> #include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h> #include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
@ -744,15 +745,40 @@ void HTMLInputElement::commit_pending_changes()
dispatch_event(change_event); dispatch_event(change_event);
} }
static GC::Ref<CSS::CSSStyleProperties> placeholder_style_when_visible()
{
static GC::Root<CSS::CSSStyleProperties> style;
if (!style) {
style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
style->set_declarations_from_text(R"~~~(
width: 100%;
align-items: center;
text-overflow: clip;
white-space: nowrap;
display: block;
)~~~"sv);
}
return *style;
}
static GC::Ref<CSS::CSSStyleProperties> placeholder_style_when_hidden()
{
static GC::Root<CSS::CSSStyleProperties> style;
if (!style) {
style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
style->set_declarations_from_text("display: none;"sv);
}
return *style;
}
void HTMLInputElement::update_placeholder_visibility() void HTMLInputElement::update_placeholder_visibility()
{ {
if (!m_placeholder_element) if (!m_placeholder_element)
return; return;
if (this->placeholder_value().has_value()) { if (this->placeholder_value().has_value())
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv)); m_placeholder_element->set_inline_style(placeholder_style_when_visible());
} else { else
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv)); m_placeholder_element->set_inline_style(placeholder_style_when_hidden());
}
} }
void HTMLInputElement::update_button_input_shadow_tree() void HTMLInputElement::update_button_input_shadow_tree()
@ -989,26 +1015,27 @@ void HTMLInputElement::create_text_input_shadow_tree()
auto initial_value = m_value; auto initial_value = m_value;
auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
MUST(element->set_attribute(HTML::AttributeNames::style, R"~~~( {
display: flex; static GC::Root<CSS::CSSStyleProperties> style;
height: 100%; if (!style) {
align-items: center; style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
white-space: pre; style->set_declarations_from_text(R"~~~(
border: none; display: flex;
padding: 1px 2px; height: 100%;
)~~~"_string)); align-items: center;
white-space: pre;
border: none;
padding: 1px 2px;
)~~~"sv);
}
element->set_inline_style(*style);
}
MUST(shadow_root->append_child(element)); MUST(shadow_root->append_child(element));
m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
m_placeholder_element->set_use_pseudo_element(CSS::PseudoElement::Placeholder); m_placeholder_element->set_use_pseudo_element(CSS::PseudoElement::Placeholder);
update_placeholder_visibility();
// https://www.w3.org/TR/css-ui-4/#input-rules
MUST(m_placeholder_element->set_attribute(HTML::AttributeNames::style, R"~~~(
width: 100%;
align-items: center;
text-overflow: clip;
white-space: nowrap;
)~~~"_string));
MUST(element->append_child(*m_placeholder_element)); MUST(element->append_child(*m_placeholder_element));
m_placeholder_text_node = realm().create<DOM::Text>(document(), String {}); m_placeholder_text_node = realm().create<DOM::Text>(document(), String {});
@ -1017,13 +1044,20 @@ void HTMLInputElement::create_text_input_shadow_tree()
// https://www.w3.org/TR/css-ui-4/#input-rules // https://www.w3.org/TR/css-ui-4/#input-rules
m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
MUST(m_inner_text_element->set_attribute(HTML::AttributeNames::style, R"~~~( {
width: 100%; static GC::Root<CSS::CSSStyleProperties> style;
height: 1lh; if (!style) {
align-items: center; style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
text-overflow: clip; style->set_declarations_from_text(R"~~~(
white-space: nowrap; width: 100%;
)~~~"_string)); height: 1lh;
align-items: center;
text-overflow: clip;
white-space: nowrap;
)~~~"sv);
}
m_inner_text_element->set_inline_style(*style);
}
MUST(element->append_child(*m_inner_text_element)); MUST(element->append_child(*m_inner_text_element));
m_text_node = realm().create<DOM::Text>(document(), move(initial_value)); m_text_node = realm().create<DOM::Text>(document(), move(initial_value));