Serenity Operating System
at master 158 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2020, the SerenityOS developers. 3 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/StringBuilder.h> 9#include <LibWeb/ARIA/Roles.h> 10#include <LibWeb/Bindings/Intrinsics.h> 11#include <LibWeb/DOM/Node.h> 12#include <LibWeb/DOM/Text.h> 13#include <LibWeb/HTML/HTMLOptGroupElement.h> 14#include <LibWeb/HTML/HTMLOptionElement.h> 15#include <LibWeb/HTML/HTMLScriptElement.h> 16#include <LibWeb/HTML/HTMLSelectElement.h> 17#include <LibWeb/Infra/Strings.h> 18 19namespace Web::HTML { 20 21HTMLOptionElement::HTMLOptionElement(DOM::Document& document, DOM::QualifiedName qualified_name) 22 : HTMLElement(document, move(qualified_name)) 23{ 24} 25 26HTMLOptionElement::~HTMLOptionElement() = default; 27 28JS::ThrowCompletionOr<void> HTMLOptionElement::initialize(JS::Realm& realm) 29{ 30 MUST_OR_THROW_OOM(Base::initialize(realm)); 31 set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLOptionElementPrototype>(realm, "HTMLOptionElement")); 32 33 return {}; 34} 35 36void HTMLOptionElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) 37{ 38 HTMLElement::parse_attribute(name, value); 39 40 if (name == HTML::AttributeNames::selected) { 41 // Except where otherwise specified, when the element is created, its selectedness must be set to true 42 // if the element has a selected attribute. Whenever an option element's selected attribute is added, 43 // if its dirtiness is false, its selectedness must be set to true. 44 if (!m_dirty) 45 m_selected = true; 46 } 47} 48 49void HTMLOptionElement::did_remove_attribute(DeprecatedFlyString const& name) 50{ 51 HTMLElement::did_remove_attribute(name); 52 53 if (name == HTML::AttributeNames::selected) { 54 // Whenever an option element's selected attribute is removed, if its dirtiness is false, its selectedness must be set to false. 55 if (!m_dirty) 56 m_selected = false; 57 } 58} 59 60// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-selected 61void HTMLOptionElement::set_selected(bool selected) 62{ 63 // On setting, it must set the element's selectedness to the new value, set its dirtiness to true, and then cause the element to ask for a reset. 64 m_selected = selected; 65 m_dirty = true; 66 ask_for_a_reset(); 67} 68 69// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value 70DeprecatedString HTMLOptionElement::value() const 71{ 72 // The value of an option element is the value of the value content attribute, if there is one. 73 if (auto value_attr = get_attribute(HTML::AttributeNames::value); !value_attr.is_null()) 74 return value_attr; 75 76 // ...or, if there is not, the value of the element's text IDL attribute. 77 return text(); 78} 79 80// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value 81void HTMLOptionElement::set_value(DeprecatedString value) 82{ 83 MUST(set_attribute(HTML::AttributeNames::value, value)); 84} 85 86static void concatenate_descendants_text_content(DOM::Node const* node, StringBuilder& builder) 87{ 88 // FIXME: SVGScriptElement should also be skipped, but it doesn't exist yet. 89 if (is<HTMLScriptElement>(node)) 90 return; 91 if (is<DOM::Text>(node)) 92 builder.append(verify_cast<DOM::Text>(node)->data()); 93 node->for_each_child([&](auto const& node) { 94 concatenate_descendants_text_content(&node, builder); 95 }); 96} 97 98// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text 99DeprecatedString HTMLOptionElement::text() const 100{ 101 StringBuilder builder; 102 103 // Concatenation of data of all the Text node descendants of the option element, in tree order, 104 // excluding any that are descendants of descendants of the option element that are themselves 105 // script or SVG script elements. 106 for_each_child([&](auto const& node) { 107 concatenate_descendants_text_content(&node, builder); 108 }); 109 110 // Return the result of stripping and collapsing ASCII whitespace from the above concatenation. 111 return Infra::strip_and_collapse_whitespace(builder.string_view()).release_value_but_fixme_should_propagate_errors().to_deprecated_string(); 112} 113 114// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text 115void HTMLOptionElement::set_text(DeprecatedString text) 116{ 117 string_replace_all(text); 118} 119 120// https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-index 121int HTMLOptionElement::index() const 122{ 123 // An option element's index is the number of option elements that are in the same list of options but that come before it in tree order. 124 if (auto select_element = first_ancestor_of_type<HTMLSelectElement>()) { 125 int index = 0; 126 for (auto const& option_element : select_element->list_of_options()) { 127 if (option_element.ptr() == this) 128 return index; 129 ++index; 130 } 131 } 132 133 // If the option element is not in a list of options, then the option element's index is zero. 134 return 0; 135} 136 137// https://html.spec.whatwg.org/multipage/form-elements.html#ask-for-a-reset 138void HTMLOptionElement::ask_for_a_reset() 139{ 140 // FIXME: Implement this operation. 141} 142 143// https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-disabled 144bool HTMLOptionElement::disabled() const 145{ 146 // An option element is disabled if its disabled attribute is present or if it is a child of an optgroup element whose disabled attribute is present. 147 return has_attribute(AttributeNames::disabled) 148 || (parent() && is<HTMLOptGroupElement>(parent()) && static_cast<HTMLOptGroupElement const&>(*parent()).has_attribute(AttributeNames::disabled)); 149} 150 151Optional<ARIA::Role> HTMLOptionElement::default_role() const 152{ 153 // https://www.w3.org/TR/html-aria/#el-option 154 // TODO: Only an option element that is in a list of options or that represents a suggestion in a datalist should return option 155 return ARIA::Role::option; 156} 157 158}