LibWeb: Cache the value of Element::lang()

This reduces the time spent in
`SelectorEngine::matches_lang_pseudo_class()` from 1.9% to 0.41% on
https://cloudflare.com
This commit is contained in:
Tim Ledbetter 2025-11-11 18:43:29 +00:00 committed by Jelle Raaijmakers
parent 0ec97eabab
commit 154e9db033
Notes: github-actions[bot] 2025-11-12 11:37:21 +00:00
5 changed files with 92 additions and 8 deletions

View file

@ -3907,6 +3907,11 @@ void Element::attribute_changed(FlyString const& local_name, Optional<String> co
m_dir = Dir::Auto;
else
m_dir = {};
} else if (local_name == HTML::AttributeNames::lang) {
for_each_in_inclusive_subtree_of_type<Element>([](auto& element) {
element.invalidate_lang_value();
return TraversalDecision::Continue;
});
}
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:concept-element-attributes-change-ext
@ -4020,7 +4025,7 @@ void Element::set_counters_set(OwnPtr<CSS::CountersSet>&& counters_set)
// https://html.spec.whatwg.org/multipage/dom.html#the-lang-and-xml:lang-attributes
Optional<String> Element::lang() const
{
auto attempt_to_determine_lang_attribute = [&]() -> Optional<String> {
auto determine_lang_attribute = [&]() -> String {
// 1. If the node is an element that has a lang attribute in the XML namespace set
// Use the value of that attribute.
auto maybe_xml_lang = get_attribute_ns(Namespace::XML, HTML::AttributeNames::lang);
@ -4039,23 +4044,23 @@ Optional<String> Element::lang() const
// Use the language of that shadow root's host.
if (auto parent = parent_element()) {
if (parent->is_shadow_root())
return parent->shadow_root()->host()->lang();
return parent->shadow_root()->host()->lang().value_or({});
}
// 4. If the node's parent element is not null
// Use the language of that parent element.
if (auto parent = parent_element())
return parent->lang();
return parent->lang().value_or({});
// 5. Otherwise
// - If there is a pragma-set default language set, then that is the language of the node.
if (document().pragma_set_default_language().has_value()) {
return document().pragma_set_default_language();
return document().pragma_set_default_language().value_or({});
}
// - If there is no pragma-set default language set, then language information from a higher-level protocol (such as HTTP),
if (document().http_content_language().has_value()) {
return document().http_content_language();
return document().http_content_language().value_or({});
}
// if any, must be used as the final fallback language instead.
@ -4065,11 +4070,22 @@ Optional<String> Element::lang() const
return {};
};
if (!m_lang_value.has_value())
m_lang_value = determine_lang_attribute();
// If the resulting value is the empty string, then it must be interpreted as meaning that the language of the node is explicitly unknown.
auto maybe_lang = attempt_to_determine_lang_attribute();
if (!maybe_lang.has_value() || maybe_lang->is_empty())
if (m_lang_value->is_empty())
return {};
return maybe_lang.release_value();
return m_lang_value;
}
void Element::invalidate_lang_value()
{
if (m_lang_value.has_value()) {
m_lang_value.clear();
set_needs_style_update(true);
}
}
template<typename Callback>