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
parent 55eed9b344
commit 829e218427
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;
}
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
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 const> inline_style() const { return m_inline_style; }
void set_inline_style(GC::Ptr<CSS::CSSStyleProperties>);
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, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
@ -19,6 +19,7 @@
#include <LibWeb/Bindings/HTMLInputElementPrototype.h>
#include <LibWeb/Bindings/PrincipalHostDefined.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
@ -744,15 +745,40 @@ void HTMLInputElement::commit_pending_changes()
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()
{
if (!m_placeholder_element)
return;
if (this->placeholder_value().has_value()) {
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv));
} else {
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv));
}
if (this->placeholder_value().has_value())
m_placeholder_element->set_inline_style(placeholder_style_when_visible());
else
m_placeholder_element->set_inline_style(placeholder_style_when_hidden());
}
void HTMLInputElement::update_button_input_shadow_tree()
@ -989,26 +1015,27 @@ void HTMLInputElement::create_text_input_shadow_tree()
auto initial_value = m_value;
auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
MUST(element->set_attribute(HTML::AttributeNames::style, R"~~~(
display: flex;
height: 100%;
align-items: center;
white-space: pre;
border: none;
padding: 1px 2px;
)~~~"_string));
{
static GC::Root<CSS::CSSStyleProperties> style;
if (!style) {
style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
style->set_declarations_from_text(R"~~~(
display: flex;
height: 100%;
align-items: center;
white-space: pre;
border: none;
padding: 1px 2px;
)~~~"sv);
}
element->set_inline_style(*style);
}
MUST(shadow_root->append_child(element));
m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
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));
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
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%;
height: 1lh;
align-items: center;
text-overflow: clip;
white-space: nowrap;
)~~~"_string));
{
static GC::Root<CSS::CSSStyleProperties> style;
if (!style) {
style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {});
style->set_declarations_from_text(R"~~~(
width: 100%;
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));
m_text_node = realm().create<DOM::Text>(document(), move(initial_value));