From ebd6d49415374ecb25e8b93ef3b8782ff7a9c805 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Mon, 27 Jan 2025 13:35:18 +1300 Subject: [PATCH] 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 :^) --- Libraries/LibWeb/HTML/HTMLOptionElement.cpp | 6 +++-- Libraries/LibWeb/HTML/HTMLSelectElement.cpp | 28 ++++++++++++--------- Libraries/LibWeb/HTML/HTMLSelectElement.h | 2 ++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Libraries/LibWeb/HTML/HTMLOptionElement.cpp b/Libraries/LibWeb/HTML/HTMLOptionElement.cpp index e8dec0f8f5e..a0da743834a 100644 --- a/Libraries/LibWeb/HTML/HTMLOptionElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLOptionElement.cpp @@ -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) diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index 34207d6c88d..8fa27dc6677 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -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(*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(*metadata.node)) + return can_skip_selectedness_update_for_inserted_option(*option); return false; } diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Libraries/LibWeb/HTML/HTMLSelectElement.h index ddea134edaf..5390fa402f1 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.h +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.h @@ -98,6 +98,8 @@ public: void update_inner_text_element(Badge); + bool can_skip_selectedness_update_for_inserted_option(HTMLOptionElement const&) const; + private: HTMLSelectElement(DOM::Document&, DOM::QualifiedName);