2021-10-16 15:30:21 -04:00
/*
2022-01-31 13:07:22 -05:00
* Copyright ( c ) 2021 , Tim Flynn < trflynn89 @ serenityos . org >
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2022 , Andreas Kling < andreas @ ladybird . org >
2022-12-16 17:31:56 +03:00
* Copyright ( c ) 2022 , Alexander Narsudinov < a . narsudinov @ gmail . com >
2021-10-16 15:30:21 -04:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/NamedNodeMapPrototype.h>
2022-09-18 01:03:58 +02:00
# include <LibWeb/DOM/Attr.h>
2021-10-16 15:30:21 -04:00
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/NamedNodeMap.h>
2023-11-21 07:39:58 +13:00
# include <LibWeb/Infra/Strings.h>
2021-10-16 15:30:21 -04:00
# include <LibWeb/Namespace.h>
namespace Web : : DOM {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( NamedNodeMap ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < NamedNodeMap > NamedNodeMap : : create ( Element & element )
2021-10-16 15:30:21 -04:00
{
2022-08-28 13:42:07 +02:00
auto & realm = element . realm ( ) ;
2024-11-14 05:50:17 +13:00
return realm . create < NamedNodeMap > ( element ) ;
2021-10-16 15:30:21 -04:00
}
2022-08-08 15:06:56 +02:00
NamedNodeMap : : NamedNodeMap ( Element & element )
2024-01-09 16:05:03 -07:00
: Bindings : : PlatformObject ( element . realm ( ) )
2022-08-08 15:06:56 +02:00
, m_element ( element )
2021-10-16 15:30:21 -04: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-10-16 15:30:21 -04:00
}
2023-08-07 08:41:28 +02:00
void NamedNodeMap : : 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 ( NamedNodeMap ) ;
2023-01-10 06:56:59 -05:00
}
2022-08-28 13:42:07 +02:00
void NamedNodeMap : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_element ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_attributes ) ;
2022-08-28 13:42:07 +02:00
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names%E2%91%A0
2023-12-24 20:59:00 +01:00
Vector < FlyString > NamedNodeMap : : supported_property_names ( ) const
2021-10-16 15:30:21 -04:00
{
// 1. Let names be the qualified names of the attributes in this NamedNodeMap object’ s attribute list, with duplicates omitted, in order.
2023-12-24 20:59:00 +01:00
Vector < FlyString > names ;
2021-10-16 15:30:21 -04:00
names . ensure_capacity ( m_attributes . size ( ) ) ;
for ( auto const & attribute : m_attributes ) {
2023-11-21 07:39:58 +13:00
auto const attribute_name = attribute - > name ( ) ;
2023-09-10 16:06:58 +12:00
if ( ! names . contains_slow ( attribute_name ) )
2023-11-21 07:39:58 +13:00
names . append ( attribute_name . to_string ( ) ) ;
2021-10-16 15:30:21 -04:00
}
// 2. If this NamedNodeMap object’ s element is in the HTML namespace and its node document is an HTML document, then for each name in names:
// FIXME: Handle the second condition, assume it is an HTML document for now.
2023-11-04 18:42:04 +01:00
if ( associated_element ( ) . namespace_uri ( ) = = Namespace : : HTML ) {
2021-10-16 15:30:21 -04:00
// 1. Let lowercaseName be name, in ASCII lowercase.
// 2. If lowercaseName is not equal to name, remove name from names.
2024-10-14 10:51:15 +02:00
names . remove_all_matching ( [ ] ( auto const & name ) { return name ! = name . to_ascii_lowercase ( ) ; } ) ;
2021-10-16 15:30:21 -04:00
}
// 3. Return names.
return names ;
}
// https://dom.spec.whatwg.org/#dom-namednodemap-item
2022-09-18 01:03:58 +02:00
Attr const * NamedNodeMap : : item ( u32 index ) const
2021-10-16 15:30:21 -04:00
{
// 1. If index is equal to or greater than this’ s attribute list’ s size, then return null.
if ( index > = m_attributes . size ( ) )
return nullptr ;
// 2. Otherwise, return this’ s attribute list[index].
2022-08-28 13:42:07 +02:00
return m_attributes [ index ] . ptr ( ) ;
2021-10-16 15:30:21 -04:00
}
// https://dom.spec.whatwg.org/#dom-namednodemap-getnameditem
2024-01-02 21:23:53 +13:00
Attr const * NamedNodeMap : : get_named_item ( FlyString const & qualified_name ) const
2021-10-16 15:30:21 -04:00
{
return get_attribute ( qualified_name ) ;
}
2022-12-16 17:45:03 +03:00
// https://dom.spec.whatwg.org/#dom-namednodemap-getnameditemns
2024-01-02 23:49:13 +13:00
Attr const * NamedNodeMap : : get_named_item_ns ( Optional < FlyString > const & namespace_ , FlyString const & local_name ) const
2022-12-16 17:45:03 +03:00
{
2024-01-02 23:49:13 +13:00
return get_attribute_ns ( namespace_ , local_name ) ;
2022-12-16 17:45:03 +03:00
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditem
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ptr < Attr > > NamedNodeMap : : set_named_item ( Attr & attribute )
2021-10-16 15:30:21 -04:00
{
return set_attribute ( attribute ) ;
}
2022-12-17 17:32:41 +03:00
// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditemns
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ptr < Attr > > NamedNodeMap : : set_named_item_ns ( Attr & attribute )
2022-12-17 17:32:41 +03:00
{
return set_attribute ( attribute ) ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
2024-01-02 21:23:53 +13:00
WebIDL : : ExceptionOr < Attr const * > NamedNodeMap : : remove_named_item ( FlyString const & qualified_name )
2021-10-16 15:30:21 -04:00
{
// 1. Let attr be the result of removing an attribute given qualifiedName and element.
auto const * attribute = remove_attribute ( qualified_name ) ;
// 2. If attr is null, then throw a "NotFoundError" DOMException.
if ( ! attribute )
2023-09-06 16:03:01 +12:00
return WebIDL : : NotFoundError : : create ( realm ( ) , MUST ( String : : formatted ( " Attribute with name '{}' not found " , qualified_name ) ) ) ;
2021-10-16 15:30:21 -04:00
// 3. Return attr.
2022-12-17 17:05:35 +03:00
return attribute ;
2021-10-16 15:30:21 -04:00
}
2022-12-17 17:08:51 +03:00
// https://dom.spec.whatwg.org/#dom-namednodemap-removenameditemns
2024-01-02 23:49:13 +13:00
WebIDL : : ExceptionOr < Attr const * > NamedNodeMap : : remove_named_item_ns ( Optional < FlyString > const & namespace_ , FlyString const & local_name )
2022-12-17 17:08:51 +03:00
{
// 1. Let attr be the result of removing an attribute given namespace, localName, and element.
2024-01-02 23:49:13 +13:00
auto const * attribute = remove_attribute_ns ( namespace_ , local_name ) ;
2022-12-17 17:08:51 +03:00
// 2. If attr is null, then throw a "NotFoundError" DOMException.
if ( ! attribute )
2023-09-06 16:03:01 +12:00
return WebIDL : : NotFoundError : : create ( realm ( ) , MUST ( String : : formatted ( " Attribute with namespace '{}' and local name '{}' not found " , namespace_ , local_name ) ) ) ;
2022-12-17 17:08:51 +03:00
// 3. Return attr.
return attribute ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
2024-01-02 21:23:53 +13:00
Attr * NamedNodeMap : : get_attribute ( FlyString const & qualified_name , size_t * item_index )
2021-10-16 15:30:21 -04:00
{
2022-09-18 01:03:58 +02:00
return const_cast < Attr * > ( const_cast < NamedNodeMap const * > ( this ) - > get_attribute ( qualified_name , item_index ) ) ;
2021-10-16 15:30:21 -04:00
}
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
2024-01-02 21:23:53 +13:00
Attr const * NamedNodeMap : : get_attribute ( FlyString const & qualified_name , size_t * item_index ) const
2021-10-16 15:30:21 -04:00
{
if ( item_index )
* item_index = 0 ;
// 1. If element is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
// FIXME: Handle the second condition, assume it is an HTML document for now.
2023-11-04 18:42:04 +01:00
bool compare_as_lowercase = associated_element ( ) . namespace_uri ( ) = = Namespace : : HTML ;
2021-10-16 15:30:21 -04:00
// 2. Return the first attribute in element’ s attribute list whose qualified name is qualifiedName; otherwise null.
for ( auto const & attribute : m_attributes ) {
2021-10-28 08:58:11 -04:00
if ( compare_as_lowercase ) {
2023-03-10 08:48:54 +01:00
if ( attribute - > name ( ) . equals_ignoring_ascii_case ( qualified_name ) )
2024-01-02 21:23:53 +13:00
return attribute ;
2021-10-28 08:58:11 -04:00
} else {
2022-08-28 13:42:07 +02:00
if ( attribute - > name ( ) = = qualified_name )
2024-01-02 21:23:53 +13:00
return attribute ;
2021-10-28 08:58:11 -04:00
}
2021-10-16 15:30:21 -04:00
if ( item_index )
+ + ( * item_index ) ;
}
2022-12-16 17:31:56 +03:00
return nullptr ;
}
2024-03-15 18:19:59 +01:00
Attr const * NamedNodeMap : : get_attribute_with_lowercase_qualified_name ( FlyString const & lowercase_qualified_name ) const
{
bool compare_as_lowercase = associated_element ( ) . namespace_uri ( ) = = Namespace : : HTML ;
VERIFY ( compare_as_lowercase ) ;
for ( auto const & attribute : m_attributes ) {
if ( attribute - > lowercase_name ( ) = = lowercase_qualified_name )
return attribute ;
}
return nullptr ;
}
2023-11-05 11:11:01 +13:00
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
Attr * NamedNodeMap : : get_attribute_ns ( Optional < FlyString > const & namespace_ , FlyString const & local_name , size_t * item_index )
{
return const_cast < Attr * > ( const_cast < NamedNodeMap const * > ( this ) - > get_attribute_ns ( namespace_ , local_name , item_index ) ) ;
}
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
2024-01-02 23:49:13 +13:00
Attr const * NamedNodeMap : : get_attribute_ns ( Optional < FlyString > const & namespace_ , FlyString const & local_name , size_t * item_index ) const
2022-12-16 17:31:56 +03:00
{
if ( item_index )
* item_index = 0 ;
// 1. If namespace is the empty string, then set it to null.
2024-01-02 23:49:13 +13:00
Optional < FlyString > normalized_namespace ;
2024-01-13 17:30:32 +13:00
if ( namespace_ ! = String { } )
2024-01-02 23:49:13 +13:00
normalized_namespace = namespace_ ;
2022-12-16 17:31:56 +03:00
// 2. Return the attribute in element’ s attribute list whose namespace is namespace and local name is localName, if any; otherwise null.
for ( auto const & attribute : m_attributes ) {
2024-01-02 23:49:13 +13:00
if ( attribute - > namespace_uri ( ) = = normalized_namespace & & attribute - > local_name ( ) = = local_name )
2022-12-16 17:31:56 +03:00
return attribute . ptr ( ) ;
if ( item_index )
+ + ( * item_index ) ;
}
2021-10-16 15:30:21 -04:00
return nullptr ;
}
// https://dom.spec.whatwg.org/#concept-element-attributes-set
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ptr < Attr > > NamedNodeMap : : set_attribute ( Attr & attribute )
2021-10-16 15:30:21 -04:00
{
// 1. If attr’ s element is neither null nor element, throw an "InUseAttributeError" DOMException.
2021-12-09 15:34:56 +01:00
if ( ( attribute . owner_element ( ) ! = nullptr ) & & ( attribute . owner_element ( ) ! = & associated_element ( ) ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : InUseAttributeError : : create ( realm ( ) , " Attribute must not already be in use " _string ) ;
2021-10-16 15:30:21 -04:00
// 2. Let oldAttr be the result of getting an attribute given attr’ s namespace, attr’ s local name, and element.
size_t old_attribute_index = 0 ;
2023-12-24 15:45:56 +13:00
auto * old_attribute = get_attribute_ns ( attribute . namespace_uri ( ) , attribute . local_name ( ) , & old_attribute_index ) ;
2021-10-16 15:30:21 -04:00
// 3. If oldAttr is attr, return attr.
if ( old_attribute = = & attribute )
return & attribute ;
// 4. If oldAttr is non-null, then replace oldAttr with attr.
if ( old_attribute ) {
replace_attribute ( * old_attribute , attribute , old_attribute_index ) ;
}
// 5. Otherwise, append attr to element.
else {
append_attribute ( attribute ) ;
}
// 6. Return oldAttr.
return old_attribute ;
}
// https://dom.spec.whatwg.org/#concept-element-attributes-replace
2022-09-18 01:03:58 +02:00
void NamedNodeMap : : replace_attribute ( Attr & old_attribute , Attr & new_attribute , size_t old_attribute_index )
2021-10-16 15:30:21 -04:00
{
2022-07-11 16:40:01 +01:00
VERIFY ( old_attribute . owner_element ( ) ) ;
2021-10-16 15:30:21 -04:00
2023-09-02 10:54:56 -04:00
// 1. Replace oldAttr by newAttr in oldAttr’ s element’ s attribute list.
2021-10-16 15:30:21 -04:00
m_attributes . remove ( old_attribute_index ) ;
m_attributes . insert ( old_attribute_index , new_attribute ) ;
2023-09-02 10:54:56 -04:00
// 2. Set newAttr’ s element to oldAttr’ s element.
2021-10-16 15:30:21 -04:00
new_attribute . set_owner_element ( old_attribute . owner_element ( ) ) ;
2023-09-02 10:54:56 -04:00
// 3. Set oldAttr’ s element to null.
2021-10-16 15:30:21 -04:00
old_attribute . set_owner_element ( nullptr ) ;
2023-09-02 10:54:56 -04:00
// 4. Handle attribute changes for oldAttr with newAttr’ s element, oldAttr’ s value, and newAttr’ s value.
2023-11-19 18:10:36 +13:00
old_attribute . handle_attribute_changes ( * new_attribute . owner_element ( ) , old_attribute . value ( ) , new_attribute . value ( ) ) ;
2021-10-16 15:30:21 -04:00
}
// https://dom.spec.whatwg.org/#concept-element-attributes-append
2022-09-18 01:03:58 +02:00
void NamedNodeMap : : append_attribute ( Attr & attribute )
2021-10-16 15:30:21 -04:00
{
2023-09-02 10:54:56 -04:00
// 1. Append attribute to element’ s attribute list.
2021-10-16 15:30:21 -04:00
m_attributes . append ( attribute ) ;
2023-09-02 10:54:56 -04:00
// 2. Set attribute’ s element to element.
2021-12-09 15:34:56 +01:00
attribute . set_owner_element ( & associated_element ( ) ) ;
2023-09-02 10:54:56 -04:00
// 3. Handle attribute changes for attribute with element, null, and attribute’ s value.
2023-11-19 18:10:36 +13:00
attribute . handle_attribute_changes ( associated_element ( ) , { } , attribute . value ( ) ) ;
2021-10-16 15:30:21 -04:00
}
2022-07-11 16:40:01 +01:00
// https://dom.spec.whatwg.org/#concept-element-attributes-remove
void NamedNodeMap : : remove_attribute_at_index ( size_t attribute_index )
{
2024-11-15 04:01:23 +13:00
GC : : Ref < Attr > attribute = m_attributes . at ( attribute_index ) ;
2022-07-11 16:40:01 +01:00
2023-09-02 10:54:56 -04:00
// 1. Let element be attribute’ s element.
auto * element = attribute - > owner_element ( ) ;
VERIFY ( element ) ;
2022-07-11 16:40:01 +01:00
2023-09-02 10:54:56 -04:00
// 2. Remove attribute from element’ s attribute list.
2022-07-11 16:40:01 +01:00
m_attributes . remove ( attribute_index ) ;
// 3. Set attribute’ s element to null.
attribute - > set_owner_element ( nullptr ) ;
2023-09-02 10:54:56 -04:00
// 4. Handle attribute changes for attribute with element, attribute’ s value, and null.
2023-11-19 18:10:36 +13:00
attribute - > handle_attribute_changes ( * element , attribute - > value ( ) , { } ) ;
2022-07-11 16:40:01 +01:00
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
2024-01-02 21:23:53 +13:00
Attr const * NamedNodeMap : : remove_attribute ( FlyString const & qualified_name )
2021-10-16 15:30:21 -04:00
{
size_t item_index = 0 ;
// 1. Let attr be the result of getting an attribute given qualifiedName and element.
auto const * attribute = get_attribute ( qualified_name , & item_index ) ;
// 2. If attr is non-null, then remove attr.
if ( attribute )
2022-07-11 16:40:01 +01:00
remove_attribute_at_index ( item_index ) ;
2021-10-16 15:30:21 -04:00
// 3. Return attr.
return attribute ;
}
2022-12-17 17:08:51 +03:00
// https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-namespace
2024-01-02 23:49:13 +13:00
Attr const * NamedNodeMap : : remove_attribute_ns ( Optional < FlyString > const & namespace_ , FlyString const & local_name )
2022-12-17 17:08:51 +03:00
{
size_t item_index = 0 ;
// 1. Let attr be the result of getting an attribute given namespace, localName, and element.
auto const * attribute = get_attribute_ns ( namespace_ , local_name , & item_index ) ;
// 2. If attr is non-null, then remove attr.
if ( attribute )
remove_attribute_at_index ( item_index ) ;
// 3. Return attr.
return attribute ;
}
2024-07-25 18:15:51 +12:00
Optional < JS : : Value > NamedNodeMap : : item_value ( size_t index ) const
2022-08-08 15:06:56 +02:00
{
auto const * node = item ( index ) ;
if ( ! node )
2024-07-25 18:15:51 +12:00
return { } ;
2022-08-28 13:42:07 +02:00
return node ;
2022-08-08 15:06:56 +02:00
}
2024-07-25 17:36:10 +12:00
JS : : Value NamedNodeMap : : named_item_value ( FlyString const & name ) const
2022-08-08 15:06:56 +02:00
{
auto const * node = get_named_item ( name ) ;
if ( ! node )
return JS : : js_undefined ( ) ;
2022-08-28 13:42:07 +02:00
return node ;
2022-08-08 15:06:56 +02:00
}
2024-04-14 15:49:48 +02:00
// https://dom.spec.whatwg.org/#dom-element-removeattributenode
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < Attr > > NamedNodeMap : : remove_attribute_node ( GC : : Ref < Attr > attr )
2024-04-14 15:49:48 +02:00
{
// 1. If this’ s attribute list does not contain attr, then throw a "NotFoundError" DOMException.
auto index = m_attributes . find_first_index ( attr ) ;
if ( ! index . has_value ( ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : NotFoundError : : create ( realm ( ) , " Attribute not found " _string ) ;
2024-04-14 15:49:48 +02:00
// 2. Remove attr.
remove_attribute_at_index ( index . value ( ) ) ;
// 3. Return attr.
return attr ;
}
2021-10-16 15:30:21 -04:00
}