2021-09-26 15:24:41 +01:00
/*
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/CharacterTypes.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/DOMStringMapPrototype.h>
2022-09-25 16:38:21 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2022-08-08 14:49:54 +02:00
# include <LibWeb/DOM/Document.h>
2021-09-26 15:24:41 +01:00
# include <LibWeb/DOM/Element.h>
# include <LibWeb/HTML/DOMStringMap.h>
namespace Web : : HTML {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( DOMStringMap ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < DOMStringMap > DOMStringMap : : create ( DOM : : Element & element )
2022-08-08 14:49:54 +02:00
{
2022-09-25 16:38:21 -06:00
auto & realm = element . realm ( ) ;
2024-11-14 05:50:17 +13:00
return realm . create < DOMStringMap > ( element ) ;
2022-08-08 14:49:54 +02:00
}
DOMStringMap : : DOMStringMap ( DOM : : Element & element )
2024-01-09 16:05:03 -07:00
: PlatformObject ( element . realm ( ) )
2022-08-08 14:49:54 +02:00
, m_associated_element ( element )
2021-09-26 15:24:41 +01:00
{
2024-01-09 16:05:03 -07:00
m_legacy_platform_object_flags = LegacyPlatformObjectFlags {
. supports_named_properties = true ,
. has_named_property_setter = true ,
. has_named_property_deleter = true ,
. has_legacy_override_built_ins_interface_extended_attribute = true ,
} ;
2021-09-26 15:24:41 +01:00
}
2022-03-14 13:21:51 -06:00
DOMStringMap : : ~ DOMStringMap ( ) = default ;
2021-09-26 15:24:41 +01:00
2023-08-07 08:41:28 +02:00
void DOMStringMap : : 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 ( DOMStringMap ) ;
2023-01-10 06:56:59 -05:00
}
2022-08-28 13:42:07 +02:00
void DOMStringMap : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_associated_element ) ;
2022-08-28 13:42:07 +02:00
}
2021-09-26 15:24:41 +01:00
// https://html.spec.whatwg.org/multipage/dom.html#concept-domstringmap-pairs
Vector < DOMStringMap : : NameValuePair > DOMStringMap : : get_name_value_pairs ( ) const
{
// 1. Let list be an empty list of name-value pairs.
Vector < NameValuePair > list ;
// 2. For each content attribute on the DOMStringMap's associated element whose first five characters are the string "data-" and whose remaining characters (if any) do not include any ASCII upper alphas,
// in the order that those attributes are listed in the element's attribute list, add a name-value pair to list whose name is the attribute's name with the first five characters removed and whose value
// is the attribute's value.
m_associated_element - > for_each_attribute ( [ & ] ( auto & name , auto & value ) {
2023-10-08 11:42:00 +13:00
if ( ! name . bytes_as_string_view ( ) . starts_with ( " data- " sv ) )
2021-09-26 15:24:41 +01:00
return ;
2023-10-08 11:42:00 +13:00
auto name_after_starting_data = name . bytes_as_string_view ( ) . substring_view ( 5 ) ;
2021-09-26 15:24:41 +01:00
for ( auto character : name_after_starting_data ) {
if ( is_ascii_upper_alpha ( character ) )
return ;
}
// 3. For each name in list, for each U+002D HYPHEN-MINUS character (-) in the name that is followed by an ASCII lower alpha, remove the U+002D HYPHEN-MINUS character (-) and replace the character
// that followed it by the same character converted to ASCII uppercase.
StringBuilder builder ;
for ( size_t character_index = 0 ; character_index < name_after_starting_data . length ( ) ; + + character_index ) {
auto current_character = name_after_starting_data [ character_index ] ;
if ( character_index + 1 < name_after_starting_data . length ( ) & & current_character = = ' - ' ) {
auto next_character = name_after_starting_data [ character_index + 1 ] ;
if ( is_ascii_lower_alpha ( next_character ) ) {
builder . append ( to_ascii_uppercase ( next_character ) ) ;
// Skip the next character
+ + character_index ;
2022-04-17 18:43:00 +02:00
continue ;
2021-09-26 15:24:41 +01:00
}
}
builder . append ( current_character ) ;
}
2024-01-16 19:04:45 +01:00
list . append ( { MUST ( builder . to_string ( ) ) , value } ) ;
2021-09-26 15:24:41 +01:00
} ) ;
// 4. Return list.
return list ;
}
// https://html.spec.whatwg.org/multipage/dom.html#concept-domstringmap-pairs
// NOTE: There isn't a direct link to this, so the link is to one of the algorithms above it.
2023-12-24 20:59:00 +01:00
Vector < FlyString > DOMStringMap : : supported_property_names ( ) const
2021-09-26 15:24:41 +01:00
{
// The supported property names on a DOMStringMap object at any instant are the names of each pair returned from getting the DOMStringMap's name-value pairs at that instant, in the order returned.
2023-12-24 20:59:00 +01:00
Vector < FlyString > names ;
2021-09-26 15:24:41 +01:00
auto name_value_pairs = get_name_value_pairs ( ) ;
for ( auto & name_value_pair : name_value_pairs ) {
2023-12-24 20:59:00 +01:00
names . append ( name_value_pair . name ) ;
2021-09-26 15:24:41 +01:00
}
return names ;
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-nameditem
2023-11-22 09:03:44 +13:00
String DOMStringMap : : determine_value_of_named_property ( FlyString const & name ) const
2021-09-26 15:24:41 +01:00
{
// To determine the value of a named property name for a DOMStringMap, return the value component of the name-value pair whose name component is name in the list returned from getting the
// DOMStringMap's name-value pairs.
auto name_value_pairs = get_name_value_pairs ( ) ;
2023-11-22 09:03:44 +13:00
auto optional_value = name_value_pairs . first_matching ( [ & name ] ( NameValuePair const & name_value_pair ) {
2021-09-26 15:24:41 +01:00
return name_value_pair . name = = name ;
} ) ;
// NOTE: determine_value_of_named_property is only called if `name` is in supported_property_names.
VERIFY ( optional_value . has_value ( ) ) ;
return optional_value - > value ;
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-setitem
2023-11-21 12:15:56 +13:00
WebIDL : : ExceptionOr < void > DOMStringMap : : set_value_of_new_named_property ( String const & name , JS : : Value unconverted_value )
2021-09-26 15:24:41 +01:00
{
2024-01-09 16:05:03 -07:00
// NOTE: Since PlatformObject does not know the type of value, we must convert it ourselves.
2023-02-28 00:05:39 +00:00
// The type of `value` is `DOMString`.
2023-11-05 12:57:54 +13:00
auto value = TRY ( unconverted_value . to_string ( vm ( ) ) ) ;
2023-02-28 00:05:39 +00:00
2023-11-21 12:15:56 +13:00
StringBuilder builder ;
2021-09-26 15:24:41 +01:00
// 3. Insert the string data- at the front of name.
// NOTE: This is done out of order because StringBuilder doesn't have prepend.
2022-07-11 17:32:29 +00:00
builder . append ( " data- " sv ) ;
2021-09-26 15:24:41 +01:00
2023-11-21 12:15:56 +13:00
auto name_view = name . bytes_as_string_view ( ) ;
for ( size_t character_index = 0 ; character_index < name_view . length ( ) ; + + character_index ) {
2021-09-26 15:24:41 +01:00
// 1. If name contains a U+002D HYPHEN-MINUS character (-) followed by an ASCII lower alpha, then throw a "SyntaxError" DOMException.
2023-11-21 12:15:56 +13:00
auto current_character = name_view [ character_index ] ;
2021-09-26 15:24:41 +01:00
2023-11-21 12:15:56 +13:00
if ( current_character = = ' - ' & & character_index + 1 < name_view . length ( ) ) {
auto next_character = name_view [ character_index + 1 ] ;
2021-09-26 15:24:41 +01:00
if ( is_ascii_lower_alpha ( next_character ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : SyntaxError : : create ( realm ( ) , " Name cannot contain a '-' followed by a lowercase character. " _string ) ;
2021-09-26 15:24:41 +01:00
}
// 2. For each ASCII upper alpha in name, insert a U+002D HYPHEN-MINUS character (-) before the character and replace the character with the same character converted to ASCII lowercase.
if ( is_ascii_upper_alpha ( current_character ) ) {
builder . append ( ' - ' ) ;
builder . append ( to_ascii_lowercase ( current_character ) ) ;
continue ;
}
builder . append ( current_character ) ;
}
2023-11-05 12:57:54 +13:00
auto data_name = MUST ( builder . to_string ( ) ) ;
2021-09-26 15:24:41 +01:00
// FIXME: 4. If name does not match the XML Name production, throw an "InvalidCharacterError" DOMException.
// 5. Set an attribute value for the DOMStringMap's associated element using name and value.
2024-07-19 14:57:23 +02:00
TRY ( m_associated_element - > set_attribute ( data_name , value ) ) ;
2021-09-26 15:24:41 +01:00
return { } ;
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-setitem
2023-11-21 12:15:56 +13:00
WebIDL : : ExceptionOr < void > DOMStringMap : : set_value_of_existing_named_property ( String const & name , JS : : Value value )
2021-09-26 15:24:41 +01:00
{
return set_value_of_new_named_property ( name , value ) ;
}
// https://html.spec.whatwg.org/multipage/dom.html#dom-domstringmap-removeitem
2024-01-09 16:05:03 -07:00
WebIDL : : ExceptionOr < Bindings : : PlatformObject : : DidDeletionFail > DOMStringMap : : delete_value ( String const & name )
2021-09-26 15:24:41 +01:00
{
2023-11-21 12:15:56 +13:00
StringBuilder builder ;
2021-09-26 15:24:41 +01:00
// 2. Insert the string data- at the front of name.
// NOTE: This is done out of order because StringBuilder doesn't have prepend.
2022-07-11 17:32:29 +00:00
builder . append ( " data- " sv ) ;
2021-09-26 15:24:41 +01:00
2023-11-21 12:15:56 +13:00
for ( auto character : name . bytes_as_string_view ( ) ) {
2021-09-26 15:24:41 +01:00
// 1. For each ASCII upper alpha in name, insert a U+002D HYPHEN-MINUS character (-) before the character and replace the character with the same character converted to ASCII lowercase.
if ( is_ascii_upper_alpha ( character ) ) {
builder . append ( ' - ' ) ;
builder . append ( to_ascii_lowercase ( character ) ) ;
continue ;
}
builder . append ( character ) ;
}
// Remove an attribute by name given name and the DOMStringMap's associated element.
2024-01-02 21:23:53 +13:00
auto data_name = MUST ( builder . to_string ( ) ) ;
2021-09-26 15:24:41 +01:00
m_associated_element - > remove_attribute ( data_name ) ;
// The spec doesn't have the step. This indicates that the deletion was successful.
2023-02-28 00:05:39 +00:00
return DidDeletionFail : : No ;
2021-09-26 15:24:41 +01:00
}
2024-07-25 17:36:10 +12:00
JS : : Value DOMStringMap : : named_item_value ( FlyString const & name ) const
2022-10-08 12:58:56 +02:00
{
2023-11-22 09:03:44 +13:00
return JS : : PrimitiveString : : create ( vm ( ) , determine_value_of_named_property ( name ) ) ;
2022-10-08 12:58:56 +02:00
}
2021-09-26 15:24:41 +01:00
}