mirror of
https://github.com/servo/servo.git
synced 2025-09-30 00:29:14 +01:00
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:
parent
2eb959a159
commit
4e4a4c0a28
67 changed files with 1641 additions and 619 deletions
|
@ -12615,6 +12615,13 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"ElementInternals-setFormValue.html": [
|
||||
"d553dd1a90262700323a714359df60906ac057cf",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"Event.html": [
|
||||
"3947b286122ee47f2f874232763ceeff3c2b661e",
|
||||
[
|
||||
|
@ -13410,7 +13417,7 @@
|
|||
]
|
||||
],
|
||||
"interfaces.html": [
|
||||
"5461ff50d836f872fdf09defd98575d08fda0061",
|
||||
"063495d90f464f161c52a8d099a298e914b9b082",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
|
|
@ -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>
|
|
@ -77,6 +77,7 @@ test_interfaces([
|
|||
"DOMStringMap",
|
||||
"DOMTokenList",
|
||||
"Element",
|
||||
"ElementInternals",
|
||||
"ErrorEvent",
|
||||
"Event",
|
||||
"EventSource",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue