LibWeb/HTML: Add fastpath to skip selectedness update on option insert

This extends the optimization introduced in the previous commit to
also apply to the inserted steps for an option element. This makes
the test:

https://wpt.live/html/select/options-length-too-large.html

Go from not ever completing due to how slow it was to running, to
finishing in 800ms on my PC :^)
This commit is contained in:
Shannon Booth 2025-01-27 13:35:18 +13:00 committed by Andrew Kaster
parent 075c7ea63e
commit ebd6d49415
Notes: github-actions[bot] 2025-01-30 20:56:39 +00:00
3 changed files with 22 additions and 14 deletions

View file

@ -234,8 +234,10 @@ void HTMLOptionElement::inserted()
// If insertedNode's parent is a select element,
// or insertedNode's parent is an optgroup element whose parent is a select element,
// then run that select element's selectedness setting algorithm.
if (auto select_element = owner_select_element())
select_element->update_selectedness();
if (auto select_element = owner_select_element()) {
if (!select_element->can_skip_selectedness_update_for_inserted_option(*this))
select_element->update_selectedness();
}
}
void HTMLOptionElement::removed_from(Node* old_parent, Node& old_root)

View file

@ -293,6 +293,20 @@ i32 HTMLSelectElement::default_tab_index_value() const
return 0;
}
bool HTMLSelectElement::can_skip_selectedness_update_for_inserted_option(HTMLOptionElement const& option) const
{
if (option.selected())
return false;
if (m_cached_number_of_selected_options >= 2)
return false;
if (display_size() == 1 && m_cached_number_of_selected_options == 0)
return false;
return true;
}
bool HTMLSelectElement::can_skip_children_changed_selectedness_update(ChildrenChangedMetadata const& metadata) const
{
// If the following criteria are met, there is no need to re-run the selectedness algorithm.
@ -300,18 +314,8 @@ bool HTMLSelectElement::can_skip_children_changed_selectedness_update(ChildrenCh
if (metadata.type != ChildrenChangedMetadata::Type::Inserted)
return false;
if (auto* option = as_if<HTMLOptionElement>(*metadata.node)) {
if (option->selected())
return false;
if (m_cached_number_of_selected_options >= 2)
return false;
if (display_size() == 1 && m_cached_number_of_selected_options == 0)
return false;
return true;
}
if (auto* option = as_if<HTMLOptionElement>(*metadata.node))
return can_skip_selectedness_update_for_inserted_option(*option);
return false;
}

View file

@ -98,6 +98,8 @@ public:
void update_inner_text_element(Badge<HTMLOptionElement>);
bool can_skip_selectedness_update_for_inserted_option(HTMLOptionElement const&) const;
private:
HTMLSelectElement(DOM::Document&, DOM::QualifiedName);