aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorSimon Wülker <simon.wuelker@arcor.de>2025-03-12 18:53:27 +0100
committerGitHub <noreply@github.com>2025-03-12 17:53:27 +0000
commitb10c53ba6b71b18ad847377a85e1d8520e5c2ada (patch)
tree9494063fdb2249aef2f0fa3919a1502e96b677db /components/script/dom
parentbb3d28bca722082c451c2d45dac929ee1abb5368 (diff)
downloadservo-b10c53ba6b71b18ad847377a85e1d8520e5c2ada.tar.gz
servo-b10c53ba6b71b18ad847377a85e1d8520e5c2ada.zip
Set `is` value when constructing custom elements with the `new` operator (#35930)
* Add doc comments throughout the code Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Set is-value for elements constructed with the "new" operator Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Allow custom elements to extend <slot> Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/constructor.rs52
-rw-r--r--components/script/dom/create.rs2
-rw-r--r--components/script/dom/customelementregistry.rs37
-rw-r--r--components/script/dom/element.rs2
-rw-r--r--components/script/dom/node.rs12
5 files changed, 71 insertions, 34 deletions
diff --git a/components/script/dom/bindings/constructor.rs b/components/script/dom/bindings/constructor.rs
index a5874ed4397..ae9305f0767 100644
--- a/components/script/dom/bindings/constructor.rs
+++ b/components/script/dom/bindings/constructor.rs
@@ -31,12 +31,12 @@ use crate::dom::bindings::codegen::Bindings::{
HTMLOptionElementBinding, HTMLOutputElementBinding, HTMLParagraphElementBinding,
HTMLParamElementBinding, HTMLPictureElementBinding, HTMLPreElementBinding,
HTMLProgressElementBinding, HTMLQuoteElementBinding, HTMLScriptElementBinding,
- HTMLSelectElementBinding, HTMLSourceElementBinding, HTMLSpanElementBinding,
- HTMLStyleElementBinding, HTMLTableCaptionElementBinding, HTMLTableCellElementBinding,
- HTMLTableColElementBinding, HTMLTableElementBinding, HTMLTableRowElementBinding,
- HTMLTableSectionElementBinding, HTMLTemplateElementBinding, HTMLTextAreaElementBinding,
- HTMLTimeElementBinding, HTMLTitleElementBinding, HTMLTrackElementBinding,
- HTMLUListElementBinding, HTMLVideoElementBinding,
+ HTMLSelectElementBinding, HTMLSlotElementBinding, HTMLSourceElementBinding,
+ HTMLSpanElementBinding, HTMLStyleElementBinding, HTMLTableCaptionElementBinding,
+ HTMLTableCellElementBinding, HTMLTableColElementBinding, HTMLTableElementBinding,
+ HTMLTableRowElementBinding, HTMLTableSectionElementBinding, HTMLTemplateElementBinding,
+ HTMLTextAreaElementBinding, HTMLTimeElementBinding, HTMLTitleElementBinding,
+ HTMLTrackElementBinding, HTMLUListElementBinding, HTMLVideoElementBinding,
};
use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::conversions::DerivedFrom;
@@ -54,7 +54,7 @@ use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext, JSContext as SafeJSContext};
use crate::script_thread::ScriptThread;
-// https://html.spec.whatwg.org/multipage/#htmlconstructor
+/// <https://html.spec.whatwg.org/multipage/#htmlconstructor>
unsafe fn html_constructor(
cx: JSContext,
global: &GlobalScope,
@@ -67,7 +67,7 @@ unsafe fn html_constructor(
let window = global.downcast::<Window>().unwrap();
let document = window.Document();
- // Step 1
+ // Step 1. Let registry be current global object's custom element registry.
let registry = window.CustomElements();
// Step 2 https://html.spec.whatwg.org/multipage/#htmlconstructor
@@ -95,7 +95,8 @@ unsafe fn html_constructor(
return Err(());
}
- // Step 3
+ // Step 3. Let definition be the item in registry's custom element definition set with constructor
+ // equal to NewTarget. If there is no such item, then throw a TypeError.
rooted!(in(*cx) let new_target = call_args.new_target().to_object());
let definition = match registry.lookup_definition_by_constructor(new_target.handle()) {
Some(definition) => definition,
@@ -110,6 +111,9 @@ unsafe fn html_constructor(
},
};
+ // Step 4. Let isValue be null.
+ let mut is_value = None;
+
rooted!(in(*cx) let callee = UnwrapObjectStatic(call_args.callee()));
if callee.is_null() {
throw_dom_exception(cx, global, Error::Security, can_gc);
@@ -121,24 +125,28 @@ unsafe fn html_constructor(
rooted!(in(*cx) let mut constructor = ptr::null_mut::<JSObject>());
rooted!(in(*cx) let global_object = CurrentGlobalOrNull(*cx));
+ // Step 5. If definition's local name is equal to definition's name
+ // (i.e., definition is for an autonomous custom element):
if definition.is_autonomous() {
- // Step 4
// Since this element is autonomous, its active function object must be the HTMLElement
-
// Retrieve the constructor object for HTMLElement
HTMLElementBinding::GetConstructorObject(
cx,
global_object.handle(),
constructor.handle_mut(),
);
- } else {
- // Step 5
+ }
+ // Step 6. Otherwise (i.e., if definition is for a customized built-in element):
+ else {
get_constructor_object_from_local_name(
definition.local_name.clone(),
cx,
global_object.handle(),
constructor.handle_mut(),
);
+
+ // Step 6.3 Set isValue to definition's name.
+ is_value = Some(definition.name.clone());
}
// Callee must be the same as the element interface's constructor object.
if constructor.get() != callee.get() {
@@ -158,9 +166,9 @@ unsafe fn html_constructor(
let entry = definition.construction_stack.borrow().last().cloned();
let result = match entry {
- // Step 8
+ // Step 7. If definition's construction stack is empty:
None => {
- // Step 8.1
+ // Step 7.1
let name = QualName::new(None, ns!(html), definition.local_name.clone());
// Any prototype used to create these elements will be overwritten before returning
// from this function, so we don't bother overwriting the defaults here.
@@ -176,19 +184,24 @@ unsafe fn html_constructor(
)
};
- // Step 8.2 is performed in the generated caller code.
+ // Step 7.2-7.5 are performed in the generated caller code.
- // Step 8.3
+ // Step 7.6 Set element's custom element state to "custom".
element.set_custom_element_state(CustomElementState::Custom);
- // Step 8.4
+ // Step 7.7 Set element's custom element definition to definition.
element.set_custom_element_definition(definition.clone());
- // Step 8.5
+ // Step 7.8 Set element's is value to isValue.
+ if let Some(is_value) = is_value {
+ element.set_is(is_value);
+ }
+
if !check_type(&element) {
throw_dom_exception(cx, global, Error::InvalidState, can_gc);
return Err(());
} else {
+ // Step 7.9 Return element.
element
}
},
@@ -339,6 +352,7 @@ fn get_constructor_object_from_local_name(
local_name!("script") => HTMLScriptElementBinding::GetConstructorObject,
local_name!("section") => HTMLElementBinding::GetConstructorObject,
local_name!("select") => HTMLSelectElementBinding::GetConstructorObject,
+ local_name!("slot") => HTMLSlotElementBinding::GetConstructorObject,
local_name!("small") => HTMLElementBinding::GetConstructorObject,
local_name!("source") => HTMLSourceElementBinding::GetConstructorObject,
local_name!("span") => HTMLSpanElementBinding::GetConstructorObject,
diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs
index 14774549b27..c2e038eb66b 100644
--- a/components/script/dom/create.rs
+++ b/components/script/dom/create.rs
@@ -119,7 +119,7 @@ fn create_svg_element(
}
}
-// https://dom.spec.whatwg.org/#concept-create-element
+/// <https://dom.spec.whatwg.org/#concept-create-element>
#[allow(unsafe_code)]
#[allow(clippy::too_many_arguments)]
fn create_html_element(
diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs
index 5e570743325..a9e787068f1 100644
--- a/components/script/dom/customelementregistry.rs
+++ b/components/script/dom/customelementregistry.rs
@@ -664,25 +664,34 @@ pub(crate) enum ConstructionStackEntry {
/// <https://html.spec.whatwg.org/multipage/#custom-element-definition>
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) struct CustomElementDefinition {
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-name>
#[no_trace]
pub(crate) name: LocalName,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-local-name>
#[no_trace]
pub(crate) local_name: LocalName,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-constructor>
#[ignore_malloc_size_of = "Rc"]
pub(crate) constructor: Rc<CustomElementConstructor>,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-observed-attributes>
pub(crate) observed_attributes: Vec<DOMString>,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-lifecycle-callbacks>
pub(crate) callbacks: LifecycleCallbacks,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-construction-stack>
pub(crate) construction_stack: DomRefCell<Vec<ConstructionStackEntry>>,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-form-associated>
pub(crate) form_associated: bool,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-disable-internals>
pub(crate) disable_internals: bool,
+ /// <https://html.spec.whatwg.org/multipage/#concept-custom-element-definition-disable-shadow>
pub(crate) disable_shadow: bool,
}
@@ -797,19 +806,21 @@ pub(crate) fn upgrade_element(
element: &Element,
can_gc: CanGc,
) {
- // Step 1
+ // Step 1. If element's custom element state is not "undefined" or "uncustomized", then return.
let state = element.get_custom_element_state();
if state != CustomElementState::Undefined && state != CustomElementState::Uncustomized {
return;
}
- // Step 2
+ // Step 2. Set element's custom element definition to definition.
element.set_custom_element_definition(Rc::clone(&definition));
- // Step 3
+ // Step 3. Set element's custom element state to "failed".
element.set_custom_element_state(CustomElementState::Failed);
- // Step 4
+ // Step 4. For each attribute in element's attribute list, in order, enqueue a custom element callback reaction
+ // with element, callback name "attributeChangedCallback", and « attribute's local name, null, attribute's value,
+ // attribute's namespace ».
for attr in element.attrs().iter() {
let local_name = attr.local_name().clone();
let value = DOMString::from(&**attr.value());
@@ -821,7 +832,8 @@ pub(crate) fn upgrade_element(
);
}
- // Step 5
+ // Step 5. If element is connected, then enqueue a custom element callback reaction with element,
+ // callback name "connectedCallback", and « ».
if element.is_connected() {
ScriptThread::enqueue_callback_reaction(
element,
@@ -830,7 +842,7 @@ pub(crate) fn upgrade_element(
);
}
- // Step 6
+ // Step 6. Add element to the end of definition's construction stack.
definition
.construction_stack
.borrow_mut()
@@ -976,7 +988,8 @@ fn run_upgrade_constructor(
/// <https://html.spec.whatwg.org/multipage/#concept-try-upgrade>
pub(crate) fn try_upgrade_element(element: &Element) {
- // Step 1
+ // Step 1. Let definition be the result of looking up a custom element definition given element's node document,
+ // element's namespace, element's local name, and element's is value.
let document = element.owner_document();
let namespace = element.namespace();
let local_name = element.local_name();
@@ -984,7 +997,8 @@ pub(crate) fn try_upgrade_element(element: &Element) {
if let Some(definition) =
document.lookup_custom_element_definition(namespace, local_name, is.as_ref())
{
- // Step 2
+ // Step 2. If definition is not null, then enqueue a custom element upgrade reaction given
+ // element and definition.
ScriptThread::enqueue_upgrade_reaction(element, definition);
}
}
@@ -1242,9 +1256,11 @@ impl CustomElementReactionStack {
element: &Element,
definition: Rc<CustomElementDefinition>,
) {
- // Step 1
+ // Step 1. Add a new upgrade reaction to element's custom element reaction queue,
+ // with custom element definition definition.
element.push_upgrade_reaction(definition);
- // Step 2
+
+ // Step 2. Enqueue an element on the appropriate element queue given element.
self.enqueue_element(element);
}
}
@@ -1449,6 +1465,7 @@ fn is_extendable_element_interface(element: &str) -> bool {
element == "script" ||
element == "section" ||
element == "select" ||
+ element == "slot" ||
element == "small" ||
element == "source" ||
element == "span" ||
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 79b783cbfb4..35ccd0d89e2 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -174,6 +174,7 @@ pub struct Element {
attrs: DomRefCell<Vec<Dom<Attr>>>,
#[no_trace]
id_attribute: DomRefCell<Option<Atom>>,
+ /// <https://dom.spec.whatwg.org/#concept-element-is-value>
#[no_trace]
is: DomRefCell<Option<LocalName>>,
#[ignore_malloc_size_of = "Arc"]
@@ -344,6 +345,7 @@ impl Element {
*self.is.borrow_mut() = Some(is);
}
+ /// <https://dom.spec.whatwg.org/#concept-element-is-value>
pub(crate) fn get_is(&self) -> Option<LocalName> {
self.is.borrow().clone()
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 4a4c9f53462..2837e25c08a 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -3261,20 +3261,24 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
}
/// <https://dom.spec.whatwg.org/#dom-node-clonenode>
- fn CloneNode(&self, deep: bool, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
+ fn CloneNode(&self, subtree: bool, can_gc: CanGc) -> Fallible<DomRoot<Node>> {
+ // Step 1. If this is a shadow root, then throw a "NotSupportedError" DOMException.
if self.is::<ShadowRoot>() {
return Err(Error::NotSupported);
}
- Ok(Node::clone(
+
+ // Step 2. Return the result of cloning a node given this with subtree set to subtree.
+ let result = Node::clone(
self,
None,
- if deep {
+ if subtree {
CloneChildrenFlag::CloneChildren
} else {
CloneChildrenFlag::DoNotCloneChildren
},
can_gc,
- ))
+ );
+ Ok(result)
}
/// <https://dom.spec.whatwg.org/#dom-node-isequalnode>