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:
parent
132ab775d8
commit
4d78c66b3d
Notes:
github-actions[bot]
2024-07-25 11:12:56 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/4d78c66b3d0 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/823 Reviewed-by: https://github.com/shannonbooth
4 changed files with 48 additions and 31 deletions
|
@ -47,9 +47,7 @@ JS::ThrowCompletionOr<bool> PlatformObject::is_named_property_exposed_on_object(
|
|||
|
||||
// 1. If P is not a supported property name of O, then return false.
|
||||
// NOTE: This is in it's own variable to enforce the type.
|
||||
auto supported_property_names = this->supported_property_names();
|
||||
auto property_key_string = MUST(String::from_byte_string(property_key.to_string()));
|
||||
if (!supported_property_names.contains_slow(property_key_string))
|
||||
if (!is_supported_property_name(MUST(String::from_byte_string(property_key.to_string()))))
|
||||
return false;
|
||||
|
||||
// 2. If O has an own property named P, then return false.
|
||||
|
@ -500,6 +498,11 @@ Vector<FlyString> PlatformObject::supported_property_names() const
|
|||
return {};
|
||||
}
|
||||
|
||||
bool PlatformObject::is_supported_property_name(FlyString const& name) const
|
||||
{
|
||||
return supported_property_names().contains_slow(name);
|
||||
}
|
||||
|
||||
bool PlatformObject::is_supported_property_index(u32) const
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -75,6 +75,7 @@ protected:
|
|||
virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const;
|
||||
virtual WebIDL::ExceptionOr<JS::Value> named_item_value(FlyString const& name) const;
|
||||
virtual Vector<FlyString> supported_property_names() const;
|
||||
virtual bool is_supported_property_name(FlyString const&) const;
|
||||
virtual bool is_supported_property_index(u32) const;
|
||||
|
||||
// NOTE: These will crash if you make has_named_property_setter return true but do not override these methods.
|
||||
|
|
|
@ -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 element’s 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 element’s 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 element’s 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 element’s 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.
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
virtual WebIDL::ExceptionOr<JS::Value> item_value(size_t index) const override;
|
||||
virtual WebIDL::ExceptionOr<JS::Value> named_item_value(FlyString const& name) const override;
|
||||
virtual Vector<FlyString> supported_property_names() const override;
|
||||
virtual bool is_supported_property_name(FlyString const&) const override;
|
||||
virtual bool is_supported_property_index(u32) const override;
|
||||
|
||||
protected:
|
||||
|
@ -57,9 +58,11 @@ private:
|
|||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
void update_cache_if_needed() const;
|
||||
void update_name_to_element_mappings_if_needed() const;
|
||||
|
||||
mutable u64 m_cached_dom_tree_version { 0 };
|
||||
mutable Vector<JS::NonnullGCPtr<Element>> m_cached_elements;
|
||||
mutable OwnPtr<HashMap<FlyString, JS::NonnullGCPtr<Element>>> m_cached_name_to_element_mappings;
|
||||
|
||||
JS::NonnullGCPtr<ParentNode> m_root;
|
||||
Function<bool(Element const&)> m_filter;
|
||||
|
|
Loading…
Add table
Reference in a new issue