ソースを参照

LibWeb: Bring HTMLOptionElement closer to spec

Igor Pissolati 3 年 前
コミット
00099a8ade

+ 99 - 0
Userland/Libraries/LibWeb/HTML/HTMLOptionElement.cpp

@@ -5,7 +5,13 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <AK/StringBuilder.h>
+#include <LibWeb/DOM/Node.h>
+#include <LibWeb/DOM/Text.h>
 #include <LibWeb/HTML/HTMLOptionElement.h>
 #include <LibWeb/HTML/HTMLOptionElement.h>
+#include <LibWeb/HTML/HTMLScriptElement.h>
+#include <LibWeb/HTML/HTMLSelectElement.h>
+#include <ctype.h>
 
 
 namespace Web::HTML {
 namespace Web::HTML {
 
 
@@ -49,6 +55,99 @@ void HTMLOptionElement::set_selected(bool selected)
     ask_for_a_reset();
     ask_for_a_reset();
 }
 }
 
 
+// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
+String HTMLOptionElement::value() const
+{
+    // The value of an option element is the value of the value content attribute, if there is one.
+    if (auto value_attr = get_attribute(HTML::AttributeNames::value); !value_attr.is_null())
+        return value_attr;
+
+    // ...or, if there is not, the value of the element's text IDL attribute.
+    return text();
+}
+
+// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
+void HTMLOptionElement::set_value(String value)
+{
+    set_attribute(HTML::AttributeNames::value, value);
+}
+
+// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
+static String strip_and_collapse_whitespace(StringView string)
+{
+    // 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.
+    StringBuilder builder;
+    for (size_t i = 0; i < string.length(); ++i) {
+        if (isspace(string[i])) {
+            builder.append(' ');
+            while (i < string.length()) {
+                if (isspace(string[i])) {
+                    ++i;
+                    continue;
+                }
+                builder.append(string[i]);
+                break;
+            }
+            continue;
+        }
+        builder.append(string[i]);
+    }
+
+    // ...and then remove any leading and trailing ASCII whitespace from that string.
+    return builder.to_string().trim_whitespace();
+}
+
+static void concatenate_descendants_text_content(DOM::Node const* node, StringBuilder& builder)
+{
+    // FIXME: SVGScriptElement should also be skipped, but it doesn't exist yet.
+    if (is<HTMLScriptElement>(node))
+        return;
+    if (is<DOM::Text>(node))
+        builder.append(verify_cast<DOM::Text>(node)->data());
+    node->for_each_child([&](auto const& node) {
+        concatenate_descendants_text_content(&node, builder);
+    });
+}
+
+// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text
+String HTMLOptionElement::text() const
+{
+    StringBuilder builder;
+
+    // Concatenation of data of all the Text node descendants of the option element, in tree order,
+    // excluding any that are descendants of descendants of the option element that are themselves
+    // script or SVG script elements.
+    for_each_child([&](auto const& node) {
+        concatenate_descendants_text_content(&node, builder);
+    });
+
+    // Return the result of stripping and collapsing ASCII whitespace from the above concatenation.
+    return strip_and_collapse_whitespace(builder.to_string());
+}
+
+// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-text
+void HTMLOptionElement::set_text(String text)
+{
+    string_replace_all(text);
+}
+
+// https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-index
+int HTMLOptionElement::index() const
+{
+    // 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.
+    if (auto select_element = first_ancestor_of_type<HTMLSelectElement>()) {
+        int index = 0;
+        for (auto const& option_element : select_element->list_of_options()) {
+            if (&option_element == this)
+                return index;
+            ++index;
+        }
+    }
+
+    // If the option element is not in a list of options, then the option element's index is zero.
+    return 0;
+}
+
 // https://html.spec.whatwg.org/multipage/form-elements.html#ask-for-a-reset
 // https://html.spec.whatwg.org/multipage/form-elements.html#ask-for-a-reset
 void HTMLOptionElement::ask_for_a_reset()
 void HTMLOptionElement::ask_for_a_reset()
 {
 {

+ 8 - 0
Userland/Libraries/LibWeb/HTML/HTMLOptionElement.h

@@ -21,6 +21,14 @@ public:
     bool selected() const { return m_selected; }
     bool selected() const { return m_selected; }
     void set_selected(bool);
     void set_selected(bool);
 
 
+    String value() const;
+    void set_value(String);
+
+    String text() const;
+    void set_text(String);
+
+    int index() const;
+
 private:
 private:
     friend class Bindings::OptionConstructor;
     friend class Bindings::OptionConstructor;
     friend class HTMLSelectElement;
     friend class HTMLSelectElement;

+ 4 - 1
Userland/Libraries/LibWeb/HTML/HTMLOptionElement.idl

@@ -4,7 +4,10 @@ interface HTMLOptionElement : HTMLElement {
 
 
     [Reflect] attribute boolean disabled;
     [Reflect] attribute boolean disabled;
     [Reflect=selected] attribute boolean defaultSelected;
     [Reflect=selected] attribute boolean defaultSelected;
-
     attribute boolean selected;
     attribute boolean selected;
+    [CEReactions] attribute DOMString value;
+
+    [CEReactions] attribute DOMString text;
+    readonly attribute long index;
 
 
 };
 };