Skip to content

Commit 00099a8

Browse files
Igoorxawesomekling
authored andcommitted
LibWeb: Bring HTMLOptionElement closer to spec
1 parent ae519c6 commit 00099a8

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
* SPDX-License-Identifier: BSD-2-Clause
66
*/
77

8+
#include <AK/StringBuilder.h>
9+
#include <LibWeb/DOM/Node.h>
10+
#include <LibWeb/DOM/Text.h>
811
#include <LibWeb/HTML/HTMLOptionElement.h>
12+
#include <LibWeb/HTML/HTMLScriptElement.h>
13+
#include <LibWeb/HTML/HTMLSelectElement.h>
14+
#include <ctype.h>
915

1016
namespace Web::HTML {
1117

@@ -49,6 +55,99 @@ void HTMLOptionElement::set_selected(bool selected)
4955
ask_for_a_reset();
5056
}
5157

58+
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
59+
String HTMLOptionElement::value() const
60+
{
61+
// The value of an option element is the value of the value content attribute, if there is one.
62+
if (auto value_attr = get_attribute(HTML::AttributeNames::value); !value_attr.is_null())
63+
return value_attr;
64+
65+
// ...or, if there is not, the value of the element's text IDL attribute.
66+
return text();
67+
}
68+
69+
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
70+
void HTMLOptionElement::set_value(String value)
71+
{
72+
set_attribute(HTML::AttributeNames::value, value);
73+
}
74+
75+
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
76+
static String strip_and_collapse_whitespace(StringView string)
77+
{
78+
// Replace any sequence of one or more consecutive code points that are ASCII whitespace in the string with a single U+0020 SPACE code point.
79+
StringBuilder builder;
80+
for (size_t i = 0; i < string.length(); ++i) {
81+
if (isspace(string[i])) {
82+
builder.append(' ');
83+
while (i < string.length()) {
84+
if (isspace(string[i])) {
85+
++i;
86+
continue;
87+
}
88+
builder.append(string[i]);
89+
break;
90+
}
91+
continue;
92+
}
93+
builder.append(string[i]);
94+
}
95+
96+
// ...and then remove any leading and trailing ASCII whitespace from that string.
97+
return builder.to_string().trim_whitespace();
98+
}
99+
100+
static void concatenate_descendants_text_content(DOM::Node const* node, StringBuilder& builder)
101+
{
102+
// FIXME: SVGScriptElement should also be skipped, but it doesn't exist yet.
103+
if (is<HTMLScriptElement>(node))
104+
return;
105+
if (is<DOM::Text>(node))
106+
builder.append(verify_cast<DOM::Text>(node)->data());
107+
node->for_each_child([&](auto const& node) {
108+
concatenate_descendants_text_content(&node, builder);
109+
});
110+
}
111+
112+
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text
113+
String HTMLOptionElement::text() const
114+
{
115+
StringBuilder builder;
116+
117+
// Concatenation of data of all the Text node descendants of the option element, in tree order,
118+
// excluding any that are descendants of descendants of the option element that are themselves
119+
// script or SVG script elements.
120+
for_each_child([&](auto const& node) {
121+
concatenate_descendants_text_content(&node, builder);
122+
});
123+
124+
// Return the result of stripping and collapsing ASCII whitespace from the above concatenation.
125+
return strip_and_collapse_whitespace(builder.to_string());
126+
}
127+
128+
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text
129+
void HTMLOptionElement::set_text(String text)
130+
{
131+
string_replace_all(text);
132+
}
133+
134+
// https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-index
135+
int HTMLOptionElement::index() const
136+
{
137+
// 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.
138+
if (auto select_element = first_ancestor_of_type<HTMLSelectElement>()) {
139+
int index = 0;
140+
for (auto const& option_element : select_element->list_of_options()) {
141+
if (&option_element == this)
142+
return index;
143+
++index;
144+
}
145+
}
146+
147+
// If the option element is not in a list of options, then the option element's index is zero.
148+
return 0;
149+
}
150+
52151
// https://html.spec.whatwg.org/multipage/form-elements.html#ask-for-a-reset
53152
void HTMLOptionElement::ask_for_a_reset()
54153
{

Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ class HTMLOptionElement final : public HTMLElement {
2121
bool selected() const { return m_selected; }
2222
void set_selected(bool);
2323

24+
String value() const;
25+
void set_value(String);
26+
27+
String text() const;
28+
void set_text(String);
29+
30+
int index() const;
31+
2432
private:
2533
friend class Bindings::OptionConstructor;
2634
friend class HTMLSelectElement;

Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ interface HTMLOptionElement : HTMLElement {
44

55
[Reflect] attribute boolean disabled;
66
[Reflect=selected] attribute boolean defaultSelected;
7-
87
attribute boolean selected;
8+
[CEReactions] attribute DOMString value;
9+
10+
[CEReactions] attribute DOMString text;
11+
readonly attribute long index;
912

1013
};

0 commit comments

Comments
 (0)