mirror of
https://github.com/lexmount/moli.git
synced 2026-10-04 00:00:50 +00:00
fix(forms): track image form owners
This commit is contained in:
@@ -3282,7 +3282,6 @@ html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLU
|
||||
html/webappapis/dynamic-markup-insertion/html-unsafe-methods/Document-parseHTMLUnsafe.html
|
||||
html/webappapis/dynamic-markup-insertion/html-unsafe-methods/setHTMLUnsafe-xml.html
|
||||
html/webappapis/scripting/event-loops/microtask_before_prepare_the_script_element-01.html
|
||||
html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
|
||||
html/webappapis/scripting/events/event-handler-attributes-frameset-alt.html
|
||||
html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
|
||||
html/webappapis/scripting/events/event-handler-attributes-window-frameset.html
|
||||
|
||||
@@ -6658,6 +6658,7 @@ html/webappapis/scripting/event-loops/microtask_after_script.html
|
||||
html/webappapis/scripting/event-loops/task_microtask_ordering.html
|
||||
html/webappapis/scripting/event-loops/update-the-rendering-resize-autofocus.html
|
||||
html/webappapis/scripting/events/body-onload.html
|
||||
html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
|
||||
html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html
|
||||
html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html
|
||||
html/webappapis/scripting/events/event-handler-all-global-events.html
|
||||
|
||||
@@ -49,3 +49,18 @@ fn is_html_frame_owner_candidate(local_name: &str, namespace: &str) -> bool {
|
||||
.into_iter()
|
||||
.any(|name| local_name.eq_ignore_ascii_case(name))
|
||||
}
|
||||
|
||||
fn is_builtin_reassociateable_form_associated_element(element: &Element) -> bool {
|
||||
element.namespace() == "http://www.w3.org/1999/xhtml"
|
||||
&& matches!(
|
||||
element.local_name(),
|
||||
"button" | "fieldset" | "input" | "object" | "output" | "select" | "textarea"
|
||||
)
|
||||
}
|
||||
|
||||
fn is_parser_form_association_candidate(element: &Element) -> bool {
|
||||
// HTMLImageElement keeps a parser/ancestor form owner for legacy
|
||||
// behavior, but is neither listed nor reassociateable.
|
||||
is_builtin_reassociateable_form_associated_element(element)
|
||||
|| (element.namespace() == "http://www.w3.org/1999/xhtml" && element.local_name() == "img")
|
||||
}
|
||||
|
||||
@@ -121,13 +121,20 @@ impl DomHost {
|
||||
.is_some_and(is_listed_form_control_element)
|
||||
}
|
||||
|
||||
pub fn builtin_form_associated_owner(&self, handle: DomHandle) -> Option<DomHandle> {
|
||||
let element = self.node(handle).and_then(Node::as_element)?;
|
||||
if !is_parser_form_association_candidate(element) {
|
||||
return None;
|
||||
}
|
||||
if is_builtin_reassociateable_form_associated_element(element) {
|
||||
return self.form_control_owner(handle);
|
||||
}
|
||||
self.parser_or_ancestor_form_owner(handle)
|
||||
}
|
||||
|
||||
pub fn form_control_owner(&self, handle: DomHandle) -> Option<DomHandle> {
|
||||
let element = self.node(handle).and_then(Node::as_element)?;
|
||||
if !matches!(
|
||||
element.local_name(),
|
||||
"button" | "fieldset" | "input" | "object" | "output" | "select" | "textarea"
|
||||
) || element.namespace() != "http://www.w3.org/1999/xhtml"
|
||||
{
|
||||
if !is_builtin_reassociateable_form_associated_element(element) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -145,7 +152,14 @@ impl DomHost {
|
||||
.then_some(candidate);
|
||||
}
|
||||
|
||||
if let Some(owner) = element.parser_associated_form_owner()
|
||||
self.parser_or_ancestor_form_owner(handle)
|
||||
}
|
||||
|
||||
fn parser_or_ancestor_form_owner(&self, handle: DomHandle) -> Option<DomHandle> {
|
||||
if let Some(owner) = self
|
||||
.node(handle)
|
||||
.and_then(Node::as_element)
|
||||
.and_then(Element::parser_associated_form_owner)
|
||||
&& self.is_html_element_named(owner, "form")
|
||||
&& self.root_node_handle(handle) == self.root_node_handle(owner)
|
||||
{
|
||||
|
||||
@@ -92,6 +92,13 @@ impl DomHost {
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
) {
|
||||
if !self
|
||||
.node(handle)
|
||||
.and_then(Node::as_element)
|
||||
.is_some_and(is_builtin_reassociateable_form_associated_element)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.clear_parser_associated_form_owner(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -114,12 +114,7 @@ impl DomHost {
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if element.namespace() != "http://www.w3.org/1999/xhtml"
|
||||
|| !matches!(
|
||||
element.local_name(),
|
||||
"button" | "fieldset" | "input" | "object" | "output" | "select" | "textarea"
|
||||
)
|
||||
{
|
||||
if !is_parser_form_association_candidate(element) {
|
||||
return false;
|
||||
}
|
||||
element.set_parser_associated_form_owner(Some(form))
|
||||
|
||||
@@ -1427,6 +1427,45 @@ mod tests {
|
||||
assert_eq!(host.form_control_owner(nested_input), Some(form));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_form_owner_tracks_parser_and_ancestor_without_becoming_listed() {
|
||||
let mut host = DomHost::from_dom(NativeDom::new_html(test_url()));
|
||||
host.reset_html_document_shell();
|
||||
let body = host.document_body_handle().expect("document body");
|
||||
let parser_form = host.create_element("form");
|
||||
let ancestor_form = host.create_element("form");
|
||||
let image = host.create_parser_element_without_attributes(
|
||||
"img".to_owned(),
|
||||
"http://www.w3.org/1999/xhtml".to_owned(),
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(host.set_attribute(ancestor_form, "id", "attribute-owner"));
|
||||
assert!(host.append_child(body, parser_form));
|
||||
assert!(host.append_child(body, ancestor_form));
|
||||
assert!(host.associate_parser_form_owner(image, parser_form));
|
||||
assert!(host.append_child(body, image));
|
||||
assert_eq!(host.builtin_form_associated_owner(image), Some(parser_form));
|
||||
assert_eq!(host.form_control_owner(image), None);
|
||||
assert!(host.form_control_elements(parser_form).is_empty());
|
||||
|
||||
assert!(host.set_attribute(image, "form", "attribute-owner"));
|
||||
assert_eq!(host.builtin_form_associated_owner(image), Some(parser_form));
|
||||
|
||||
assert!(host.append_child(ancestor_form, image));
|
||||
assert_eq!(
|
||||
host.builtin_form_associated_owner(image),
|
||||
Some(ancestor_form)
|
||||
);
|
||||
assert_eq!(
|
||||
host.node(image)
|
||||
.and_then(Node::as_element)
|
||||
.and_then(Element::parser_associated_form_owner),
|
||||
None
|
||||
);
|
||||
assert!(host.form_control_elements(ancestor_form).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secondary_document_form_attribute_remains_authoritative() {
|
||||
let mut host = DomHost::from_dom(NativeDom::new_html(test_url()));
|
||||
|
||||
@@ -10,12 +10,9 @@ pub(crate) fn form_associated_form_owner(
|
||||
if is_form_associated_custom_element_handle(runtime, handle) {
|
||||
return custom_element_form_owner(runtime, handle, element);
|
||||
}
|
||||
if !is_builtin_form_associated_element(element) {
|
||||
return None;
|
||||
}
|
||||
runtime
|
||||
.dom_host()
|
||||
.form_control_owner(handle)
|
||||
.builtin_form_associated_owner(handle)
|
||||
.filter(|candidate| {
|
||||
runtime
|
||||
.dom_host()
|
||||
@@ -59,7 +56,7 @@ fn custom_element_form_owner(
|
||||
None
|
||||
}
|
||||
|
||||
fn is_builtin_form_associated_element(element: &crate::dom::native::Element) -> bool {
|
||||
fn has_reflected_builtin_form_owner(element: &crate::dom::native::Element) -> bool {
|
||||
element.namespace() == "http://www.w3.org/1999/xhtml"
|
||||
&& matches!(
|
||||
element.local_name(),
|
||||
@@ -72,7 +69,7 @@ fn form_associated_reflected_form_owner(
|
||||
handle: DomHandle,
|
||||
) -> Option<DomHandle> {
|
||||
let element = runtime.dom_host().node(handle).and_then(Node::as_element)?;
|
||||
if !is_builtin_form_associated_element(element) {
|
||||
if !has_reflected_builtin_form_owner(element) {
|
||||
return None;
|
||||
}
|
||||
if let Some(form_id) = element.attribute("form")
|
||||
|
||||
@@ -18,6 +18,7 @@ fn event_attribute_handlers_use_html_scope_chain_and_report_compile_errors() {
|
||||
<table><tbody><tr><td id="cell"><img id="cell-inner"></td></tr></tbody></table>
|
||||
<form id="owner" onsubmit="return false">
|
||||
<button id="button" type="button"><q id="button-inner"></q></button>
|
||||
<img id="form-image" alt="form image">
|
||||
</form>
|
||||
<a id="error-inner"></a>
|
||||
`;
|
||||
@@ -63,6 +64,14 @@ fn event_attribute_handlers_use_html_scope_chain_and_report_compile_errors() {
|
||||
`);
|
||||
buttonInner.click();
|
||||
|
||||
const formImage = document.getElementById("form-image");
|
||||
globalThis.elements = "global-elements";
|
||||
formImage.setAttribute(
|
||||
"onclick",
|
||||
`globalThis.__imageFormScope = elements;`
|
||||
);
|
||||
formImage.click();
|
||||
|
||||
globalThis.__windowScope = null;
|
||||
globalThis.__compileErrorEvents = 0;
|
||||
document.body.bodyOwn = true;
|
||||
@@ -84,6 +93,8 @@ fn event_attribute_handlers_use_html_scope_chain_and_report_compile_errors() {
|
||||
return JSON.stringify({
|
||||
cell: globalThis.__cellScope,
|
||||
form: globalThis.__formScope,
|
||||
imageForm: globalThis.__imageFormScope === form.elements,
|
||||
imageIsNotListed: !Array.from(form.elements).includes(formImage),
|
||||
window: globalThis.__windowScope,
|
||||
errors: globalThis.__compileErrorEvents,
|
||||
});
|
||||
@@ -94,7 +105,7 @@ fn event_attribute_handlers_use_html_scope_chain_and_report_compile_errors() {
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"cell":["number","string","function","boolean","undefined","object","updated"],"form":["boolean","object","string","string","boolean","boolean","undefined","object"],"window":["undefined","function","undefined","string"],"errors":1}"#,
|
||||
r#"{"cell":["number","string","function","boolean","undefined","object","updated"],"form":["boolean","object","string","string","boolean","boolean","undefined","object"],"imageForm":true,"imageIsNotListed":true,"window":["undefined","function","undefined","string"],"errors":1}"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user