aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXiaocheng Hu <xiaochengh.work@gmail.com>2025-03-14 02:03:57 +0800
committerGitHub <noreply@github.com>2025-03-13 18:03:57 +0000
commit62d6759106c987ed438d6a8e281fea28859300d3 (patch)
treee6d4e4e64311d059671fafbecd05da0fe0030964
parentea35353e9aacb65944b8e23ae564cc771bdab33f (diff)
downloadservo-62d6759106c987ed438d6a8e281fea28859300d3.tar.gz
servo-62d6759106c987ed438d6a8e281fea28859300d3.zip
Check whether an element is custom in the spec-compliant way (#35960)
* Check whether element is custom in spec-compliant way Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com> * Update tests Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com> --------- Signed-off-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
-rw-r--r--components/script/dom/attr.rs2
-rw-r--r--components/script/dom/element.rs17
-rw-r--r--components/script/dom/node.rs5
-rw-r--r--tests/wpt/meta/MANIFEST.json2
-rw-r--r--tests/wpt/meta/custom-elements/custom-element-reaction-queue.html.ini4
-rw-r--r--tests/wpt/tests/custom-elements/custom-element-reaction-queue.html54
6 files changed, 71 insertions, 13 deletions
diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs
index fff594cead0..428f02cece3 100644
--- a/components/script/dom/attr.rs
+++ b/components/script/dom/attr.rs
@@ -166,7 +166,7 @@ impl Attr {
MutationObserver::queue_a_mutation_record(owner.upcast::<Node>(), mutation);
- if owner.get_custom_element_definition().is_some() {
+ if owner.is_custom() {
let reaction = CallbackReaction::AttributeChanged(
name,
Some(old_value),
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 43f09bc60cf..2b06da5b6ce 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -371,6 +371,11 @@ impl Element {
CustomElementState::Uncustomized
}
+ /// <https://dom.spec.whatwg.org/#concept-element-custom>
+ pub(crate) fn is_custom(&self) -> bool {
+ self.get_custom_element_state() == CustomElementState::Custom
+ }
+
pub(crate) fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
self.ensure_rare_data().custom_element_definition = Some(definition);
}
@@ -1580,7 +1585,7 @@ impl Element {
MutationObserver::queue_a_mutation_record(&self.node, mutation);
- if self.get_custom_element_definition().is_some() {
+ if self.is_custom() {
let value = DOMString::from(&**attr.value());
let reaction = CallbackReaction::AttributeChanged(name, None, Some(value), namespace);
ScriptThread::enqueue_callback_reaction(self, reaction, None);
@@ -1772,9 +1777,11 @@ impl Element {
MutationObserver::queue_a_mutation_record(&self.node, mutation);
- let reaction =
- CallbackReaction::AttributeChanged(name, Some(old_value), None, namespace);
- ScriptThread::enqueue_callback_reaction(self, reaction, None);
+ if self.is_custom() {
+ let reaction =
+ CallbackReaction::AttributeChanged(name, Some(old_value), None, namespace);
+ ScriptThread::enqueue_callback_reaction(self, reaction, None);
+ }
self.attrs.borrow_mut().remove(idx);
attr.set_owner(None);
@@ -2453,7 +2460,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
}
// Step 4.
- if self.get_custom_element_definition().is_some() {
+ if self.is_custom() {
let old_name = old_attr.local_name().clone();
let old_value = DOMString::from(&**old_attr.value());
let new_value = DOMString::from(&**attr.value());
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 6ae2c5b646f..6326b59f62d 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -422,7 +422,8 @@ impl Node {
pub(crate) fn as_custom_element(&self) -> Option<DomRoot<Element>> {
self.downcast::<Element>().and_then(|element| {
- if element.get_custom_element_definition().is_some() {
+ if element.is_custom() {
+ assert!(element.get_custom_element_definition().is_some());
Some(DomRoot::from_ref(element))
} else {
None
@@ -2334,7 +2335,7 @@ impl Node {
.filter_map(DomRoot::downcast::<Element>)
{
// Step 7.7.2, whatwg/dom#833
- if descendant.get_custom_element_definition().is_some() {
+ if descendant.is_custom() {
if descendant.is_connected() {
ScriptThread::enqueue_callback_reaction(
&descendant,
diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json
index e54ddc40d22..de9221bf3ac 100644
--- a/tests/wpt/meta/MANIFEST.json
+++ b/tests/wpt/meta/MANIFEST.json
@@ -612804,7 +612804,7 @@
]
],
"custom-element-reaction-queue.html": [
- "246b15a0af36cffa0b64f1d57e4208538b92bdd7",
+ "eb8366f2b415894f396da613987982ae41ffe0b6",
[
null,
{}
diff --git a/tests/wpt/meta/custom-elements/custom-element-reaction-queue.html.ini b/tests/wpt/meta/custom-elements/custom-element-reaction-queue.html.ini
deleted file mode 100644
index bd5d04c5256..00000000000
--- a/tests/wpt/meta/custom-elements/custom-element-reaction-queue.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[custom-element-reaction-queue.html]
- [Upgrading a custom element must not invoke attributeChangedCallback for the attribute that is changed during upgrading]
- expected: FAIL
-
diff --git a/tests/wpt/tests/custom-elements/custom-element-reaction-queue.html b/tests/wpt/tests/custom-elements/custom-element-reaction-queue.html
index 246b15a0af3..eb8366f2b41 100644
--- a/tests/wpt/tests/custom-elements/custom-element-reaction-queue.html
+++ b/tests/wpt/tests/custom-elements/custom-element-reaction-queue.html
@@ -85,6 +85,60 @@ test_with_window(function (contentWindow) {
test_with_window(function (contentWindow) {
const contentDocument = contentWindow.document;
+ contentDocument.write('<test-element>');
+
+ const element = contentDocument.querySelector('test-element');
+ assert_equals(Object.getPrototypeOf(element), contentWindow.HTMLElement.prototype);
+
+ let log = [];
+ class TestElement extends contentWindow.HTMLElement {
+ constructor() {
+ super();
+ this.remove();
+ log.push(create_constructor_log(this));
+ }
+ connectedCallback(...args) {
+ log.push(create_connected_callback_log(this, ...args));
+ }
+ disconnectedCallback(...args) {
+ log.push(create_disconnected_callback_log(this, ...args));
+ }
+ }
+ contentWindow.customElements.define('test-element', TestElement);
+ assert_equals(Object.getPrototypeOf(element), TestElement.prototype);
+
+ assert_equals(log.length, 2);
+ assert_constructor_log_entry(log[0], element);
+ assert_connected_log_entry(log[1], element);
+}, 'Upgrading a custom element must not invoke disconnectedCallback if the element is disconnected during upgrading');
+
+test_with_window(function (contentWindow) {
+ const contentDocument = contentWindow.document;
+ const element = contentDocument.createElement('test-element');
+ assert_equals(Object.getPrototypeOf(element), contentWindow.HTMLElement.prototype);
+
+ let log = [];
+ class TestElement extends contentWindow.HTMLElement {
+ constructor() {
+ super();
+ contentDocument.documentElement.appendChild(this);
+ log.push(create_constructor_log(this));
+ }
+ connectedCallback(...args) {
+ log.push(create_connected_callback_log(this, ...args));
+ }
+ }
+ contentWindow.customElements.define('test-element', TestElement);
+ contentWindow.customElements.upgrade(element);
+
+ assert_equals(Object.getPrototypeOf(element), TestElement.prototype);
+
+ assert_equals(log.length, 1);
+ assert_constructor_log_entry(log[0], element);
+}, 'Upgrading a disconnected custom element must not invoke connectedCallback if the element is connected during upgrading');
+
+test_with_window(function (contentWindow) {
+ const contentDocument = contentWindow.document;
contentDocument.write('<test-element id="first-element">');
contentDocument.write('<test-element id="second-element">');