2021-04-22 21:11:20 +02:00
/*
2022-09-01 20:50:16 +02:00
* Copyright ( c ) 2021 - 2022 , Andreas Kling < kling @ serenityos . org >
2021-09-26 15:14:37 +01:00
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2021-04-22 21:11:20 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-09-25 16:15:49 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-03-19 15:41:02 +01:00
# include <LibWeb/DOM/Document.h>
2021-04-22 21:11:20 +02:00
# include <LibWeb/DOM/Element.h>
# include <LibWeb/DOM/HTMLCollection.h>
# include <LibWeb/DOM/ParentNode.h>
2021-09-26 15:14:37 +01:00
# include <LibWeb/Namespace.h>
2021-04-22 21:11:20 +02:00
namespace Web : : DOM {
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( HTMLCollection ) ;
2023-08-13 13:05:26 +02:00
JS : : NonnullGCPtr < HTMLCollection > HTMLCollection : : create ( ParentNode & root , Scope scope , Function < bool ( Element const & ) > filter )
2022-09-01 20:50:16 +02:00
{
2023-08-13 13:05:26 +02:00
return root . heap ( ) . allocate < HTMLCollection > ( root . realm ( ) , root , scope , move ( filter ) ) ;
2022-09-01 20:50:16 +02:00
}
2023-05-23 11:25:07 +02:00
HTMLCollection : : HTMLCollection ( ParentNode & root , Scope scope , Function < bool ( Element const & ) > filter )
2024-01-09 16:05:03 -07:00
: PlatformObject ( root . realm ( ) )
2022-09-01 20:50:16 +02:00
, m_root ( root )
2021-04-22 21:11:20 +02:00
, m_filter ( move ( filter ) )
2023-05-23 11:25:07 +02:00
, m_scope ( scope )
2021-04-22 21:11:20 +02:00
{
2024-01-09 16:05:03 -07:00
m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
. supports_indexed_properties = true ,
. supports_named_properties = true ,
. has_legacy_unenumerable_named_properties_interface_extended_attribute = true ,
} ;
2021-04-22 21:11:20 +02:00
}
2022-03-14 13:21:51 -06:00
HTMLCollection : : ~ HTMLCollection ( ) = default ;
2021-04-22 21:11:20 +02:00
2023-08-07 08:41:28 +02:00
void HTMLCollection : : initialize ( JS : : Realm & realm )
2023-01-10 06:56:59 -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 ( HTMLCollection ) ;
2023-01-10 06:56:59 -05:00
}
2022-09-01 20:50:16 +02:00
void HTMLCollection : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_root ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_cached_elements ) ;
2022-09-01 20:50:16 +02:00
}
2024-04-01 15:08:22 +02:00
void HTMLCollection : : update_cache_if_needed ( ) const
2021-04-22 21:11:20 +02:00
{
2024-04-01 15:08:22 +02:00
// Nothing to do, the DOM hasn't updated since we last built the cache.
if ( m_cached_dom_tree_version = = root ( ) - > document ( ) . dom_tree_version ( ) )
return ;
m_cached_elements . clear ( ) ;
if ( m_scope = = Scope : : Descendants ) {
m_root - > for_each_in_subtree_of_type < Element > ( [ & ] ( auto & element ) {
if ( m_filter ( element ) )
m_cached_elements . append ( element ) ;
return IterationDecision : : Continue ;
} ) ;
} else {
m_root - > for_each_child_of_type < Element > ( [ & ] ( auto & element ) {
if ( m_filter ( element ) )
m_cached_elements . append ( element ) ;
return IterationDecision : : Continue ;
} ) ;
2023-05-23 11:25:07 +02:00
}
2024-04-01 15:08:22 +02:00
m_cached_dom_tree_version = root ( ) - > document ( ) . dom_tree_version ( ) ;
}
2024-03-19 15:41:02 +01:00
2024-04-01 15:08:22 +02:00
JS : : MarkedVector < JS : : NonnullGCPtr < Element > > HTMLCollection : : collect_matching_elements ( ) const
{
update_cache_if_needed ( ) ;
2024-03-31 21:55:43 +02:00
JS : : MarkedVector < JS : : NonnullGCPtr < Element > > elements ( heap ( ) ) ;
2024-03-19 15:41:02 +01:00
for ( auto & element : m_cached_elements )
elements . append ( element ) ;
2021-04-22 21:11:20 +02:00
return elements ;
}
2021-09-26 15:14:37 +01:00
// https://dom.spec.whatwg.org/#dom-htmlcollection-length
2023-08-19 13:41:57 +12:00
size_t HTMLCollection : : length ( ) const
2021-04-22 21:11:20 +02:00
{
2021-09-26 15:14:37 +01:00
// The length getter steps are to return the number of nodes represented by the collection.
2024-04-01 15:31:12 +02:00
update_cache_if_needed ( ) ;
return m_cached_elements . size ( ) ;
2021-04-22 21:11:20 +02:00
}
2021-09-26 15:14:37 +01:00
// https://dom.spec.whatwg.org/#dom-htmlcollection-item
Element * HTMLCollection : : item ( size_t index ) const
2021-04-22 21:11:20 +02:00
{
2021-09-26 15:14:37 +01:00
// The item(index) method steps are to return the indexth element in the collection. If there is no indexth element in the collection, then the method must return null.
2024-04-01 15:31:12 +02:00
update_cache_if_needed ( ) ;
if ( index > = m_cached_elements . size ( ) )
2021-04-22 21:11:20 +02:00
return nullptr ;
2024-04-01 15:31:12 +02:00
return m_cached_elements [ index ] ;
2021-04-22 21:11:20 +02:00
}
2021-09-26 15:14:37 +01:00
// https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key
2024-04-26 22:27:27 +12:00
Element * HTMLCollection : : named_item ( FlyString const & key ) const
2021-04-22 21:11:20 +02:00
{
2021-09-26 15:14:37 +01:00
// 1. If key is the empty string, return null.
2024-04-26 22:27:27 +12:00
if ( key . is_empty ( ) )
2021-04-22 21:11:20 +02:00
return nullptr ;
2024-04-01 15:31:12 +02:00
update_cache_if_needed ( ) ;
2021-09-26 15:14:37 +01:00
// 2. Return the first element in the collection for which at least one of the following is true:
2024-04-26 22:27:27 +12:00
for ( auto const & element : m_cached_elements ) {
// - it has an ID which is key;
if ( element - > id ( ) = = key )
return element ;
// - it is in the HTML namespace and has a name attribute whose value is key;
if ( element - > namespace_uri ( ) = = Namespace : : HTML & & element - > name ( ) = = key )
return element ;
}
// or null if there is no such element.
2021-04-22 21:11:20 +02:00
return nullptr ;
}
2021-09-26 15:14:37 +01:00
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names
2023-12-24 20:59:00 +01:00
Vector < FlyString > HTMLCollection : : supported_property_names ( ) const
2021-09-26 15:14:37 +01:00
{
// 1. Let result be an empty list.
2023-12-24 20:59:00 +01:00
Vector < FlyString > result ;
2021-09-26 15:14:37 +01:00
// 2. For each element represented by the collection, in tree order:
2024-04-01 15:31:12 +02:00
update_cache_if_needed ( ) ;
for ( auto const & element : m_cached_elements ) {
2021-09-26 15:14:37 +01:00
// 1. If element has an ID which is not in result, append element’ s ID to result.
2023-12-24 21:01:00 +01:00
if ( auto const & id = element - > id ( ) ; id . has_value ( ) ) {
if ( ! result . contains_slow ( id . value ( ) ) )
result . append ( id . value ( ) ) ;
2021-09-26 15:14:37 +01:00
}
// 2. If element is in the HTML namespace and has a name attribute whose value is neither the empty string nor is in result, append element’ s name attribute value to result.
2024-01-13 20:12:25 +13:00
if ( element - > namespace_uri ( ) = = Namespace : : HTML & & element - > name ( ) . has_value ( ) ) {
auto name = element - > name ( ) . value ( ) ;
if ( ! name . is_empty ( ) & & ! result . contains_slow ( name ) )
result . append ( move ( name ) ) ;
2021-09-26 15:14:37 +01:00
}
}
// 3. Return result.
return result ;
}
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices%E2%91%A1
bool HTMLCollection : : is_supported_property_index ( u32 index ) const
{
// The object’ s supported property indices are the numbers in the range zero to one less than the number of elements represented by the collection.
// If there are no such elements, then there are no supported property indices.
2024-04-01 15:20:02 +02:00
return index < length ( ) ;
2021-09-26 15:14:37 +01:00
}
2023-02-28 00:05:39 +00:00
WebIDL : : ExceptionOr < JS : : Value > HTMLCollection : : item_value ( size_t index ) const
2022-09-01 20:50:16 +02:00
{
auto * element = item ( index ) ;
if ( ! element )
return JS : : js_undefined ( ) ;
2024-04-01 15:23:31 +02:00
return element ;
2022-09-01 20:50:16 +02:00
}
2024-04-01 15:23:31 +02:00
WebIDL : : ExceptionOr < JS : : Value > HTMLCollection : : named_item_value ( FlyString const & name ) const
2022-09-01 20:50:16 +02:00
{
2024-04-01 15:23:31 +02:00
auto * element = named_item ( name ) ;
2022-09-01 20:50:16 +02:00
if ( ! element )
return JS : : js_undefined ( ) ;
2024-04-01 15:23:31 +02:00
return element ;
2022-09-01 20:50:16 +02:00
}
2021-04-22 21:11:20 +02:00
}