mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
Previously, the select button's text was only refreshed inside the two non-trivial branches of the selectedness setting algorithm. Paths that left the select with exactly one selected option hit a no-op branch and skipped the refresh. Fix this by implementing the "clone selected option into select button" algorithm and invoking it whenever the set of selected options may have changed.
178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
/*
|
|
* Copyright (c) 2020, the SerenityOS developers.
|
|
* Copyright (c) 2021-2022, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
|
* Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <LibWeb/Export.h>
|
|
#include <LibWeb/HTML/AutocompleteElement.h>
|
|
#include <LibWeb/HTML/HTMLElement.h>
|
|
#include <LibWeb/HTML/HTMLOptionsCollection.h>
|
|
#include <LibWeb/HTML/SelectItem.h>
|
|
#include <LibWeb/WebIDL/Types.h>
|
|
|
|
namespace Web::HTML {
|
|
|
|
class WEB_API HTMLSelectElement final
|
|
: public HTMLElement
|
|
, public AutocompleteElement {
|
|
WEB_PLATFORM_OBJECT(HTMLSelectElement, HTMLElement);
|
|
GC_DECLARE_ALLOCATOR(HTMLSelectElement);
|
|
AUTOCOMPLETE_ELEMENT(HTMLElement, HTMLSelectElement);
|
|
|
|
public:
|
|
virtual ~HTMLSelectElement() override;
|
|
|
|
virtual bool is_html_select_element() const final { return true; }
|
|
|
|
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
|
|
|
|
WebIDL::UnsignedLong size() const;
|
|
void set_size(WebIDL::UnsignedLong);
|
|
|
|
GC::Ptr<HTMLOptionsCollection> const& options() const;
|
|
|
|
WebIDL::UnsignedLong length();
|
|
WebIDL::ExceptionOr<void> set_length(WebIDL::UnsignedLong);
|
|
HTMLOptionElement* item(WebIDL::UnsignedLong index);
|
|
virtual Optional<JS::Value> item_value(size_t index) const override;
|
|
HTMLOptionElement* named_item(FlyString const& name);
|
|
WebIDL::ExceptionOr<void> add(HTMLOptionOrOptGroupElement element, NullableHTMLElementOrElementIndex before = { Empty {} });
|
|
virtual WebIDL::ExceptionOr<void> set_value_of_indexed_property(u32, JS::Value) override;
|
|
void remove();
|
|
void remove(WebIDL::Long);
|
|
|
|
GC::Ref<DOM::HTMLCollection> selected_options();
|
|
GC::Ref<DOM::HTMLCollection> selected_options() const { return const_cast<HTMLSelectElement*>(this)->selected_options(); }
|
|
|
|
WebIDL::Long selected_index() const;
|
|
WebIDL::ExceptionOr<void> set_selected_index(WebIDL::Long);
|
|
|
|
Utf16String value() const;
|
|
virtual Utf16String form_value() const override { return value(); }
|
|
WebIDL::ExceptionOr<void> set_value(Utf16String const&);
|
|
|
|
bool is_open() const { return m_is_open; }
|
|
void set_is_open(bool);
|
|
|
|
WebIDL::ExceptionOr<void> show_picker();
|
|
|
|
Vector<GC::Root<HTMLOptionElement>> list_of_options() const;
|
|
|
|
// ^EventTarget
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-select-element
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#focusable-area
|
|
// https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
|
|
virtual bool is_focusable() const override;
|
|
|
|
// ^FormAssociatedElement
|
|
virtual bool is_form_associated_element() const override { return true; }
|
|
|
|
// ^FormAssociatedElement
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-listed
|
|
virtual bool is_listed() const override { return true; }
|
|
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-submit
|
|
virtual bool is_submittable() const override { return true; }
|
|
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-reset
|
|
virtual bool is_resettable() const override { return true; }
|
|
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-autocapitalize
|
|
virtual bool is_autocapitalize_and_autocorrect_inheriting() const override { return true; }
|
|
|
|
// ^HTMLElement
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-label
|
|
virtual bool is_labelable() const override { return true; }
|
|
|
|
virtual void reset_algorithm() override;
|
|
|
|
String const& type() const;
|
|
|
|
virtual Optional<ARIA::Role> default_role() const override;
|
|
|
|
virtual bool has_activation_behavior() const override;
|
|
virtual void activation_behavior(DOM::Event const&) override;
|
|
|
|
virtual void form_associated_element_was_inserted() override;
|
|
virtual void form_associated_element_attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
|
|
|
void did_select_item(Optional<u32> const& id);
|
|
|
|
void update_selectedness();
|
|
|
|
void clone_selected_option_into_select_button();
|
|
|
|
bool can_skip_selectedness_update_for_inserted_option(HTMLOptionElement const&) const;
|
|
|
|
bool user_validity() const { return m_user_validity; }
|
|
void set_user_validity(bool flag) { m_user_validity = flag; }
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#placeholder-label-option
|
|
HTMLOptionElement* placeholder_label_option() const;
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#select-enabled-selectedcontent
|
|
GC::Ptr<HTMLSelectedContentElement> enabled_selectedcontent() const;
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#clear-a-select%27s-non-primary-selectedcontent-elements
|
|
void clear_non_primary_selectedcontent();
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#update-a-select%27s-selectedcontent
|
|
WebIDL::ExceptionOr<void> update_selectedcontent();
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#the-select-element%3Asuffering-from-being-missing
|
|
virtual bool suffering_from_being_missing() const override;
|
|
|
|
virtual bool is_mutable() const override;
|
|
|
|
private:
|
|
HTMLSelectElement(DOM::Document&, DOM::QualifiedName);
|
|
|
|
virtual void initialize(JS::Realm&) override;
|
|
virtual void visit_edges(Cell::Visitor&) override;
|
|
|
|
// ^DOM::Element
|
|
virtual i32 default_tab_index_value() const override;
|
|
|
|
virtual void computed_properties_changed() override;
|
|
|
|
virtual void children_changed(ChildrenChangedMetadata const&) override;
|
|
bool can_skip_children_changed_selectedness_update(ChildrenChangedMetadata const& metadata) const;
|
|
|
|
void update_cached_list_of_options() const;
|
|
void show_the_picker_if_applicable();
|
|
|
|
void create_shadow_tree_if_needed();
|
|
// https://html.spec.whatwg.org/multipage/form-elements.html#send-select-update-notifications
|
|
void send_select_update_notifications();
|
|
|
|
u32 display_size() const;
|
|
|
|
mutable Vector<GC::Ref<HTMLOptionElement>> m_cached_list_of_options;
|
|
mutable size_t m_cached_number_of_selected_options { 0 };
|
|
|
|
mutable GC::Ptr<HTMLOptionsCollection> m_options;
|
|
GC::Ptr<DOM::HTMLCollection> m_selected_options;
|
|
bool m_is_open { false };
|
|
Vector<SelectItem> m_select_items;
|
|
GC::Ptr<DOM::Element> m_inner_text_element;
|
|
GC::Ptr<DOM::Element> m_chevron_icon_element;
|
|
|
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#user-validity
|
|
bool m_user_validity { false };
|
|
};
|
|
|
|
}
|
|
|
|
namespace Web::DOM {
|
|
|
|
template<>
|
|
inline bool Node::fast_is<HTML::HTMLSelectElement>() const { return is_html_select_element(); }
|
|
|
|
}
|