Serenity Operating System
at master 180 lines 7.5 kB view raw
1/* 2 * Copyright (c) 2020, the SerenityOS developers. 3 * Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibWeb/Bindings/Intrinsics.h> 9#include <LibWeb/HTML/HTMLFormElement.h> 10#include <LibWeb/HTML/HTMLOptGroupElement.h> 11#include <LibWeb/HTML/HTMLOptionElement.h> 12#include <LibWeb/HTML/HTMLSelectElement.h> 13 14namespace Web::HTML { 15 16HTMLSelectElement::HTMLSelectElement(DOM::Document& document, DOM::QualifiedName qualified_name) 17 : HTMLElement(document, move(qualified_name)) 18{ 19} 20 21HTMLSelectElement::~HTMLSelectElement() = default; 22 23JS::ThrowCompletionOr<void> HTMLSelectElement::initialize(JS::Realm& realm) 24{ 25 MUST_OR_THROW_OOM(Base::initialize(realm)); 26 set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLSelectElementPrototype>(realm, "HTMLSelectElement")); 27 28 return {}; 29} 30 31void HTMLSelectElement::visit_edges(Cell::Visitor& visitor) 32{ 33 Base::visit_edges(visitor); 34 visitor.visit(m_options.ptr()); 35} 36 37// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-options 38JS::GCPtr<HTMLOptionsCollection> const& HTMLSelectElement::options() 39{ 40 if (!m_options) { 41 m_options = HTMLOptionsCollection::create(*this, [](DOM::Element const& element) { 42 // https://html.spec.whatwg.org/multipage/form-elements.html#concept-select-option-list 43 // The list of options for a select element consists of all the option element children of 44 // the select element, and all the option element children of all the optgroup element children 45 // of the select element, in tree order. 46 return is<HTMLOptionElement>(element); 47 }).release_value_but_fixme_should_propagate_errors(); 48 } 49 return m_options; 50} 51 52// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-length 53size_t HTMLSelectElement::length() 54{ 55 // The length IDL attribute must return the number of nodes represented by the options collection. On setting, it must act like the attribute of the same name on the options collection. 56 return const_cast<HTMLOptionsCollection&>(*options()).length(); 57} 58 59// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-item 60DOM::Element* HTMLSelectElement::item(size_t index) 61{ 62 // The item(index) method must return the value returned by the method of the same name on the options collection, when invoked with the same argument. 63 return const_cast<HTMLOptionsCollection&>(*options()).item(index); 64} 65 66// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-nameditem 67DOM::Element* HTMLSelectElement::named_item(DeprecatedFlyString const& name) 68{ 69 // The namedItem(name) method must return the value returned by the method of the same name on the options collection, when invoked with the same argument. 70 return const_cast<HTMLOptionsCollection&>(*options()).named_item(name); 71} 72 73// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-add 74WebIDL::ExceptionOr<void> HTMLSelectElement::add(HTMLOptionOrOptGroupElement element, Optional<HTMLElementOrElementIndex> before) 75{ 76 // Similarly, the add(element, before) method must act like its namesake method on that same options collection. 77 return const_cast<HTMLOptionsCollection&>(*options()).add(move(element), move(before)); 78} 79 80// https://html.spec.whatwg.org/multipage/form-elements.html#concept-select-option-list 81Vector<JS::Handle<HTMLOptionElement>> HTMLSelectElement::list_of_options() const 82{ 83 // The list of options for a select element consists of all the option element children of the select element, 84 // and all the option element children of all the optgroup element children of the select element, in tree order. 85 Vector<JS::Handle<HTMLOptionElement>> list; 86 87 for_each_child_of_type<HTMLOptionElement>([&](HTMLOptionElement& option_element) { 88 list.append(JS::make_handle(option_element)); 89 }); 90 91 for_each_child_of_type<HTMLOptGroupElement>([&](HTMLOptGroupElement const& optgroup_element) { 92 optgroup_element.for_each_child_of_type<HTMLOptionElement>([&](HTMLOptionElement& option_element) { 93 list.append(JS::make_handle(option_element)); 94 }); 95 }); 96 97 return list; 98} 99 100// https://html.spec.whatwg.org/multipage/form-elements.html#the-select-element:concept-form-reset-control 101void HTMLSelectElement::reset_algorithm() 102{ 103 // The reset algorithm for select elements is to go through all the option elements in the element's list of options, 104 for (auto const& option_element : list_of_options()) { 105 // set their selectedness to true if the option element has a selected attribute, and false otherwise, 106 option_element->m_selected = option_element->has_attribute(AttributeNames::selected); 107 // set their dirtiness to false, 108 option_element->m_dirty = false; 109 // and then have the option elements ask for a reset. 110 option_element->ask_for_a_reset(); 111 } 112} 113 114// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-selectedindex 115int HTMLSelectElement::selected_index() const 116{ 117 // The selectedIndex IDL attribute, on getting, must return the index of the first option element in the list of options 118 // in tree order that has its selectedness set to true, if any. If there isn't one, then it must return −1. 119 120 int index = 0; 121 for (auto const& option_element : list_of_options()) { 122 if (option_element->selected()) 123 return index; 124 ++index; 125 } 126 return -1; 127} 128 129void HTMLSelectElement::set_selected_index(int index) 130{ 131 // On setting, the selectedIndex attribute must set the selectedness of all the option elements in the list of options to false, 132 // and then the option element in the list of options whose index is the given new value, 133 // if any, must have its selectedness set to true and its dirtiness set to true. 134 auto options = list_of_options(); 135 for (auto& option : options) 136 option->m_selected = false; 137 138 if (index < 0 || index >= static_cast<int>(options.size())) 139 return; 140 141 auto& selected_option = options[index]; 142 selected_option->m_selected = true; 143 selected_option->m_dirty = true; 144} 145 146// https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex 147i32 HTMLSelectElement::default_tab_index_value() const 148{ 149 // See the base function for the spec comments. 150 return 0; 151} 152 153// https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-type 154DeprecatedString const& HTMLSelectElement::type() const 155{ 156 // The type IDL attribute, on getting, must return the string "select-one" if the multiple attribute is absent, and the string "select-multiple" if the multiple attribute is present. 157 static DeprecatedString select_one = "select-one"sv; 158 static DeprecatedString select_multiple = "select-multiple"sv; 159 160 if (!has_attribute(AttributeNames::multiple)) 161 return select_one; 162 163 return select_multiple; 164} 165 166Optional<ARIA::Role> HTMLSelectElement::default_role() const 167{ 168 // https://www.w3.org/TR/html-aria/#el-select-multiple-or-size-greater-1 169 if (has_attribute("multiple")) 170 return ARIA::Role::listbox; 171 if (has_attribute("size")) { 172 auto size_attribute = attribute("size").to_int(); 173 if (size_attribute.has_value() && size_attribute.value() > 1) 174 return ARIA::Role::listbox; 175 } 176 // https://www.w3.org/TR/html-aria/#el-select 177 return ARIA::Role::combobox; 178} 179 180}