From 5c277144d889e5e794b1b5e578f673ade2f91e58 Mon Sep 17 00:00:00 2001 From: Bastiaan van der Plaat Date: Mon, 8 Apr 2024 21:50:35 +0200 Subject: [PATCH] LibWeb: Add select and option collection set length --- Tests/LibWeb/Text/expected/select.txt | 4 ++ Tests/LibWeb/Text/input/select.html | 40 +++++++++++++++++++ .../LibWeb/HTML/HTMLOptionsCollection.cpp | 37 +++++++++++++++++ .../LibWeb/HTML/HTMLOptionsCollection.h | 3 ++ .../LibWeb/HTML/HTMLOptionsCollection.idl | 2 +- .../LibWeb/HTML/HTMLSelectElement.cpp | 8 +++- .../Libraries/LibWeb/HTML/HTMLSelectElement.h | 3 +- .../LibWeb/HTML/HTMLSelectElement.idl | 3 +- 8 files changed, 95 insertions(+), 5 deletions(-) diff --git a/Tests/LibWeb/Text/expected/select.txt b/Tests/LibWeb/Text/expected/select.txt index f10468bcba1..f7fbd0f67e8 100644 --- a/Tests/LibWeb/Text/expected/select.txt +++ b/Tests/LibWeb/Text/expected/select.txt @@ -7,3 +7,7 @@ 7. 45 8. 0 9. 0 +10. 3 +11. 999 +12. 10 +13. 10 diff --git a/Tests/LibWeb/Text/input/select.html b/Tests/LibWeb/Text/input/select.html index f27c66298b3..3db8cbccb2f 100644 --- a/Tests/LibWeb/Text/input/select.html +++ b/Tests/LibWeb/Text/input/select.html @@ -95,5 +95,45 @@ const select = document.createElement('select'); return select.size; }); + + // 10. Use select set length to remove options + testPart(() => { + const select = document.createElement('select'); + for (let i = 0; i < 10; i++) { + select.appendChild(document.createElement('option')); + } + select.length = 3; + return select.length; + }); + + // 11. Use select set length to add options + testPart(() => { + const select = document.createElement('select'); + for (let i = 0; i < 10; i++) { + select.appendChild(document.createElement('option')); + } + select.length = 999; + return select.length; + }); + + // 12. Use select set length to add options with invalid large size + testPart(() => { + const select = document.createElement('select'); + for (let i = 0; i < 10; i++) { + select.appendChild(document.createElement('option')); + } + select.length = 100_001; + return select.length; + }); + + // 13. Use select set length to add options with invalid small size + testPart(() => { + const select = document.createElement('select'); + for (let i = 0; i < 10; i++) { + select.appendChild(document.createElement('option')); + } + select.length = -10; + return select.length; + }); }); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp index 4a2ff0eb406..4100c1beeee 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp @@ -5,10 +5,12 @@ */ #include +#include #include #include #include #include +#include #include namespace Web::HTML { @@ -33,6 +35,41 @@ void HTMLOptionsCollection::initialize(JS::Realm& realm) WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLOptionsCollection); } +// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-length +WebIDL::ExceptionOr HTMLOptionsCollection::set_length(WebIDL::UnsignedLong value) +{ + // 1. Let current be the number of nodes represented by the collection. + auto current = static_cast(length()); + + // 2. If the given value is greater than current, then: + if (value > current) { + // 2.1. If the given value is greater than 100,000, then return. + if (value > 100'000) + return {}; + + // 2.2. Let n be value − current. + auto n = value - current; + + // 2.3. Append n new option elements with no attributes and no child nodes to the select element on which this is rooted. + // Mutation events must be fired as if a DocumentFragment containing the new option elements had been inserted. + auto root_element = root(); + for (WebIDL::UnsignedLong i = 0; i < n; i++) + TRY(root_element->append_child(TRY(DOM::create_element(root_element->document(), HTML::TagNames::option, Namespace::HTML)))); + } + + // 3. If the given value is less than current, then: + if (value < current) { + // 3.1. Let n be current − value. + auto n = current - value; + + // 3.2. Remove the last n nodes in the collection from their parent nodes. + for (WebIDL::UnsignedLong i = current - 1; i >= current - n; i--) + this->item(i)->remove(); + } + + return {}; +} + // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-add WebIDL::ExceptionOr HTMLOptionsCollection::add(HTMLOptionOrOptGroupElement element, Optional before) { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h index f871902aaac..f7414c838ea 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Web::HTML { @@ -23,6 +24,8 @@ public: [[nodiscard]] static JS::NonnullGCPtr create(DOM::ParentNode& root, Function filter); virtual ~HTMLOptionsCollection() override; + WebIDL::ExceptionOr set_length(WebIDL::UnsignedLong); + WebIDL::ExceptionOr add(HTMLOptionOrOptGroupElement element, Optional before = {}); private: diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl index 6b94089a448..846839b9162 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl @@ -5,7 +5,7 @@ // https://html.spec.whatwg.org/#htmloptionscollection [Exposed=Window] interface HTMLOptionsCollection : HTMLCollection { - // [CEReactions] attribute unsigned long length; // shadows inherited length + [CEReactions] attribute unsigned long length; // shadows inherited length // [CEReactions] setter undefined (unsigned long index, HTMLOptionElement? option); [CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null); // [CEReactions] undefined remove(long index); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index 094a4e10ffe..8f2f2d830b9 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -100,12 +100,18 @@ JS::GCPtr const& HTMLSelectElement::options() } // https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-length -size_t HTMLSelectElement::length() +WebIDL::UnsignedLong HTMLSelectElement::length() { // 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. return const_cast(*options()).length(); } +WebIDL::ExceptionOr HTMLSelectElement::set_length(WebIDL::UnsignedLong length) +{ + // On setting, it must act like the attribute of the same name on the options collection. + return const_cast(*options()).set_length(length); +} + // https://html.spec.whatwg.org/multipage/form-elements.html#dom-select-item DOM::Element* HTMLSelectElement::item(size_t index) { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h index 069f5fe6450..f815f4b906e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h @@ -34,7 +34,8 @@ public: JS::GCPtr const& options(); - size_t length(); + WebIDL::UnsignedLong length(); + WebIDL::ExceptionOr set_length(WebIDL::UnsignedLong); DOM::Element* item(size_t index); DOM::Element* named_item(FlyString const& name); WebIDL::ExceptionOr add(HTMLOptionOrOptGroupElement element, Optional before = {}); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl index 893a90a1013..1638cef9a9e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl @@ -18,8 +18,7 @@ interface HTMLSelectElement : HTMLElement { readonly attribute DOMString type; [SameObject] readonly attribute HTMLOptionsCollection options; - // FIXME: This isn't readonly - [CEReactions] readonly attribute unsigned long length; + [CEReactions] attribute unsigned long length; // FIXME: Element is really HTMLOptionElement getter Element? item(unsigned long index); // FIXME: Element is really HTMLOptionElement