diff options
author | Josh Matthews <josh@joshmatthews.net> | 2025-02-07 02:53:06 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-07 07:53:06 +0000 |
commit | 6393a6c750c6fcd04967faac9e3d07310af370a4 (patch) | |
tree | dfa6fd5fedb419c758e2f3e7dc6016564176958d /components/script/dom | |
parent | 2ef12cf40f8173a2da6810fc8afe11e9f6781aef (diff) | |
download | servo-6393a6c750c6fcd04967faac9e3d07310af370a4.tar.gz servo-6393a6c750c6fcd04967faac9e3d07310af370a4.zip |
script: Annotate steps for custom element creation. (#35354)
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/create.rs | 96 | ||||
-rw-r--r-- | components/script/dom/customelementregistry.rs | 29 |
2 files changed, 82 insertions, 43 deletions
diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index 65601c0f30b..5a9377985e4 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -134,46 +134,70 @@ fn create_html_element( ) -> DomRoot<Element> { assert_eq!(name.ns, ns!(html)); - // Step 4 + // Step 2. Let definition be the result of looking up a custom element + // definition given document, namespace, localName, and is. let definition = document.lookup_custom_element_definition(&name.ns, &name.local, is.as_ref()); + // Step 3. If definition is non-null... if let Some(definition) = definition { - if definition.is_autonomous() { + // ...and definition’s name is not equal to its local name + // (i.e., definition represents a customized built-in element): + if !definition.is_autonomous() { + // Step 3.1. Let interface be the element interface for localName and the HTML namespace. + // Step 3.2. Set result to a new element that implements interface, with no attributes, + // namespace set to the HTML namespace, namespace prefix set to prefix, + // local name set to localName, custom element state set to "undefined", + // custom element definition set to null, is value set to is, + // and node document set to document. + let element = create_native_html_element(name, prefix, document, creator, proto); + element.set_is(definition.name.clone()); + element.set_custom_element_state(CustomElementState::Undefined); match mode { + // Step 3.3. If synchronousCustomElements is true, then run this step while catching any exceptions: + CustomElementCreationMode::Synchronous => { + // Step 3.3.1. Upgrade result using definition. + upgrade_element(definition, &element, can_gc); + // TODO: "If this step threw an exception exception:" steps. + }, + // Step 3.4. Otherwise, enqueue a custom element upgrade reaction given result and definition. CustomElementCreationMode::Asynchronous => { - let result = DomRoot::upcast::<Element>(HTMLElement::new( - name.local.clone(), - prefix.clone(), - document, - proto, - can_gc, - )); - result.set_custom_element_state(CustomElementState::Undefined); - ScriptThread::enqueue_upgrade_reaction(&result, definition); - return result; + ScriptThread::enqueue_upgrade_reaction(&element, definition) }, + } + return element; + } else { + // Step 4. Otherwise, if definition is non-null: + match mode { + // Step 4.1. If synchronousCustomElements is true, then run these + // steps while catching any exceptions: CustomElementCreationMode::Synchronous => { let local_name = name.local.clone(); //TODO(jdm) Pass proto to create_element? + // Steps 4.1.1-4.1.11 return match definition.create_element(document, prefix.clone(), can_gc) { Ok(element) => { element.set_custom_element_definition(definition.clone()); element }, Err(error) => { - // Step 6. Recovering from exception. + // If any of these steps threw an exception exception: let global = GlobalScope::current().unwrap_or_else(|| document.global()); let cx = GlobalScope::get_cx(); - // Step 6.1.1 + // Substep 1. Report exception for definition’s constructor’s corresponding + // JavaScript object’s associated realm’s global object. unsafe { let ar = enter_realm(&*global); throw_dom_exception(cx, &global, error); report_pending_exception(*cx, true, InRealm::Entered(&ar), can_gc); } - // Step 6.1.2 + // Substep 2. Set result to a new element that implements the HTMLUnknownElement interface, + // with no attributes, namespace set to the HTML namespace, namespace prefix set to prefix, + // local name set to localName, custom element state set to "failed", + // custom element definition set to null, is value set to null, + // and node document set to document. let element = DomRoot::upcast::<Element>(HTMLUnknownElement::new( local_name, prefix, document, proto, can_gc, )); @@ -182,28 +206,38 @@ fn create_html_element( }, }; }, - } - } else { - // Steps 5.1-5.2 - let element = create_native_html_element(name, prefix, document, creator, proto); - element.set_is(definition.name.clone()); - element.set_custom_element_state(CustomElementState::Undefined); - match mode { - // Step 5.3 - CustomElementCreationMode::Synchronous => { - upgrade_element(definition, &element, can_gc) - }, - // Step 5.4 + // Step 4.2. Otherwise: CustomElementCreationMode::Asynchronous => { - ScriptThread::enqueue_upgrade_reaction(&element, definition) + // Step 4.2.1. Set result to a new element that implements the HTMLElement interface, + // with no attributes, namespace set to the HTML namespace, namespace prefix set to + // prefix, local name set to localName, custom element state set to "undefined", + // custom element definition set to null, is value set to null, and node document + // set to document. + let result = DomRoot::upcast::<Element>(HTMLElement::new( + name.local.clone(), + prefix.clone(), + document, + proto, + can_gc, + )); + result.set_custom_element_state(CustomElementState::Undefined); + // Step 4.2.2. Enqueue a custom element upgrade reaction given result and definition. + ScriptThread::enqueue_upgrade_reaction(&result, definition); + return result; }, } - return element; } } - // Steps 7.1-7.3 + // Step 5. Otherwise: + // Step 5.1. Let interface be the element interface for localName and namespace. + // Step 5.2. Set result to a new element that implements interface, with no attributes, + // namespace set to namespace, namespace prefix set to prefix, local name set to localName, + // custom element state set to "uncustomized", custom element definition set to null, + // is value set to is, and node document set to document. let result = create_native_html_element(name.clone(), prefix, document, creator, proto); + // Step 5.3. If namespace is the HTML namespace, and either localName is a valid custom element name or + // is is non-null, then set result’s custom element state to "undefined". match is { Some(is) => { result.set_is(is); @@ -218,7 +252,7 @@ fn create_html_element( }, }; - // Step 8 + // Step 6. Return result. result } diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs index 6eead76f8ae..dd6a2844a5f 100644 --- a/components/script/dom/customelementregistry.rs +++ b/components/script/dom/customelementregistry.rs @@ -710,7 +710,7 @@ impl CustomElementDefinition { self.name == self.local_name } - /// <https://dom.spec.whatwg.org/#concept-create-element> Step 6.1 + /// <https://dom.spec.whatwg.org/#concept-create-element> Step 4.1 #[allow(unsafe_code)] pub(crate) fn create_element( &self, @@ -720,12 +720,13 @@ impl CustomElementDefinition { ) -> Fallible<DomRoot<Element>> { let window = document.window(); let cx = GlobalScope::get_cx(); - // Step 2 + // Step 4.1.1. Let C be definition’s constructor. rooted!(in(*cx) let constructor = ObjectValue(self.constructor.callback())); rooted!(in(*cx) let mut element = ptr::null_mut::<JSObject>()); { // Go into the constructor's realm let _ac = JSAutoRealm::new(*cx, self.constructor.callback()); + // Step 4.1.2. Set result to the result of constructing C, with no arguments. let args = HandleValueArray::empty(); if unsafe { !Construct1(*cx, constructor.handle(), &args, element.handle_mut()) } { return Err(Error::JSFailed); @@ -752,14 +753,18 @@ impl CustomElementDefinition { _ => return Err(Error::JSFailed), }; - // Step 3 - if !element.is::<HTMLElement>() { - return Err(Error::Type( - "Constructor did not return a DOM node".to_owned(), - )); - } - - // Steps 4-9 + // Step 4.1.3. Assert: result’s custom element state and custom element definition are initialized. + // Step 4.1.4. Assert: result’s namespace is the HTML namespace. + // Note: IDL enforces that result is an HTMLElement object, which all use the HTML namespace. + // Note: the custom element definition is initialized by the caller if + // this method returns a success value. + assert!(element.is::<HTMLElement>()); + + // Step 4.1.5. If result’s attribute list is not empty, then throw a "NotSupportedError" DOMException. + // Step 4.1.6. If result has children, then throw a "NotSupportedError" DOMException. + // Step 4.1.7. If result’s parent is not null, then throw a "NotSupportedError" DOMException. + // Step 4.1.8. If result’s node document is not document, then throw a "NotSupportedError" DOMException. + // Step 4.1.9. If result’s local name is not equal to localName then throw a "NotSupportedError" DOMException. if element.HasAttributes() || element.upcast::<Node>().children_count() > 0 || element.upcast::<Node>().has_parent() || @@ -770,10 +775,10 @@ impl CustomElementDefinition { return Err(Error::NotSupported); } - // Step 10 + // Step 4.1.10. Set result’s namespace prefix to prefix. element.set_prefix(prefix); - // Step 11 + // Step 4.1.11. Set result’s is value to null. // Element's `is` is None by default Ok(element) |