2023-03-29 23:46:18 +01:00
/*
* Copyright ( c ) 2023 , Luke Wilde < lukew @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/FunctionObject.h>
2023-07-19 06:54:48 -04:00
# include <LibJS/Runtime/Iterator.h>
2023-10-06 17:54:21 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2023-04-23 17:49:25 -06:00
# include <LibWeb/Bindings/CustomElementRegistryPrototype.h>
2023-03-29 23:46:18 +01:00
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/ElementFactory.h>
# include <LibWeb/DOM/ShadowRoot.h>
# include <LibWeb/HTML/CustomElements/CustomElementName.h>
# include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h>
# include <LibWeb/HTML/CustomElements/CustomElementRegistry.h>
# include <LibWeb/HTML/Scripting/Environments.h>
# include <LibWeb/HTML/Window.h>
# include <LibWeb/Namespace.h>
namespace Web : : HTML {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( CustomElementRegistry ) ;
GC_DEFINE_ALLOCATOR ( CustomElementDefinition ) ;
2023-11-19 19:47:52 +01:00
2023-03-29 23:46:18 +01:00
CustomElementRegistry : : CustomElementRegistry ( JS : : Realm & realm )
: Bindings : : PlatformObject ( realm )
{
}
CustomElementRegistry : : ~ CustomElementRegistry ( ) = default ;
2023-08-07 08:41:28 +02:00
void CustomElementRegistry : : initialize ( JS : : Realm & realm )
2023-03-29 23:46:18 +01: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 ( CustomElementRegistry ) ;
2023-03-29 23:46:18 +01:00
}
2024-03-11 19:37:21 +01:00
void CustomElementRegistry : : visit_edges ( Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_custom_element_definitions ) ;
visitor . visit ( m_when_defined_promise_map ) ;
2024-03-11 19:37:21 +01:00
}
2023-03-29 23:46:18 +01:00
// https://webidl.spec.whatwg.org/#es-callback-function
2024-11-19 00:38:06 +13:00
// https://github.com/whatwg/html/pull/9893
2024-11-15 04:01:23 +13:00
static JS : : ThrowCompletionOr < GC : : Ref < WebIDL : : CallbackType > > convert_value_to_callback_function ( JS : : VM & vm , JS : : Value value )
2023-03-29 23:46:18 +01:00
{
// FIXME: De-duplicate this from the IDL generator.
// 1. If the result of calling IsCallable(V) is false and the conversion to an IDL value is not being performed due to V being assigned to an attribute whose type is a nullable callback function that is annotated with [LegacyTreatNonObjectAsNull], then throw a TypeError.
if ( ! value . is_function ( ) )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAFunction , value . to_string_without_side_effects ( ) ) ;
2023-03-29 23:46:18 +01:00
2024-11-19 00:38:06 +13:00
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent realm as the callback context.
return vm . heap ( ) . allocate < WebIDL : : CallbackType > ( value . as_object ( ) , HTML : : incumbent_realm ( ) ) ;
2023-03-29 23:46:18 +01:00
}
// https://webidl.spec.whatwg.org/#es-sequence
static JS : : ThrowCompletionOr < Vector < String > > convert_value_to_sequence_of_strings ( JS : : VM & vm , JS : : Value value )
{
// FIXME: De-duplicate this from the IDL generator.
// An ECMAScript value V is converted to an IDL sequence<T> value as follows:
2024-10-31 11:34:22 +00:00
// 1. If V is not an Object, throw a TypeError.
2023-03-29 23:46:18 +01:00
if ( ! value . is_object ( ) )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAnObject , value . to_string_without_side_effects ( ) ) ;
2023-03-29 23:46:18 +01:00
// 2. Let method be ? GetMethod(V, @@iterator).
2023-04-13 15:41:29 +02:00
auto method = TRY ( value . get_method ( vm , vm . well_known_symbol_iterator ( ) ) ) ;
2023-03-29 23:46:18 +01:00
// 3. If method is undefined, throw a TypeError.
if ( ! method )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotIterable , value . to_string_without_side_effects ( ) ) ;
2023-03-29 23:46:18 +01:00
// 4. Return the result of creating a sequence from V and method.
// https://webidl.spec.whatwg.org/#create-sequence-from-iterable
// To create an IDL value of type sequence<T> given an iterable iterable and an iterator getter method, perform the following steps:
// 1. Let iter be ? GetIterator(iterable, sync, method).
2023-07-18 14:40:02 -04:00
// FIXME: The WebIDL spec is out of date - it should be using GetIteratorFromMethod.
auto iterator = TRY ( JS : : get_iterator_from_method ( vm , value , * method ) ) ;
2023-03-29 23:46:18 +01:00
// 2. Initialize i to be 0.
Vector < String > sequence_of_strings ;
// 3. Repeat
for ( ; ; ) {
// 1. Let next be ? IteratorStep(iter).
2023-04-15 16:23:03 +02:00
auto next = TRY ( JS : : iterator_step ( vm , iterator ) ) ;
2023-03-29 23:46:18 +01:00
// 2. If next is false, then return an IDL sequence value of type sequence<T> of length i, where the value of the element at index j is Sj.
if ( ! next )
return sequence_of_strings ;
// 3. Let nextItem be ? IteratorValue(next).
auto next_item = TRY ( JS : : iterator_value ( vm , * next ) ) ;
// 4. Initialize Si to the result of converting nextItem to an IDL value of type T.
// https://webidl.spec.whatwg.org/#es-DOMString
// An ECMAScript value V is converted to an IDL DOMString value by running the following algorithm:
// 1. If V is null and the conversion is to an IDL type associated with the [LegacyNullToEmptyString] extended attribute, then return the DOMString value that represents the empty string.
// NOTE: This doesn't apply.
// 2. Let x be ? ToString(V).
// 3. Return the IDL DOMString value that represents the same sequence of code units as the one the ECMAScript String value x represents.
auto string_value = TRY ( next_item . to_string ( vm ) ) ;
sequence_of_strings . append ( move ( string_value ) ) ;
// 5. Set i to i + 1.
}
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-define
JS : : ThrowCompletionOr < void > CustomElementRegistry : : define ( String const & name , WebIDL : : CallbackType * constructor , ElementDefinitionOptions options )
{
auto & realm = this - > realm ( ) ;
auto & vm = this - > vm ( ) ;
// 1. If IsConstructor(constructor) is false, then throw a TypeError.
if ( ! JS : : Value ( constructor - > callback ) . is_constructor ( ) )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAConstructor , JS : : Value ( constructor - > callback ) . to_string_without_side_effects ( ) ) ;
2023-03-29 23:46:18 +01:00
// 2. If name is not a valid custom element name, then throw a "SyntaxError" DOMException.
if ( ! is_valid_custom_element_name ( name ) )
2025-04-08 13:50:50 -04:00
return JS : : throw_completion ( WebIDL : : SyntaxError : : create ( realm , MUST ( String : : formatted ( " '{}' is not a valid custom element name " , name ) ) ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 3. If this's custom element definition set contains an item with name name, then throw a "NotSupportedError" DOMException.
2024-03-11 19:37:21 +01:00
auto existing_definition_with_name_iterator = m_custom_element_definitions . find_if ( [ & name ] ( auto const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > name ( ) = = name ;
} ) ;
if ( existing_definition_with_name_iterator ! = m_custom_element_definitions . end ( ) )
2025-04-08 13:50:50 -04:00
return JS : : throw_completion ( WebIDL : : NotSupportedError : : create ( realm , MUST ( String : : formatted ( " A custom element with name '{}' is already defined " , name ) ) ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 4. If this's custom element definition set contains an item with constructor constructor, then throw a "NotSupportedError" DOMException.
2024-03-11 19:37:21 +01:00
auto existing_definition_with_constructor_iterator = m_custom_element_definitions . find_if ( [ & constructor ] ( auto const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > constructor ( ) . callback = = constructor - > callback ;
} ) ;
if ( existing_definition_with_constructor_iterator ! = m_custom_element_definitions . end ( ) )
2024-10-12 20:56:21 +02:00
return JS : : throw_completion ( WebIDL : : NotSupportedError : : create ( realm , " The given constructor is already in use by another custom element " _string ) ) ;
2023-03-29 23:46:18 +01:00
// 5. Let localName be name.
String local_name = name ;
2024-12-18 15:58:36 +00:00
// 6. Let extends be options["extends"] if it exists; otherwise null.
2023-03-29 23:46:18 +01:00
auto & extends = options . extends ;
2024-12-18 15:58:36 +00:00
// 7. If extends is not null:
2023-03-29 23:46:18 +01:00
if ( extends . has_value ( ) ) {
// 1. If extends is a valid custom element name, then throw a "NotSupportedError" DOMException.
if ( is_valid_custom_element_name ( extends . value ( ) ) )
2025-04-08 13:50:50 -04:00
return JS : : throw_completion ( WebIDL : : NotSupportedError : : create ( realm , MUST ( String : : formatted ( " '{}' is a custom element name, only non-custom elements can be extended " , extends . value ( ) ) ) ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. If the element interface for extends and the HTML namespace is HTMLUnknownElement
// (e.g., if extends does not indicate an element definition in this specification),
// then throw a "NotSupportedError" DOMException.
2023-10-01 20:07:44 +13:00
if ( DOM : : is_unknown_html_element ( extends . value ( ) ) )
2025-04-08 13:50:50 -04:00
return JS : : throw_completion ( WebIDL : : NotSupportedError : : create ( realm , MUST ( String : : formatted ( " '{}' is an unknown HTML element " , extends . value ( ) ) ) ) ) ;
2023-03-29 23:46:18 +01:00
// 3. Set localName to extends.
local_name = extends . value ( ) ;
}
2024-12-18 15:58:36 +00:00
// 8. If this's element definition is running is true, then throw a "NotSupportedError" DOMException.
2023-03-29 23:46:18 +01:00
if ( m_element_definition_is_running )
2024-10-12 20:56:21 +02:00
return JS : : throw_completion ( WebIDL : : NotSupportedError : : create ( realm , " Cannot recursively define custom elements " _string ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 9. Set this's element definition is running to true.
2023-03-29 23:46:18 +01:00
m_element_definition_is_running = true ;
// 10. Let formAssociated be false.
bool form_associated = false ;
// 11. Let disableInternals be false.
bool disable_internals = false ;
// 12. Let disableShadow be false.
bool disable_shadow = false ;
// 13. Let observedAttributes be an empty sequence<DOMString>.
Vector < String > observed_attributes ;
2024-12-18 15:58:36 +00:00
// NOTE: This is not in the spec, but is required because of how we catch the exception by using a lambda, meaning we need to define this
// variable outside of it to use it later.
2024-12-26 18:17:41 +01:00
OrderedHashMap < FlyString , GC : : Root < WebIDL : : CallbackType > > lifecycle_callbacks ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 14. Run the following steps while catching any exceptions:
2023-03-29 23:46:18 +01:00
auto get_definition_attributes_from_constructor = [ & ] ( ) - > JS : : ThrowCompletionOr < void > {
// 1. Let prototype be ? Get(constructor, "prototype").
auto prototype_value = TRY ( constructor - > callback - > get ( vm . names . prototype ) ) ;
2024-10-31 11:34:22 +00:00
// 2. If prototype is not an Object, then throw a TypeError exception.
2023-03-29 23:46:18 +01:00
if ( ! prototype_value . is_object ( ) )
2023-08-09 08:49:02 +02:00
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAnObject , prototype_value . to_string_without_side_effects ( ) ) ;
2023-03-29 23:46:18 +01:00
auto & prototype = prototype_value . as_object ( ) ;
2024-12-18 15:58:36 +00:00
// 3. Let lifecycleCallbacks be the ordered map «[ "connectedCallback" → null, "disconnectedCallback" → null, "adoptedCallback" → null,
// "attributeChangedCallback" → null ]».
2023-03-29 23:46:18 +01:00
lifecycle_callbacks . set ( CustomElementReactionNames : : connectedCallback , { } ) ;
lifecycle_callbacks . set ( CustomElementReactionNames : : disconnectedCallback , { } ) ;
lifecycle_callbacks . set ( CustomElementReactionNames : : adoptedCallback , { } ) ;
lifecycle_callbacks . set ( CustomElementReactionNames : : attributeChangedCallback , { } ) ;
2024-12-18 15:58:36 +00:00
// 4. For each callbackName of the keys of lifecycleCallbacks:
2023-03-29 23:46:18 +01:00
for ( auto const & callback_name : { CustomElementReactionNames : : connectedCallback , CustomElementReactionNames : : disconnectedCallback , CustomElementReactionNames : : adoptedCallback , CustomElementReactionNames : : attributeChangedCallback } ) {
// 1. Let callbackValue be ? Get(prototype, callbackName).
2025-03-18 18:08:02 -05:00
auto callback_value = TRY ( prototype . get ( callback_name ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of
// converting callbackValue to the Web IDL Function callback type.
2023-03-29 23:46:18 +01:00
if ( ! callback_value . is_undefined ( ) ) {
auto callback = TRY ( convert_value_to_callback_function ( vm , callback_value ) ) ;
2024-03-11 19:12:52 +01:00
lifecycle_callbacks . set ( callback_name , callback ) ;
2023-03-29 23:46:18 +01:00
}
}
2024-12-18 15:58:36 +00:00
// 5. If lifecycleCallbacks["attributeChangedCallback"] is not null:
2023-03-29 23:46:18 +01:00
auto attribute_changed_callback_iterator = lifecycle_callbacks . find ( CustomElementReactionNames : : attributeChangedCallback ) ;
VERIFY ( attribute_changed_callback_iterator ! = lifecycle_callbacks . end ( ) ) ;
2024-03-11 19:12:52 +01:00
if ( attribute_changed_callback_iterator - > value ) {
2023-03-29 23:46:18 +01:00
// 1. Let observedAttributesIterable be ? Get(constructor, "observedAttributes").
2024-12-01 01:06:25 +01:00
auto observed_attributes_iterable = TRY ( constructor - > callback - > get ( vm . names . observedAttributes ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. If observedAttributesIterable is not undefined, then set observedAttributes to the result of converting observedAttributesIterable
// to a sequence<DOMString>. Rethrow any exceptions from the conversion.
2023-03-29 23:46:18 +01:00
if ( ! observed_attributes_iterable . is_undefined ( ) )
observed_attributes = TRY ( convert_value_to_sequence_of_strings ( vm , observed_attributes_iterable ) ) ;
}
// 6. Let disabledFeatures be an empty sequence<DOMString>.
Vector < String > disabled_features ;
// 7. Let disabledFeaturesIterable be ? Get(constructor, "disabledFeatures").
2024-12-01 01:06:25 +01:00
auto disabled_features_iterable = TRY ( constructor - > callback - > get ( vm . names . disabledFeatures ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 8. If disabledFeaturesIterable is not undefined, then set disabledFeatures to the result of converting disabledFeaturesIterable to a
// sequence<DOMString>. Rethrow any exceptions from the conversion.
2023-03-29 23:46:18 +01:00
if ( ! disabled_features_iterable . is_undefined ( ) )
disabled_features = TRY ( convert_value_to_sequence_of_strings ( vm , disabled_features_iterable ) ) ;
2024-12-18 15:58:36 +00:00
// 9. If disabledFeatures contains "internals", then set disableInternals to true.
2023-08-22 19:23:32 +02:00
disable_internals = disabled_features . contains_slow ( " internals " sv ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 10. If disabledFeatures contains "shadow", then set disableShadow to true.
2023-08-22 19:23:32 +02:00
disable_shadow = disabled_features . contains_slow ( " shadow " sv ) ;
2023-03-29 23:46:18 +01:00
// 11. Let formAssociatedValue be ? Get( constructor, "formAssociated").
2024-12-01 01:06:25 +01:00
auto form_associated_value = TRY ( constructor - > callback - > get ( vm . names . formAssociated ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 12. Set formAssociated to the result of converting formAssociatedValue to a boolean.
2023-03-29 23:46:18 +01:00
form_associated = form_associated_value . to_boolean ( ) ;
2024-12-18 15:58:36 +00:00
// 13. If formAssociated is true, then for each callbackName of « "formAssociatedCallback", "formResetCallback", "formDisabledCallback",
// "formStateRestoreCallback" »:
2023-03-29 23:46:18 +01:00
if ( form_associated ) {
for ( auto const & callback_name : { CustomElementReactionNames : : formAssociatedCallback , CustomElementReactionNames : : formResetCallback , CustomElementReactionNames : : formDisabledCallback , CustomElementReactionNames : : formStateRestoreCallback } ) {
// 1. Let callbackValue be ? Get(prototype, callbackName).
2025-03-18 18:08:02 -05:00
auto callback_value = TRY ( prototype . get ( callback_name ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. If callbackValue is not undefined, then set lifecycleCallbacks[callbackName] to the result of converting callbackValue
// to the Web IDL Function callback type.
2023-03-29 23:46:18 +01:00
if ( ! callback_value . is_undefined ( ) )
2024-03-11 19:12:52 +01:00
lifecycle_callbacks . set ( callback_name , TRY ( convert_value_to_callback_function ( vm , callback_value ) ) ) ;
2023-03-29 23:46:18 +01:00
}
}
return { } ;
} ;
auto maybe_exception = get_definition_attributes_from_constructor ( ) ;
2024-12-18 15:58:36 +00:00
// Then, regardless of whether the above steps threw an exception or not: set this's element definition is running to false.
2023-03-29 23:46:18 +01:00
m_element_definition_is_running = false ;
2024-12-18 15:58:36 +00:00
// Finally, if the steps threw an exception, rethrow that exception.
2023-03-29 23:46:18 +01:00
if ( maybe_exception . is_throw_completion ( ) )
return maybe_exception . release_error ( ) ;
2024-12-18 15:58:36 +00:00
// 15. Let definition be a new custom element definition with name name, local name localName, constructor constructor,
// observed attributes observedAttributes, lifecycle callbacks lifecycleCallbacks, form-associated formAssociated,
// disable internals disableInternals, and disable shadow disableShadow.
2023-03-29 23:46:18 +01:00
auto definition = CustomElementDefinition : : create ( realm , name , local_name , * constructor , move ( observed_attributes ) , move ( lifecycle_callbacks ) , form_associated , disable_internals , disable_shadow ) ;
2024-12-18 15:58:36 +00:00
// 16. Append definition to this's custom element definition set.
2024-03-11 19:37:21 +01:00
m_custom_element_definitions . append ( definition ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 17. Let document be this's relevant global object's associated Document.
2025-01-21 09:12:05 -05:00
auto & document = as < HTML : : Window > ( relevant_global_object ( * this ) ) . associated_document ( ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 18. Let upgradeCandidates be all elements that are shadow-including descendants of document, whose namespace is the HTML namespace
// and whose local name is localName, in shadow-including tree order.
2023-03-29 23:46:18 +01:00
// Additionally, if extends is non-null, only include elements whose is value is equal to name.
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < DOM : : Element > > upgrade_candidates ;
2023-03-29 23:46:18 +01:00
document . for_each_shadow_including_descendant ( [ & ] ( DOM : : Node & inclusive_descendant ) {
if ( ! is < DOM : : Element > ( inclusive_descendant ) )
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2023-03-29 23:46:18 +01:00
auto & inclusive_descendant_element = static_cast < DOM : : Element & > ( inclusive_descendant ) ;
2023-11-04 18:42:04 +01:00
if ( inclusive_descendant_element . namespace_uri ( ) = = Namespace : : HTML & & inclusive_descendant_element . local_name ( ) = = local_name & & ( ! extends . has_value ( ) | | inclusive_descendant_element . is_value ( ) = = name ) )
2024-11-15 04:01:23 +13:00
upgrade_candidates . append ( GC : : make_root ( inclusive_descendant_element ) ) ;
2023-03-29 23:46:18 +01:00
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2023-03-29 23:46:18 +01:00
} ) ;
2024-12-18 15:58:36 +00:00
// 19. For each element element of upgradeCandidates, enqueue a custom element upgrade reaction given element and definition.
2023-03-29 23:46:18 +01:00
for ( auto & element : upgrade_candidates )
element - > enqueue_a_custom_element_upgrade_reaction ( definition ) ;
2024-12-18 15:58:36 +00:00
// 20. If this's when-defined promise map[name] exists:
2023-03-29 23:46:18 +01:00
auto promise_when_defined_iterator = m_when_defined_promise_map . find ( name ) ;
if ( promise_when_defined_iterator ! = m_when_defined_promise_map . end ( ) ) {
2024-12-18 15:58:36 +00:00
// 1. Resolve this's when-defined promise map[name] with constructor.
WebIDL : : resolve_promise ( realm , promise_when_defined_iterator - > value , constructor - > callback ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. Remove this's when-defined promise map[name].
2023-03-29 23:46:18 +01:00
m_when_defined_promise_map . remove ( name ) ;
}
return { } ;
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-get
2025-01-12 22:50:03 +13:00
Variant < GC : : Root < WebIDL : : CallbackType > , Empty > CustomElementRegistry : : get ( String const & name ) const
2023-03-29 23:46:18 +01:00
{
2024-12-18 15:58:36 +00:00
// 1. If this's custom element definition set contains an item with name name, then return that item's constructor.
2024-03-11 19:37:21 +01:00
auto existing_definition_iterator = m_custom_element_definitions . find_if ( [ & name ] ( auto const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > name ( ) = = name ;
} ) ;
if ( ! existing_definition_iterator . is_end ( ) )
2024-11-15 04:01:23 +13:00
return GC : : make_root ( ( * existing_definition_iterator ) - > constructor ( ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. Return undefined.
2025-01-12 22:50:03 +13:00
return Empty { } ;
2023-03-29 23:46:18 +01:00
}
2024-07-06 15:35:10 +01:00
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-getname
2024-11-15 04:01:23 +13:00
Optional < String > CustomElementRegistry : : get_name ( GC : : Root < WebIDL : : CallbackType > const & constructor ) const
2024-07-06 15:35:10 +01:00
{
2024-12-18 15:58:36 +00:00
// 1. If this's custom element definition set contains an item with constructor constructor, then return that item's name.
2024-07-06 15:35:10 +01:00
auto existing_definition_iterator = m_custom_element_definitions . find_if ( [ & constructor ] ( auto const & definition ) {
return definition - > constructor ( ) . callback = = constructor . cell ( ) - > callback ;
} ) ;
if ( ! existing_definition_iterator . is_end ( ) )
return ( * existing_definition_iterator ) - > name ( ) ;
// 2. Return null.
return { } ;
}
2023-03-29 23:46:18 +01:00
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-whendefined
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < WebIDL : : Promise > > CustomElementRegistry : : when_defined ( String const & name )
2023-03-29 23:46:18 +01:00
{
auto & realm = this - > realm ( ) ;
2024-12-18 15:58:36 +00:00
// 1. If name is not a valid custom element name, then return a promise rejected with a "SyntaxError" DOMException.
2024-10-25 12:38:19 -06:00
if ( ! is_valid_custom_element_name ( name ) )
2025-04-08 13:50:50 -04:00
return WebIDL : : create_rejected_promise ( realm , WebIDL : : SyntaxError : : create ( realm , MUST ( String : : formatted ( " '{}' is not a valid custom element name " , name ) ) ) ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 2. If this's custom element definition set contains an item with name name, then return a promise resolved with that item's constructor.
2024-11-15 04:01:23 +13:00
auto existing_definition_iterator = m_custom_element_definitions . find_if ( [ & name ] ( GC : : Root < CustomElementDefinition > const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > name ( ) = = name ;
} ) ;
2024-10-25 12:38:19 -06:00
if ( existing_definition_iterator ! = m_custom_element_definitions . end ( ) )
return WebIDL : : create_resolved_promise ( realm , ( * existing_definition_iterator ) - > constructor ( ) . callback ) ;
2023-03-29 23:46:18 +01:00
2024-12-18 15:58:36 +00:00
// 3. If this's when-defined promise map[name] does not exist, then set this's when-defined promise map[name] to a new promise.
2023-03-29 23:46:18 +01:00
auto existing_promise_iterator = m_when_defined_promise_map . find ( name ) ;
2024-12-18 15:58:36 +00:00
GC : : Ptr < WebIDL : : Promise > promise ;
if ( existing_promise_iterator = = m_when_defined_promise_map . end ( ) ) {
2024-10-25 12:38:19 -06:00
promise = WebIDL : : create_promise ( realm ) ;
2024-03-11 19:57:08 +01:00
m_when_defined_promise_map . set ( name , * promise ) ;
2024-12-18 15:58:36 +00:00
} else {
promise = existing_promise_iterator - > value ;
2023-03-29 23:46:18 +01:00
}
2024-12-18 15:58:36 +00:00
// 4. Return this's when-defined promise map[name].
2023-03-29 23:46:18 +01:00
VERIFY ( promise ) ;
2024-11-15 04:01:23 +13:00
return GC : : Ref { * promise } ;
2023-03-29 23:46:18 +01:00
}
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-upgrade
2024-11-15 04:01:23 +13:00
void CustomElementRegistry : : upgrade ( GC : : Ref < DOM : : Node > root ) const
2023-03-29 23:46:18 +01:00
{
// 1. Let candidates be a list of all of root's shadow-including inclusive descendant elements, in shadow-including tree order.
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < DOM : : Element > > candidates ;
2023-03-29 23:46:18 +01:00
root - > for_each_shadow_including_inclusive_descendant ( [ & ] ( DOM : : Node & inclusive_descendant ) {
if ( ! is < DOM : : Element > ( inclusive_descendant ) )
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2023-03-29 23:46:18 +01:00
auto & inclusive_descendant_element = static_cast < DOM : : Element & > ( inclusive_descendant ) ;
2024-11-15 04:01:23 +13:00
candidates . append ( GC : : make_root ( inclusive_descendant_element ) ) ;
2023-03-29 23:46:18 +01:00
2024-05-04 14:47:04 +01:00
return TraversalDecision : : Continue ;
2023-03-29 23:46:18 +01:00
} ) ;
// 2. For each candidate of candidates, try to upgrade candidate.
for ( auto & candidate : candidates )
candidate - > try_to_upgrade ( ) ;
}
2024-11-15 04:01:23 +13:00
GC : : Ptr < CustomElementDefinition > CustomElementRegistry : : get_definition_with_name_and_local_name ( String const & name , String const & local_name ) const
2023-03-29 23:46:18 +01:00
{
2024-11-15 04:01:23 +13:00
auto definition_iterator = m_custom_element_definitions . find_if ( [ & ] ( GC : : Root < CustomElementDefinition > const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > name ( ) = = name & & definition - > local_name ( ) = = local_name ;
} ) ;
return definition_iterator . is_end ( ) ? nullptr : definition_iterator - > ptr ( ) ;
}
2024-11-15 04:01:23 +13:00
GC : : Ptr < CustomElementDefinition > CustomElementRegistry : : get_definition_from_new_target ( JS : : FunctionObject const & new_target ) const
2023-03-29 23:46:18 +01:00
{
2024-11-15 04:01:23 +13:00
auto definition_iterator = m_custom_element_definitions . find_if ( [ & ] ( GC : : Root < CustomElementDefinition > const & definition ) {
2023-03-29 23:46:18 +01:00
return definition - > constructor ( ) . callback . ptr ( ) = = & new_target ;
} ) ;
return definition_iterator . is_end ( ) ? nullptr : definition_iterator - > ptr ( ) ;
}
}