2021-10-16 15:30:21 -04:00
/*
2022-01-31 13:07:22 -05:00
* Copyright ( c ) 2021 , Tim Flynn < trflynn89 @ serenityos . org >
2022-08-08 15:06:56 +02:00
* Copyright ( c ) 2022 , Andreas Kling < kling @ serenityos . 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
*/
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>
# include <LibWeb/Namespace.h>
namespace Web : : DOM {
2023-02-14 23:04:29 +01:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < 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 ( ) ;
2023-02-14 23:04:29 +01:00
return MUST_OR_THROW_OOM ( realm . heap ( ) . allocate < NamedNodeMap > ( realm , element ) ) ;
2021-10-16 15:30:21 -04:00
}
2022-08-08 15:06:56 +02:00
NamedNodeMap : : NamedNodeMap ( Element & element )
2023-01-10 06:56:59 -05:00
: Bindings : : LegacyPlatformObject ( element . realm ( ) )
2022-08-08 15:06:56 +02:00
, m_element ( element )
2021-10-16 15:30:21 -04:00
{
}
2023-01-28 12:33:35 -05:00
JS : : ThrowCompletionOr < void > NamedNodeMap : : initialize ( JS : : Realm & realm )
2023-01-10 06:56:59 -05:00
{
2023-01-28 12:33:35 -05:00
MUST_OR_THROW_OOM ( Base : : initialize ( realm ) ) ;
2023-01-10 06:56:59 -05:00
set_prototype ( & Bindings : : ensure_web_prototype < Bindings : : NamedNodeMapPrototype > ( realm , " NamedNodeMap " ) ) ;
2023-01-28 12:33:35 -05:00
return { } ;
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 ) ;
visitor . visit ( m_element . ptr ( ) ) ;
for ( auto & attribute : m_attributes )
visitor . visit ( attribute . ptr ( ) ) ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-indices%E2%91%A3
bool NamedNodeMap : : is_supported_property_index ( u32 index ) const
{
return index < m_attributes . size ( ) ;
}
// https://dom.spec.whatwg.org/#ref-for-dfn-supported-property-names%E2%91%A0
2022-12-04 18:02:33 +00:00
Vector < DeprecatedString > 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.
2022-12-04 18:02:33 +00:00
Vector < DeprecatedString > names ;
2021-10-16 15:30:21 -04:00
names . ensure_capacity ( m_attributes . size ( ) ) ;
for ( auto const & attribute : m_attributes ) {
2022-08-28 13:42:07 +02:00
if ( ! names . contains_slow ( attribute - > name ( ) ) )
names . append ( attribute - > name ( ) ) ;
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.
2021-12-09 15:34:56 +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.
names . remove_all_matching ( [ ] ( auto const & name ) { return name ! = name . to_lowercase ( ) ; } ) ;
}
// 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
2022-09-18 01:03:58 +02:00
Attr const * NamedNodeMap : : get_named_item ( StringView 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
Attr const * NamedNodeMap : : get_named_item_ns ( StringView namespace_ , StringView local_name ) const
{
return get_attribute_ns ( namespace_ , local_name ) ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditem
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < Attr const * > 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
WebIDL : : ExceptionOr < Attr const * > NamedNodeMap : : set_named_item_ns ( Attr & attribute )
{
return set_attribute ( attribute ) ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < Attr const * > NamedNodeMap : : remove_named_item ( StringView 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 )
2022-12-04 18:02:33 +00:00
return WebIDL : : NotFoundError : : create ( realm ( ) , DeprecatedString : : 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
WebIDL : : ExceptionOr < Attr const * > NamedNodeMap : : remove_named_item_ns ( StringView namespace_ , StringView local_name )
{
// 1. Let attr be the result of removing an attribute given namespace, localName, and element.
auto const * attribute = remove_attribute_ns ( namespace_ , local_name ) ;
// 2. If attr is null, then throw a "NotFoundError" DOMException.
if ( ! attribute )
return WebIDL : : NotFoundError : : create ( realm ( ) , DeprecatedString : : formatted ( " Attribute with namespace '{}' and local name '{}' not found " , namespace_ , local_name ) ) ;
// 3. Return attr.
return attribute ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
2022-09-18 01:03:58 +02:00
Attr * NamedNodeMap : : get_attribute ( StringView 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
2022-09-18 01:03:58 +02:00
Attr const * NamedNodeMap : : get_attribute ( StringView 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.
2021-12-09 15:34:56 +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 ) )
2022-08-28 13:42:07 +02:00
return attribute . ptr ( ) ;
2021-10-28 08:58:11 -04:00
} else {
2022-08-28 13:42:07 +02:00
if ( attribute - > name ( ) = = qualified_name )
return attribute . ptr ( ) ;
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 ;
}
// https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
Attr * NamedNodeMap : : get_attribute_ns ( StringView namespace_ , StringView 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
Attr const * NamedNodeMap : : get_attribute_ns ( StringView namespace_ , StringView local_name , size_t * item_index ) const
{
if ( item_index )
* item_index = 0 ;
// 1. If namespace is the empty string, then set it to null.
if ( namespace_ . is_empty ( ) )
namespace_ = { } ;
// 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 ) {
if ( attribute - > namespace_uri ( ) = = namespace_ & & attribute - > local_name ( ) = = local_name )
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
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < Attr const * > 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 ( ) ) )
2022-09-25 16:15:49 -06:00
return WebIDL : : InUseAttributeError : : create ( realm ( ) , " Attribute must not already be in use " sv ) ;
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 ;
2022-12-17 17:24:10 +03: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
{
// 1. Handle attribute changes for oldAttr with oldAttr’ s element, oldAttr’ s value, and newAttr’ s value.
2022-07-11 16:40:01 +01:00
VERIFY ( old_attribute . owner_element ( ) ) ;
old_attribute . handle_attribute_changes ( * old_attribute . owner_element ( ) , old_attribute . value ( ) , new_attribute . value ( ) ) ;
2021-10-16 15:30:21 -04:00
// 2. Replace oldAttr by newAttr in oldAttr’ s element’ s attribute list.
m_attributes . remove ( old_attribute_index ) ;
m_attributes . insert ( old_attribute_index , new_attribute ) ;
// 3. Set newAttr’ s element to oldAttr’ s element.
new_attribute . set_owner_element ( old_attribute . owner_element ( ) ) ;
// 4 .Set oldAttr’ s element to null.
old_attribute . set_owner_element ( nullptr ) ;
}
// 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
{
// 1. Handle attribute changes for attribute with element, null, and attribute’ s value.
2022-07-11 16:40:01 +01:00
attribute . handle_attribute_changes ( associated_element ( ) , { } , attribute . value ( ) ) ;
2021-10-16 15:30:21 -04:00
// 2. Append attribute to element’ s attribute list.
m_attributes . append ( attribute ) ;
// 3. Set attribute’ s element to element.
2021-12-09 15:34:56 +01:00
attribute . set_owner_element ( & associated_element ( ) ) ;
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 )
{
2022-09-18 01:03:58 +02:00
JS : : NonnullGCPtr < Attr > attribute = m_attributes . at ( attribute_index ) ;
2022-07-11 16:40:01 +01:00
// 1. Handle attribute changes for attribute with attribute’ s element, attribute’ s value, and null.
VERIFY ( attribute - > owner_element ( ) ) ;
attribute - > handle_attribute_changes ( * attribute - > owner_element ( ) , attribute - > value ( ) , { } ) ;
// 2. Remove attribute from attribute’ s element’ s attribute list.
m_attributes . remove ( attribute_index ) ;
// 3. Set attribute’ s element to null.
attribute - > set_owner_element ( nullptr ) ;
}
2021-10-16 15:30:21 -04:00
// https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
2022-09-18 01:03:58 +02:00
Attr const * NamedNodeMap : : remove_attribute ( StringView 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
Attr const * NamedNodeMap : : remove_attribute_ns ( StringView namespace_ , StringView local_name )
{
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 ;
}
2023-02-28 00:05:39 +00:00
WebIDL : : ExceptionOr < JS : : Value > NamedNodeMap : : item_value ( size_t index ) const
2022-08-08 15:06:56 +02:00
{
auto const * node = item ( index ) ;
if ( ! node )
return JS : : js_undefined ( ) ;
2022-08-28 13:42:07 +02:00
return node ;
2022-08-08 15:06:56 +02:00
}
2023-02-28 00:05:39 +00:00
WebIDL : : ExceptionOr < JS : : Value > NamedNodeMap : : named_item_value ( DeprecatedFlyString 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
}
2021-10-16 15:30:21 -04:00
}