2021-04-22 21:11:20 +02:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2021 - 2022 , Andreas Kling < andreas @ ladybird . 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
*/
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLCollectionPrototype.h>
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 {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( HTMLCollection ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < HTMLCollection > HTMLCollection : : create ( ParentNode & root , Scope scope , Function < bool ( Element const & ) > filter )
2022-09-01 20:50:16 +02:00
{
2024-11-14 05:50:17 +13:00
return root . realm ( ) . create < HTMLCollection > ( 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
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLCollection ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
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 ) ;
2024-07-25 07:49:41 +02:00
if ( m_cached_name_to_element_mappings )
visitor . visit ( * m_cached_name_to_element_mappings ) ;
}
void HTMLCollection : : update_name_to_element_mappings_if_needed ( ) const
{
update_cache_if_needed ( ) ;
if ( m_cached_name_to_element_mappings )
return ;
2024-11-15 04:01:23 +13:00
m_cached_name_to_element_mappings = make < OrderedHashMap < FlyString , GC : : Ref < Element > > > ( ) ;
2024-07-25 07:49:41 +02:00
for ( auto const & element : m_cached_elements ) {
// 1. If element has an ID which is not in result, append element’ s ID to result.
if ( auto const & id = element - > id ( ) ; id . has_value ( ) ) {
if ( ! id . value ( ) . is_empty ( ) & & ! m_cached_name_to_element_mappings - > contains ( id . value ( ) ) )
m_cached_name_to_element_mappings - > set ( id . value ( ) , element ) ;
}
// 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.
if ( element - > namespace_uri ( ) = = Namespace : : HTML & & element - > name ( ) . has_value ( ) ) {
auto element_name = element - > name ( ) . value ( ) ;
if ( ! element_name . is_empty ( ) & & ! m_cached_name_to_element_mappings - > contains ( element_name ) )
m_cached_name_to_element_mappings - > set ( move ( element_name ) , element ) ;
}
}
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 ( ) ;
2024-07-25 07:49:41 +02:00
m_cached_name_to_element_mappings = nullptr ;
2024-04-01 15:08:22 +02:00
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 ) ;
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2024-04-01 15:08:22 +02:00
} ) ;
} 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-12-26 14:32:52 +01:00
GC : : RootVector < GC : : Ref < Element > > HTMLCollection : : collect_matching_elements ( ) const
2024-04-01 15:08:22 +02:00
{
update_cache_if_needed ( ) ;
2024-12-26 14:32:52 +01:00
GC : : RootVector < GC : : Ref < 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
2024-07-25 07:49:41 +02:00
update_name_to_element_mappings_if_needed ( ) ;
if ( auto it = m_cached_name_to_element_mappings - > get ( key ) ; it . has_value ( ) )
return it . value ( ) ;
2021-04-22 21:11:20 +02:00
return nullptr ;
}
2024-07-25 07:49:41 +02:00
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names
bool HTMLCollection : : is_supported_property_name ( FlyString const & name ) const
{
update_name_to_element_mappings_if_needed ( ) ;
return m_cached_name_to_element_mappings - > contains ( name ) ;
}
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-07-25 07:49:41 +02:00
update_name_to_element_mappings_if_needed ( ) ;
for ( auto const & it : * m_cached_name_to_element_mappings ) {
result . append ( it . key ) ;
2021-09-26 15:14:37 +01:00
}
// 3. Return result.
return result ;
}
2024-07-25 18:15:51 +12:00
Optional < JS : : Value > HTMLCollection : : item_value ( size_t index ) const
2022-09-01 20:50:16 +02:00
{
auto * element = item ( index ) ;
if ( ! element )
2024-07-25 18:15:51 +12:00
return { } ;
2024-04-01 15:23:31 +02:00
return element ;
2022-09-01 20:50:16 +02:00
}
2024-07-25 17:36:10 +12:00
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
}
2025-05-13 07:06:33 -04:00
2021-04-22 21:11:20 +02:00
}