LibWeb: Cache name->element mappings in HTMLCollection

This makes https://wpt.fyi/ load today instead of tomorrow, although
there's a lot of room for improvement still.
This commit is contained in:
Andreas Kling 2024-07-25 07:49:41 +02:00 committed by Andreas Kling
commit 4d78c66b3d
Notes: github-actions[bot] 2024-07-25 11:12:56 +00:00
4 changed files with 48 additions and 31 deletions

View file

@ -48,6 +48,30 @@ void HTMLCollection::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_root);
visitor.visit(m_cached_elements);
if (m_cached_name_to_element_mappings)
visitor.visit(*m_cached_name_to_element_mappings);
}
void HTMLCollection::update_name_to_element_mappings_if_needed() const
{
update_cache_if_needed();
if (m_cached_name_to_element_mappings)
return;
m_cached_name_to_element_mappings = make<HashMap<FlyString, JS::NonnullGCPtr<Element>>>();
for (auto const& element : m_cached_elements) {
// 1. If element has an ID which is not in result, append elements ID to result.
if (auto const& id = element->id(); id.has_value()) {
if (!id.value().is_empty() && !m_cached_name_to_element_mappings->contains(id.value()))
m_cached_name_to_element_mappings->set(id.value(), element);
}
// 2. If element is in the HTML namespace and has a name attribute whose value is neither the empty string nor is in result, append elements name attribute value to result.
if (element->namespace_uri() == Namespace::HTML && element->name().has_value()) {
auto element_name = element->name().value();
if (!element_name.is_empty() && !m_cached_name_to_element_mappings->contains(element_name))
m_cached_name_to_element_mappings->set(move(element_name), element);
}
}
}
void HTMLCollection::update_cache_if_needed() const
@ -57,6 +81,7 @@ void HTMLCollection::update_cache_if_needed() const
return;
m_cached_elements.clear();
m_cached_name_to_element_mappings = nullptr;
if (m_scope == Scope::Descendants) {
m_root->for_each_in_subtree_of_type<Element>([&](auto& element) {
if (m_filter(element))
@ -107,23 +132,19 @@ Element* HTMLCollection::named_item(FlyString const& key) const
if (key.is_empty())
return nullptr;
update_cache_if_needed();
// 2. Return the first element in the collection for which at least one of the following is true:
for (auto const& element : m_cached_elements) {
// - it has an ID which is key;
if (element->id() == key)
return element;
// - it is in the HTML namespace and has a name attribute whose value is key;
if (element->namespace_uri() == Namespace::HTML && element->name() == key)
return element;
}
// or null if there is no such element.
update_name_to_element_mappings_if_needed();
if (auto it = m_cached_name_to_element_mappings->get(key); it.has_value())
return it.value();
return nullptr;
}
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names
bool HTMLCollection::is_supported_property_name(FlyString const& name) const
{
update_name_to_element_mappings_if_needed();
return m_cached_name_to_element_mappings->contains(name);
}
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names
Vector<FlyString> HTMLCollection::supported_property_names() const
{
@ -131,20 +152,9 @@ Vector<FlyString> HTMLCollection::supported_property_names() const
Vector<FlyString> result;
// 2. For each element represented by the collection, in tree order:
update_cache_if_needed();
for (auto const& element : m_cached_elements) {
// 1. If element has an ID which is not in result, append elements ID to result.
if (auto const& id = element->id(); id.has_value()) {
if (!id.value().is_empty() && !result.contains_slow(id.value()))
result.append(id.value());
}
// 2. If element is in the HTML namespace and has a name attribute whose value is neither the empty string nor is in result, append elements name attribute value to result.
if (element->namespace_uri() == Namespace::HTML && element->name().has_value()) {
auto name = element->name().value();
if (!name.is_empty() && !result.contains_slow(name))
result.append(move(name));
}
update_name_to_element_mappings_if_needed();
for (auto const& it : *m_cached_name_to_element_mappings) {
result.append(it.key);
}
// 3. Return result.