aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2025-02-07 02:53:06 -0500
committerGitHub <noreply@github.com>2025-02-07 07:53:06 +0000
commit6393a6c750c6fcd04967faac9e3d07310af370a4 (patch)
treedfa6fd5fedb419c758e2f3e7dc6016564176958d /components/script/dom
parent2ef12cf40f8173a2da6810fc8afe11e9f6781aef (diff)
downloadservo-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.rs96
-rw-r--r--components/script/dom/customelementregistry.rs29
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)