2022-03-16 12:58:28 +01:00
/*
* Copyright ( c ) 2022 , the SerenityOS developers .
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLOptionsCollectionPrototype.h>
2022-09-25 16:38:21 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-04-08 21:50:35 +02:00
# include <LibWeb/DOM/ElementFactory.h>
2022-03-21 20:02:40 -04:00
# include <LibWeb/HTML/HTMLOptGroupElement.h>
# include <LibWeb/HTML/HTMLOptionElement.h>
2022-03-16 12:58:28 +01:00
# include <LibWeb/HTML/HTMLOptionsCollection.h>
2022-03-21 20:02:40 -04:00
# include <LibWeb/HTML/HTMLSelectElement.h>
2024-04-08 21:50:35 +02:00
# include <LibWeb/Namespace.h>
2022-09-25 17:28:46 +01:00
# include <LibWeb/WebIDL/DOMException.h>
2022-03-16 12:58:28 +01:00
namespace Web : : HTML {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( HTMLOptionsCollection ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < HTMLOptionsCollection > HTMLOptionsCollection : : create ( DOM : : ParentNode & root , Function < bool ( DOM : : Element const & ) > filter )
2022-09-01 20:50:16 +02:00
{
2024-11-14 05:50:17 +13:00
return root . realm ( ) . create < HTMLOptionsCollection > ( root , move ( filter ) ) ;
2022-09-01 20:50:16 +02:00
}
2022-03-16 12:58:28 +01:00
HTMLOptionsCollection : : HTMLOptionsCollection ( DOM : : ParentNode & root , Function < bool ( DOM : : Element const & ) > filter )
2023-05-23 11:25:07 +02:00
: DOM : : HTMLCollection ( root , Scope : : Descendants , move ( filter ) )
2022-03-16 12:58:28 +01:00
{
2024-07-14 15:24:02 -06:00
m_legacy_platform_object_flags - > has_indexed_property_setter = true ;
m_legacy_platform_object_flags - > indexed_property_setter_has_identifier = true ;
2022-03-16 12:58:28 +01:00
}
2022-09-01 20:50:16 +02:00
HTMLOptionsCollection : : ~ HTMLOptionsCollection ( ) = default ;
2023-08-07 08:41:28 +02:00
void HTMLOptionsCollection : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLOptionsCollection ) ;
2023-01-10 06:28:20 -05:00
}
2024-04-08 21:50:35 +02:00
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-length
WebIDL : : ExceptionOr < void > HTMLOptionsCollection : : set_length ( WebIDL : : UnsignedLong value )
{
// 1. Let current be the number of nodes represented by the collection.
auto current = static_cast < WebIDL : : UnsignedLong > ( 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.
2024-07-27 13:33:51 +01:00
for ( auto i = 0u ; i < n ; i + + )
item ( length ( ) - 1 ) - > remove ( ) ;
2024-04-08 21:50:35 +02:00
}
return { } ;
}
2024-07-14 15:24:02 -06:00
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-setter
WebIDL : : ExceptionOr < void > HTMLOptionsCollection : : set_value_of_indexed_property ( u32 index , JS : : Value unconverted_option )
{
// The spec doesn't seem to require this, but it's consistent with length handling and other browsers
if ( index > = 100'000 ) {
return { } ;
}
// 1. If value is null, invoke the steps for the remove method with index as the argument, and return.
if ( unconverted_option . is_null ( ) ) {
remove ( static_cast < WebIDL : : Long > ( index ) ) ;
return { } ;
}
if ( ! unconverted_option . is_object ( ) | | ! is < HTMLOptionElement > ( unconverted_option . as_object ( ) ) ) {
2024-10-12 20:56:21 +02:00
return WebIDL : : TypeMismatchError : : create ( realm ( ) , " The value provided is not an HTMLOptionElement " _string ) ;
2024-07-14 15:24:02 -06:00
}
auto & option = static_cast < HTMLOptionElement & > ( unconverted_option . as_object ( ) ) ;
// 2. Let length be the number of nodes represented by the collection.
auto length = this - > length ( ) ;
auto root_element = root ( ) ;
if ( index > = length ) {
// 3. Let n be index minus length.
auto n = index - length ;
// 4. If n is greater than zero, then append a DocumentFragment consisting of n-1 new option elements with no attributes and no child nodes to the select element on which the HTMLOptionsCollection is rooted.
if ( n > 0 ) {
for ( WebIDL : : UnsignedLong i = 0 ; i < n - 1 ; i + + ) {
TRY ( root_element - > append_child ( TRY ( DOM : : create_element ( root_element - > document ( ) , HTML : : TagNames : : option , Namespace : : HTML ) ) ) ) ;
}
}
// 5. If n is greater than or equal to zero, append value to the select element.
TRY ( root_element - > append_child ( option ) ) ;
} else {
// 5 (cont). Otherwise, replace the indexth element in the collection by value.
TRY ( root_element - > replace_child ( option , * item ( index ) ) ) ;
}
return { } ;
}
2022-03-21 20:02:40 -04:00
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-add
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < void > HTMLOptionsCollection : : add ( HTMLOptionOrOptGroupElement element , Optional < HTMLElementOrElementIndex > before )
2022-03-21 20:02:40 -04:00
{
2022-08-28 13:42:07 +02:00
auto resolved_element = element . visit (
2024-11-15 04:01:23 +13:00
[ ] ( auto & e ) - > GC : : Root < HTMLElement > {
return GC : : make_root ( static_cast < HTML : : HTMLElement & > ( * e ) ) ;
2022-08-28 13:42:07 +02:00
} ) ;
2022-03-21 20:02:40 -04:00
2024-11-15 04:01:23 +13:00
GC : : Ptr < DOM : : Node > before_element ;
if ( before . has_value ( ) & & before - > has < GC : : Root < HTMLElement > > ( ) )
before_element = before - > get < GC : : Root < HTMLElement > > ( ) . ptr ( ) ;
2022-03-21 20:02:40 -04:00
// 1. If element is an ancestor of the select element on which the HTMLOptionsCollection is rooted, then throw a "HierarchyRequestError" DOMException.
if ( resolved_element - > is_ancestor_of ( root ( ) ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : HierarchyRequestError : : create ( realm ( ) , " The provided element is an ancestor of the root select element. " _string ) ;
2022-03-21 20:02:40 -04:00
// 2. If before is an element, but that element isn't a descendant of the select element on which the HTMLOptionsCollection is rooted, then throw a "NotFoundError" DOMException.
if ( before_element & & ! before_element - > is_descendant_of ( root ( ) ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : NotFoundError : : create ( realm ( ) , " The 'before' element is not a descendant of the root select element. " _string ) ;
2022-03-21 20:02:40 -04:00
// 3. If element and before are the same element, then return.
if ( before_element & & ( resolved_element . ptr ( ) = = before_element . ptr ( ) ) )
return { } ;
// 4. If before is a node, then let reference be that node. Otherwise, if before is an integer, and there is a beforeth node in the collection, let reference be that node. Otherwise, let reference be null.
2024-11-15 04:01:23 +13:00
GC : : Ptr < DOM : : Node > reference ;
2022-03-21 20:02:40 -04:00
if ( before_element )
reference = move ( before_element ) ;
else if ( before . has_value ( ) & & before - > has < i32 > ( ) )
reference = item ( before - > get < i32 > ( ) ) ;
// 5. If reference is not null, let parent be the parent node of reference. Otherwise, let parent be the select element on which the HTMLOptionsCollection is rooted.
DOM : : Node * parent = reference ? reference - > parent ( ) : root ( ) . ptr ( ) ;
// 6. Pre-insert element into parent node before reference.
2022-08-28 13:42:07 +02:00
( void ) TRY ( parent - > pre_insert ( * resolved_element , reference ) ) ;
2022-03-21 20:02:40 -04:00
return { } ;
}
2025-02-04 13:01:46 +01:00
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-remove
2024-04-08 21:56:33 +02:00
void HTMLOptionsCollection : : remove ( WebIDL : : Long index )
{
// 1. If the number of nodes represented by the collection is zero, return.
if ( length ( ) = = 0 )
return ;
// 2. If index is not a number greater than or equal to 0 and less than the number of nodes represented by the collection, return.
if ( index < 0 | | static_cast < WebIDL : : UnsignedLong > ( index ) > = length ( ) )
return ;
// 3. Let element be the indexth element in the collection.
auto * element = this - > item ( index ) ;
// 4. Remove element from its parent node.
element - > remove ( ) ;
}
2025-02-04 13:01:46 +01:00
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-selectedindex
2024-04-08 21:53:22 +02:00
WebIDL : : Long HTMLOptionsCollection : : selected_index ( ) const
{
// The selectedIndex IDL attribute must act like the identically named attribute
// on the select element on which the HTMLOptionsCollection is rooted.
2025-01-21 09:12:05 -05:00
return as < HTMLSelectElement > ( * root ( ) ) . selected_index ( ) ;
2024-04-08 21:53:22 +02:00
}
void HTMLOptionsCollection : : set_selected_index ( WebIDL : : Long index )
{
2025-01-21 09:12:05 -05:00
as < HTMLSelectElement > ( * root ( ) ) . set_selected_index ( index ) ;
2024-04-08 21:53:22 +02:00
}
2022-03-16 12:58:28 +01:00
}