Implement form-associated custom elements and their ElementInternals (#31980)

* FACEs work, setFormValue test is awful so now has _mozilla backup

* 1. Impl Validatable in ElementInternals instead of HTMLElement. 2. Reuse the code in Validatable trait. 3. The form associated custom element is not a customized built-in element.

* add some comments

* support readonly attribute and complete barred from constraint validation

* Addressed the code review comments

* Updated the legacy-layout results

* Fixed the WPT failures in ElementInternals-validation.html

* Addressed the code review comments

* Review suggestions

* Fixed silly mistakes and update the test result outside elementinternals

* update the test results

---------

Co-authored-by: Patrick Shaughnessy <pshaughn@comcast.net>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
cathiechen 2024-04-11 15:17:11 +02:00 committed by GitHub
parent 2eb959a159
commit 4e4a4c0a28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 1641 additions and 619 deletions

View file

@ -12615,6 +12615,13 @@
{}
]
],
"ElementInternals-setFormValue.html": [
"d553dd1a90262700323a714359df60906ac057cf",
[
null,
{}
]
],
"Event.html": [
"3947b286122ee47f2f874232763ceeff3c2b661e",
[
@ -13410,7 +13417,7 @@
]
],
"interfaces.html": [
"5461ff50d836f872fdf09defd98575d08fda0061",
"063495d90f464f161c52a8d099a298e914b9b082",
[
null,
{}

View file

@ -0,0 +1,137 @@
<!DOCTYPE html>
<!-- Like custom-elements/form-associated/ElementInternals-setFormValue,
but without any ordering assumptions about iframe loads and promise
microtasks, to test the form submissions despite Servo's racey iframe.
Includes web-platform-tests/wpt#21747 change to initial values. -->
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="container1"></div>
<div id="container2"></div>
<div id="container3"></div>
<div id="container4"></div>
<div id="container5"></div>
<div id="container6"></div>
<script>
class MyControl extends HTMLElement {
static get formAssociated() { return true; }
constructor() {
super();
this.internals_ = this.attachInternals();
this.value_ = '';
}
get value() {
return this.value_;
}
set value(v) {
this.internals_.setFormValue(v);
this.value_ = v;
}
setValues(nameValues) {
const formData = new FormData();
for (let p of nameValues) {
formData.append(p[0], p[1]);
}
this.internals_.setFormValue(formData);
}
}
customElements.define('my-control', MyControl);
const $ = document.querySelector.bind(document);
function submitPromise(t, n) {
return new Promise((resolve, reject) => {
const iframe = $('#container'+n+' iframe');
iframe.onload = () => {
if(iframe.contentWindow.location.href == "about:blank") { return; }
resolve(iframe.contentWindow.location.search);
}
iframe.onerror = () => reject(new Error('iframe onerror fired'));
$('#container'+n+' form').submit();
});
}
promise_test(t => {
$('#container1').innerHTML = '<form action="/common/blank.html" target="if1">' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control></my-control>' +
'</form>' +
'<iframe name="if1"></iframe>';
return submitPromise(t,1).then(query => {
assert_equals(query, '?name-pd1=value-pd1');
});
}, 'Single value - name is missing');
promise_test(t => {
$('#container2').innerHTML = '<form action="/common/blank.html" target="if2">' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control name=""></my-control>' +
'<input name=name-pd2 value="value-pd2">' +
'</form>' +
'<iframe name="if2"></iframe>';
$('#container2 my-control').value = 'value-ce1';
return submitPromise(t,2).then(query => {
assert_equals(query, '?name-pd1=value-pd1&name-pd2=value-pd2');
});
}, 'Single value - empty name exists');
promise_test(t => {
$('#container3').innerHTML = '<form action="/common/blank.html" target="if3" accept-charset=utf-8>' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control name="name-ce1"></my-control>' +
'<my-control name="name-usv"></my-control>' +
'<my-control name="name-file"></my-control>' +
'</form>' +
'<iframe name="if3"></iframe>';
const USV_INPUT = 'abc\uDC00\uD800def';
const USV_OUTPUT = 'abc\uFFFD\uFFFDdef';
const FILE_NAME = 'test_file.txt';
$('#container3 [name=name-usv]').value = USV_INPUT;
$('#container3 [name=name-file]').value = new File(['file content'], FILE_NAME);
return submitPromise(t,3).then(query => {
assert_equals(query, `?name-pd1=value-pd1&name-usv=${encodeURIComponent(USV_OUTPUT)}&name-file=${FILE_NAME}`);
});
}, 'Single value - Non-empty name exists');
promise_test(t => {
$('#container4').innerHTML = '<form action="/common/blank.html" target="if4">' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control name="name-ce1"></my-control>' +
'<my-control name="name-ce2"></my-control>' +
'</form>' +
'<iframe name="if4"></iframe>';
$('#container4 my-control').value = null;
return submitPromise(t,4).then(query => {
assert_equals(query, '?name-pd1=value-pd1');
});
}, 'Null value should submit nothing');
promise_test(t => {
$('#container5').innerHTML = '<form action="/common/blank.html" target="if5">' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control name=name-ce1></my-control>' +
'</form>' +
'<iframe name="if5"></iframe>';
$('#container5 my-control').value = 'value-ce1';
$('#container5 my-control').setValues([]);
$('#container5 my-control').setValues([['sub1', 'subvalue1'],
['sub2', 'subvalue2'],
['sub2', 'subvalue3']]);
return submitPromise(t,5).then(query => {
assert_equals(query, '?name-pd1=value-pd1&sub1=subvalue1&sub2=subvalue2&sub2=subvalue3');
});
}, 'Multiple values - name content attribute is ignored');
promise_test(t => {
$('#container6').innerHTML = '<form action="/common/blank.html" target="if6">' +
'<input name=name-pd1 value="value-pd1">' +
'<my-control name=name-ce1></my-control>' +
'</form>' +
'<iframe name="if6"></iframe>';
$('#container6 my-control').value = 'value-ce1';
$('#container6 my-control').setValues([]);
return submitPromise(t,6).then(query => {
assert_equals(query, '?name-pd1=value-pd1');
});
}, 'setFormValue with an empty FormData should submit nothing');
</script>

View file

@ -77,6 +77,7 @@ test_interfaces([
"DOMStringMap",
"DOMTokenList",
"Element",
"ElementInternals",
"ErrorEvent",
"Event",
"EventSource",