mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb/HTML: Ignore form owner if earlier same-ID element isn't a form
Only set form owner when there is no non-form element with the same ID as the control's form attribute earlier in tree order.
This commit is contained in:
parent
405e270583
commit
a0dbae02c8
Notes:
github-actions[bot]
2025-11-24 08:53:14 +00:00
Author: https://github.com/skyz1
Commit: a0dbae02c8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6914
Reviewed-by: https://github.com/gmta ✅
3 changed files with 341 additions and 3 deletions
|
|
@ -187,9 +187,10 @@ void FormAssociatedElement::reset_form_owner()
|
||||||
if (is_listed() && html_element.has_attribute(HTML::AttributeNames::form) && html_element.is_connected()) {
|
if (is_listed() && html_element.has_attribute(HTML::AttributeNames::form) && html_element.is_connected()) {
|
||||||
// 1. If the first element in element's tree, in tree order, to have an ID that is identical to element's form content attribute's value, is a form element, then associate the element with that form element.
|
// 1. If the first element in element's tree, in tree order, to have an ID that is identical to element's form content attribute's value, is a form element, then associate the element with that form element.
|
||||||
auto form_value = html_element.attribute(HTML::AttributeNames::form);
|
auto form_value = html_element.attribute(HTML::AttributeNames::form);
|
||||||
html_element.root().for_each_in_inclusive_subtree_of_type<HTMLFormElement>([this, &form_value](HTMLFormElement& form_element) {
|
html_element.root().for_each_in_inclusive_subtree_of_type<HTMLElement>([this, &form_value](auto& element) {
|
||||||
if (form_element.id() == form_value) {
|
if (element.id() == form_value) {
|
||||||
set_form(&form_element);
|
if (is<HTMLFormElement>(element))
|
||||||
|
set_form(as<HTMLFormElement>(&element));
|
||||||
return TraversalDecision::Break;
|
return TraversalDecision::Break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 99 tests
|
||||||
|
|
||||||
|
99 Pass
|
||||||
|
Pass Tests for parser inserted controls
|
||||||
|
Pass [BUTTON] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [BUTTON] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [BUTTON] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [BUTTON] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [BUTTON] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [BUTTON] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [BUTTON] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [BUTTON] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [BUTTON] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [BUTTON] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [BUTTON] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [BUTTON] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [BUTTON] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [BUTTON] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [FIELDSET] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [FIELDSET] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [FIELDSET] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [FIELDSET] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [FIELDSET] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [FIELDSET] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [FIELDSET] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [FIELDSET] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [FIELDSET] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [FIELDSET] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [FIELDSET] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [FIELDSET] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [FIELDSET] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [FIELDSET] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [INPUT] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [INPUT] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [INPUT] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [INPUT] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [INPUT] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [INPUT] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [INPUT] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [INPUT] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [INPUT] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [INPUT] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [INPUT] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [INPUT] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [INPUT] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [INPUT] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [OBJECT] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [OBJECT] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [OBJECT] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [OBJECT] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [OBJECT] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [OBJECT] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [OBJECT] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [OBJECT] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [OBJECT] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [OBJECT] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [OBJECT] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [OBJECT] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [OBJECT] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [OBJECT] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [OUTPUT] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [OUTPUT] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [OUTPUT] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [OUTPUT] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [OUTPUT] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [OUTPUT] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [OUTPUT] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [OUTPUT] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [OUTPUT] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [OUTPUT] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [OUTPUT] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [OUTPUT] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [OUTPUT] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [OUTPUT] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [SELECT] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [SELECT] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [SELECT] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [SELECT] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [SELECT] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [SELECT] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [SELECT] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [SELECT] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [SELECT] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [SELECT] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [SELECT] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [SELECT] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [SELECT] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [SELECT] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
Pass [TEXTAREA] Basic form association - control with no form attribute is associated with ancestor
|
||||||
|
Pass [TEXTAREA] Form owner is reset to null when control's form attribute is set to an ID that does not exist in the document
|
||||||
|
Pass [TEXTAREA] Control whose form attribute is an empty string has no form owner
|
||||||
|
Pass [TEXTAREA] Control whose form attribute is an empty string has no form owner even when form with empty attribute is present
|
||||||
|
Pass [TEXTAREA] Control's form attribute must be a case sensitive match for the form's id
|
||||||
|
Pass [TEXTAREA] Setting the form attribute of a control to the id of a non-ancestor form works
|
||||||
|
Pass [TEXTAREA] Removing form id from a control resets the form owner to ancestor
|
||||||
|
Pass [TEXTAREA] Removing the form owner of a control with form attribute resets the form owner to null
|
||||||
|
Pass [TEXTAREA] Changing form attibute of control resets form owner to correct form
|
||||||
|
Pass [TEXTAREA] Moving a control with form attribute within the document does not change the form owner
|
||||||
|
Pass [TEXTAREA] When the id of a non-ancestor form changes from not being a match for the form attribute to being a match, the control's form owner is reset
|
||||||
|
Pass [TEXTAREA] When form element with same ID as the control's form attribute is inserted earlier in tree order, the form owner is changed to the inserted form
|
||||||
|
Pass [TEXTAREA] When non-form element with same ID as the control's form attribute is inserted earlier in tree order, the control does not have a form owner
|
||||||
|
Pass [TEXTAREA] A control that is not in the document but has the form attribute set is associated with the nearest ancestor form if one exists
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="../../../../resources/testharness.js"></script>
|
||||||
|
<script src="../../../../resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<form id="f1"></form>
|
||||||
|
<form id="f2">
|
||||||
|
<input id="i1" />
|
||||||
|
<input id="i2" form="f1" />
|
||||||
|
<input id="i3" />
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
test(function() {
|
||||||
|
var i1 = document.getElementById("i1");
|
||||||
|
var i2 = document.getElementById("i2");
|
||||||
|
var i3 = document.getElementById("i3");
|
||||||
|
var f1 = document.getElementById("f1");
|
||||||
|
var f2 = document.getElementById("f2");
|
||||||
|
|
||||||
|
assert_equals(i1.form, f2,
|
||||||
|
"i1 must be associated with f2 by the parser");
|
||||||
|
assert_equals(i2.form, f1,
|
||||||
|
"i2 is not associated with f2 by the parser " +
|
||||||
|
"since it has the form attribute set to f1");
|
||||||
|
|
||||||
|
f1.appendChild(i1);
|
||||||
|
i3.setAttribute("form", "f1");
|
||||||
|
|
||||||
|
assert_equals(i1.form, f1,
|
||||||
|
"i1's form owner must be reset when parent changes");
|
||||||
|
assert_equals(i3.form, f1,
|
||||||
|
"i3's form owner must be reset when the form" +
|
||||||
|
"attribute is set");
|
||||||
|
|
||||||
|
assert_equals(i2.form, f1);
|
||||||
|
}, "Tests for parser inserted controls");
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="placeholder">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var reassociateableElements = [
|
||||||
|
"button",
|
||||||
|
"fieldset",
|
||||||
|
"input",
|
||||||
|
"object",
|
||||||
|
"output",
|
||||||
|
"select",
|
||||||
|
"textarea",
|
||||||
|
];
|
||||||
|
|
||||||
|
var form1 = null;
|
||||||
|
var form2 = null;
|
||||||
|
var placeholder = document.getElementById("placeholder");
|
||||||
|
|
||||||
|
reassociateableElements.forEach(function(localName) {
|
||||||
|
function testControl(test_, desc) {
|
||||||
|
test(function() {
|
||||||
|
var control = document.createElement(localName);
|
||||||
|
|
||||||
|
while(placeholder.firstChild)
|
||||||
|
placeholder.removeChild(placeholder.firstChild);
|
||||||
|
|
||||||
|
form1 = document.createElement("form");
|
||||||
|
form2 = document.createElement("form");
|
||||||
|
form1.id = "form1";
|
||||||
|
form2.id = "form2";
|
||||||
|
placeholder.appendChild(form1);
|
||||||
|
placeholder.appendChild(form2);
|
||||||
|
|
||||||
|
test_.call(control);
|
||||||
|
}, "[" + localName.toUpperCase() + "] " + desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "Basic form association - control with no form attribute is associated with ancestor");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form1.id = "form-one";
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
}, "Form owner is reset to null when control's form attribute is set to an ID " +
|
||||||
|
"that does not exist in the document");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
}, "Control whose form attribute is an empty string has no form owner");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
form1.id = "";
|
||||||
|
this.setAttribute("form", "");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
}, "Control whose form attribute is an empty string has no form owner " +
|
||||||
|
"even when form with empty attribute is present");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
form1.id = "FORM1";
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
}, "Control's form attribute must be a case sensitive match for the form's id");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
this.setAttribute("form", "form2");
|
||||||
|
assert_equals(this.form, form2);
|
||||||
|
}, "Setting the form attribute of a control to the id of a non-ancestor form works");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
|
||||||
|
form2.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
this.removeAttribute("form");
|
||||||
|
assert_equals(this.form, form2);
|
||||||
|
}, "Removing form id from a control resets the form owner to ancestor");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
|
||||||
|
form2.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
placeholder.removeChild(form1);
|
||||||
|
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
}, "Removing the form owner of a control with form attribute resets " +
|
||||||
|
"the form owner to null");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
var form3 = document.createElement("form");
|
||||||
|
form3.id = "form3";
|
||||||
|
placeholder.appendChild(form3);
|
||||||
|
form3.appendChild(this);
|
||||||
|
assert_equals(this.form, form3);
|
||||||
|
|
||||||
|
this.setAttribute("form", "form2");
|
||||||
|
assert_equals(this.form, form2);
|
||||||
|
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "Changing form attibute of control resets form owner to correct form");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
var form3 = document.createElement("form");
|
||||||
|
form3.id = "form3";
|
||||||
|
|
||||||
|
placeholder.appendChild(form3);
|
||||||
|
placeholder.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form2.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "Moving a control with form attribute within the document " +
|
||||||
|
"does not change the form owner");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
form1.id = "form-one";
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form2.appendChild(this);
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
|
||||||
|
form1.id = "form1";
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "When the id of a non-ancestor form changes from not being a match for the " +
|
||||||
|
"form attribute to being a match, the control's form owner is reset");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form2.id = "form1";
|
||||||
|
form1.parentNode.insertBefore(form2, form1);
|
||||||
|
assert_equals(this.form, form2);
|
||||||
|
|
||||||
|
form2.parentNode.removeChild(form2);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form1.parentNode.appendChild(form2);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "When form element with same ID as the control's form attribute is inserted " +
|
||||||
|
"earlier in tree order, the form owner is changed to the inserted form");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form2.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
var span = document.createElement("span");
|
||||||
|
span.id = "form1";
|
||||||
|
form1.parentNode.insertBefore(span, form1);
|
||||||
|
assert_equals(this.form, null);
|
||||||
|
|
||||||
|
form1.parentNode.appendChild(span);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "When non-form element with same ID as the control's form attribute is " +
|
||||||
|
"inserted earlier in tree order, the control does not have a form owner");
|
||||||
|
|
||||||
|
testControl(function() {
|
||||||
|
this.setAttribute("form", "form1");
|
||||||
|
form1.appendChild(this);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
|
||||||
|
form1.parentNode.removeChild(form1);
|
||||||
|
assert_equals(this.form, form1);
|
||||||
|
}, "A control that is not in the document but has the form attribute set " +
|
||||||
|
"is associated with the nearest ancestor form if one exists");
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue