script: Reset form controls immediately after parsing them (#41962)

This change brings Servo closer to the specification by removing the
unspecified sanitization flags used during parsing of input elements.
Instead we implement the lines of the specification that say to reset
resettable elements after parsing them. This forces a re-sanitization of
the default value (from the `value` attribute), clearing up the
confusion in parser comments.

In addition, specification text is added in the element creation code.

Testing: This just brings our code closer to the specification, so it is
covered by existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson
2026-01-17 10:05:28 +01:00
committed by GitHub
parent c941575096
commit e8f9976f35
3 changed files with 148 additions and 122 deletions

View File

@@ -1355,44 +1355,7 @@ impl HTMLFormElement {
.collect();
for child in controls {
let child = child.upcast::<Node>();
match child.type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLInputElement,
)) => {
child.downcast::<HTMLInputElement>().unwrap().reset(can_gc);
},
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLSelectElement,
)) => {
child.downcast::<HTMLSelectElement>().unwrap().reset();
},
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLTextAreaElement,
)) => {
child
.downcast::<HTMLTextAreaElement>()
.unwrap()
.reset(can_gc);
},
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLOutputElement,
)) => {
child.downcast::<HTMLOutputElement>().unwrap().reset(can_gc);
},
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLElement)) => {
let html_element = child.downcast::<HTMLElement>().unwrap();
if html_element.is_form_associated_custom_element() {
ScriptThread::enqueue_callback_reaction(
html_element.upcast::<Element>(),
CallbackReaction::FormReset,
None,
)
}
},
_ => {},
}
child.reset(can_gc);
}
self.marked_for_reset.set(false);
}
@@ -1427,6 +1390,48 @@ impl HTMLFormElement {
}
}
impl Element {
pub(crate) fn is_resettable(&self) -> bool {
let NodeTypeId::Element(ElementTypeId::HTMLElement(element_type)) =
self.upcast::<Node>().type_id()
else {
return false;
};
matches!(
element_type,
HTMLElementTypeId::HTMLInputElement |
HTMLElementTypeId::HTMLSelectElement |
HTMLElementTypeId::HTMLTextAreaElement |
HTMLElementTypeId::HTMLOutputElement |
HTMLElementTypeId::HTMLElement
)
}
pub(crate) fn reset(&self, can_gc: CanGc) {
if !self.is_resettable() {
return;
}
if let Some(input_element) = self.downcast::<HTMLInputElement>() {
input_element.reset(can_gc);
} else if let Some(select_element) = self.downcast::<HTMLSelectElement>() {
select_element.reset();
} else if let Some(textarea_element) = self.downcast::<HTMLTextAreaElement>() {
textarea_element.reset(can_gc);
} else if let Some(output_element) = self.downcast::<HTMLOutputElement>() {
output_element.reset(can_gc);
} else if let Some(html_element) = self.downcast::<HTMLElement>() {
if html_element.is_form_associated_custom_element() {
ScriptThread::enqueue_callback_reaction(
html_element.upcast::<Element>(),
CallbackReaction::FormReset,
None,
)
}
}
}
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
pub(crate) enum FormDatumValue {
File(DomRoot<File>),