diff --git a/Tests/LibWeb/Text/expected/radio-node-list.txt b/Tests/LibWeb/Text/expected/radio-node-list.txt new file mode 100644 index 0000000000..66262d0c32 --- /dev/null +++ b/Tests/LibWeb/Text/expected/radio-node-list.txt @@ -0,0 +1,47 @@ + value1 value2 =================== +non-checked-with-value +=================== +2 +value1 +value2 +value = '' +value = '' +=================== +checked-with-value +=================== +2 +value3 +value4 +value = 'value3' +value = 'value3' +=================== +checked-no-value +=================== +2 +on +on +value = 'on' +value = 'on' +=================== +no-radio-button +=================== +2 +value1 +value2 +value = '' +value = '' +=================== +non-checked-with-value +=================== +value = +value = value1 +=================== +non-checked-no-value +=================== +value = +value = on +=================== +non-checked-value-on +=================== +value = +value = on diff --git a/Tests/LibWeb/Text/input/radio-node-list.html b/Tests/LibWeb/Text/input/radio-node-list.html new file mode 100644 index 0000000000..5a0e948988 --- /dev/null +++ b/Tests/LibWeb/Text/input/radio-node-list.html @@ -0,0 +1,52 @@ +
+ + + + + + + + + + + + +
+ + diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp b/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp index 2828064410..92db6fef50 100644 --- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp +++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Web::DOM { @@ -29,4 +30,80 @@ void RadioNodeList::initialize(JS::Realm& realm) set_prototype(&Bindings::ensure_web_prototype(realm, "RadioNodeList")); } +static HTML::HTMLInputElement const* radio_button(Node const& node) +{ + if (!is(node)) + return nullptr; + + auto const& input_element = verify_cast(node); + if (input_element.type_state() != HTML::HTMLInputElement::TypeAttributeState::RadioButton) + return nullptr; + + return &input_element; +} + +// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-radionodelist-value +FlyString RadioNodeList::value() const +{ + // 1. Let element be the first element in tree order represented by the RadioNodeList object that is an input element whose type + // attribute is in the Radio Button state and whose checkedness is true. Otherwise, let it be null. + auto* element = verify_cast(first_matching([](Node const& node) -> bool { + auto const* button = radio_button(node); + if (!button) + return false; + + return button->checked(); + })); + + // 2. If element is null, return the empty string. + if (!element) + return String {}; + + // 3. If element is an element with no value attribute, return the string "on". + auto const value = element->get_attribute(HTML::AttributeNames::value); + if (value.is_null()) + return "on"_fly_string; + + // 4. Otherwise, return the value of element's value attribute. + return MUST(FlyString::from_deprecated_fly_string(value)); +} + +void RadioNodeList::set_value(FlyString const& value) +{ + HTML::HTMLInputElement* element = nullptr; + + auto deprecated_value = value.to_deprecated_fly_string(); + + // 1. If the new value is the string "on": let element be the first element in tree order represented by the RadioNodeList object + // that is an input element whose type attribute is in the Radio Button state and whose value content attribute is either absent, + // or present and equal to the new value, if any. If no such element exists, then instead let element be null. + if (value == "on"sv) { + element = verify_cast(first_matching([&deprecated_value](auto const& node) { + auto const* button = radio_button(node); + if (!button) + return false; + + auto const value = button->get_attribute(HTML::AttributeNames::value); + return value.is_null() || value == deprecated_value; + })); + } + // 2. Otherwise: let element be the first element in tree order represented by the RadioNodeList object that is an input element whose + // type attribute is in the Radio Button state and whose value content attribute is present and equal to the new value, if any. If + // no such element exists, then instead let element be null. + else { + element = verify_cast(first_matching([&deprecated_value](auto const& node) { + auto const* button = radio_button(node); + if (!button) + return false; + + auto const value = button->get_attribute(HTML::AttributeNames::value); + return !value.is_null() && value == deprecated_value; + })); + } + + // 3. If element is not null, then set its checkedness to true. + if (element) + element->set_checked(true); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.h b/Userland/Libraries/LibWeb/DOM/RadioNodeList.h index cd2cb0d202..8204ca7740 100644 --- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.h +++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.h @@ -19,6 +19,9 @@ public: virtual ~RadioNodeList() override; + FlyString value() const; + void set_value(FlyString const&); + protected: virtual void initialize(JS::Realm&) override; diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl b/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl index 66a6ffc47a..e7b2676d27 100644 --- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl +++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl @@ -3,5 +3,5 @@ // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#radionodelist [Exposed=Window, UseNewAKString] interface RadioNodeList : NodeList { - // FIXME: attribute DOMString value; + attribute DOMString value; };