mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
script: Annotate steps for custom element creation. (#35354)
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
2ef12cf40f
commit
6393a6c750
2 changed files with 82 additions and 43 deletions
|
@ -134,46 +134,70 @@ fn create_html_element(
|
||||||
) -> DomRoot<Element> {
|
) -> DomRoot<Element> {
|
||||||
assert_eq!(name.ns, ns!(html));
|
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());
|
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 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 {
|
match mode {
|
||||||
CustomElementCreationMode::Asynchronous => {
|
// Step 3.3. If synchronousCustomElements is true, then run this step while catching any exceptions:
|
||||||
let result = DomRoot::upcast::<Element>(HTMLElement::new(
|
CustomElementCreationMode::Synchronous => {
|
||||||
name.local.clone(),
|
// Step 3.3.1. Upgrade result using definition.
|
||||||
prefix.clone(),
|
upgrade_element(definition, &element, can_gc);
|
||||||
document,
|
// TODO: "If this step threw an exception exception:" steps.
|
||||||
proto,
|
|
||||||
can_gc,
|
|
||||||
));
|
|
||||||
result.set_custom_element_state(CustomElementState::Undefined);
|
|
||||||
ScriptThread::enqueue_upgrade_reaction(&result, definition);
|
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
|
// Step 3.4. Otherwise, enqueue a custom element upgrade reaction given result and definition.
|
||||||
|
CustomElementCreationMode::Asynchronous => {
|
||||||
|
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 => {
|
CustomElementCreationMode::Synchronous => {
|
||||||
let local_name = name.local.clone();
|
let local_name = name.local.clone();
|
||||||
//TODO(jdm) Pass proto to create_element?
|
//TODO(jdm) Pass proto to create_element?
|
||||||
|
// Steps 4.1.1-4.1.11
|
||||||
return match definition.create_element(document, prefix.clone(), can_gc) {
|
return match definition.create_element(document, prefix.clone(), can_gc) {
|
||||||
Ok(element) => {
|
Ok(element) => {
|
||||||
element.set_custom_element_definition(definition.clone());
|
element.set_custom_element_definition(definition.clone());
|
||||||
element
|
element
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
// Step 6. Recovering from exception.
|
// If any of these steps threw an exception exception:
|
||||||
let global =
|
let global =
|
||||||
GlobalScope::current().unwrap_or_else(|| document.global());
|
GlobalScope::current().unwrap_or_else(|| document.global());
|
||||||
let cx = GlobalScope::get_cx();
|
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 {
|
unsafe {
|
||||||
let ar = enter_realm(&*global);
|
let ar = enter_realm(&*global);
|
||||||
throw_dom_exception(cx, &global, error);
|
throw_dom_exception(cx, &global, error);
|
||||||
report_pending_exception(*cx, true, InRealm::Entered(&ar), can_gc);
|
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(
|
let element = DomRoot::upcast::<Element>(HTMLUnknownElement::new(
|
||||||
local_name, prefix, document, proto, can_gc,
|
local_name, prefix, document, proto, can_gc,
|
||||||
));
|
));
|
||||||
|
@ -182,28 +206,38 @@ fn create_html_element(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
// Step 4.2. Otherwise:
|
||||||
} 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
|
|
||||||
CustomElementCreationMode::Asynchronous => {
|
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);
|
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 {
|
match is {
|
||||||
Some(is) => {
|
Some(is) => {
|
||||||
result.set_is(is);
|
result.set_is(is);
|
||||||
|
@ -218,7 +252,7 @@ fn create_html_element(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 8
|
// Step 6. Return result.
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -710,7 +710,7 @@ impl CustomElementDefinition {
|
||||||
self.name == self.local_name
|
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)]
|
#[allow(unsafe_code)]
|
||||||
pub(crate) fn create_element(
|
pub(crate) fn create_element(
|
||||||
&self,
|
&self,
|
||||||
|
@ -720,12 +720,13 @@ impl CustomElementDefinition {
|
||||||
) -> Fallible<DomRoot<Element>> {
|
) -> Fallible<DomRoot<Element>> {
|
||||||
let window = document.window();
|
let window = document.window();
|
||||||
let cx = GlobalScope::get_cx();
|
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 constructor = ObjectValue(self.constructor.callback()));
|
||||||
rooted!(in(*cx) let mut element = ptr::null_mut::<JSObject>());
|
rooted!(in(*cx) let mut element = ptr::null_mut::<JSObject>());
|
||||||
{
|
{
|
||||||
// Go into the constructor's realm
|
// Go into the constructor's realm
|
||||||
let _ac = JSAutoRealm::new(*cx, self.constructor.callback());
|
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();
|
let args = HandleValueArray::empty();
|
||||||
if unsafe { !Construct1(*cx, constructor.handle(), &args, element.handle_mut()) } {
|
if unsafe { !Construct1(*cx, constructor.handle(), &args, element.handle_mut()) } {
|
||||||
return Err(Error::JSFailed);
|
return Err(Error::JSFailed);
|
||||||
|
@ -752,14 +753,18 @@ impl CustomElementDefinition {
|
||||||
_ => return Err(Error::JSFailed),
|
_ => return Err(Error::JSFailed),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 3
|
// Step 4.1.3. Assert: result’s custom element state and custom element definition are initialized.
|
||||||
if !element.is::<HTMLElement>() {
|
// Step 4.1.4. Assert: result’s namespace is the HTML namespace.
|
||||||
return Err(Error::Type(
|
// Note: IDL enforces that result is an HTMLElement object, which all use the HTML namespace.
|
||||||
"Constructor did not return a DOM node".to_owned(),
|
// Note: the custom element definition is initialized by the caller if
|
||||||
));
|
// this method returns a success value.
|
||||||
}
|
assert!(element.is::<HTMLElement>());
|
||||||
|
|
||||||
// Steps 4-9
|
// 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() ||
|
if element.HasAttributes() ||
|
||||||
element.upcast::<Node>().children_count() > 0 ||
|
element.upcast::<Node>().children_count() > 0 ||
|
||||||
element.upcast::<Node>().has_parent() ||
|
element.upcast::<Node>().has_parent() ||
|
||||||
|
@ -770,10 +775,10 @@ impl CustomElementDefinition {
|
||||||
return Err(Error::NotSupported);
|
return Err(Error::NotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 10
|
// Step 4.1.10. Set result’s namespace prefix to prefix.
|
||||||
element.set_prefix(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
|
// Element's `is` is None by default
|
||||||
|
|
||||||
Ok(element)
|
Ok(element)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue