summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/forms/the-form-element
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/forms/the-form-element')
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html36
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html56
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html130
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html192
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html46
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html43
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html26
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html38
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html418
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html215
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html18
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html5
24 files changed, 1579 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html
new file mode 100644
index 0000000000..8a3543fcbc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-in-inactive-document-crash.html
@@ -0,0 +1,6 @@
+<iframe id="i"></iframe>
+<script>
+var form = i.contentDocument.createElement("form");
+i.remove();
+form.action = "GET";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html
new file mode 100644
index 0000000000..67828a3077
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection-with-base-url.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.action with a base URL</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<base href="/common/blank.html">
+
+<form id="form1" action="a.html"></form>
+<form id="form2" action=""></form>
+<form id="form3"></form>
+
+<script>
+"use strict";
+
+test(() => {
+
+ assert_equals(document.querySelector("#form1").action, (new URL("a.html", document.baseURI)).href,
+ "action should equal the correct absolute URL");
+
+}, "An action URL should be resolved relative to the document's base URL (not the document's URL)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form2").action, document.URL);
+
+}, "An empty-string action content attribute should cause the IDL attribute to return the document's URL (not the document's base URL)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form3").action, document.URL);
+
+}, "A missing action content attribute should cause the IDL attribute to return the document's URL (not the document's base URL)");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html
new file mode 100644
index 0000000000..c92fd0f0cf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-reflection.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.action</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form1" action="a.html"></form>
+<form id="form2" action=""></form>
+<form id="form3"></form>
+
+<script>
+"use strict";
+
+test(() => {
+
+ assert_equals(document.querySelector("#form1").action, (new URL("a.html", document.baseURI)).href,
+ "action should equal the correct absolute URL");
+
+}, "An action URL should be resolved relative to the document's base URL (= the document's URL in this case)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form2").action, document.URL);
+
+}, "An empty-string action content attribute should cause the IDL attribute to return the document's URL (= the document's base URL in this case)");
+
+test(() => {
+
+ assert_equals(document.querySelector("#form3").action, document.URL);
+
+}, "A missing action content attribute should cause the IDL attribute to return the document's URL (= the document's base URL in this case)");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html
new file mode 100644
index 0000000000..baee5500de
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission-with-base-url.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form action="" attribute effect on submission</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+// promise_test instead of async_test because all tests use window.success, and so can't run at the same time.
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/target/form-action-url-target.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action-and-base.sub.html?action=form-action-url-target.html";
+ document.body.appendChild(iframe);
+ });
+}, "An action URL should be resolved relative to the document's base URL (not document URL)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-with-action-and-base.sub.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action-and-base.sub.html?action=";
+ document.body.appendChild(iframe);
+ });
+}, "An empty-string action should submit the form to its containing document's URL (not its base URL)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-no-action-with-base.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-no-action-with-base.html";
+ document.body.appendChild(iframe);
+ });
+}, "A missing action should submit the form to its containing document's URL (not its base URL)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html
new file mode 100644
index 0000000000..54ca7b5ff5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action-submission.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form action="" attribute effect on submission</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-fs-action">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+// promise_test instead of async_test because all tests use window.success, and so can't run at the same time.
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/target/form-action-url-target.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action.sub.html?action=target/form-action-url-target.html";
+ document.body.appendChild(iframe);
+ });
+}, "An action URL should be resolved relative to the document's base URL (= document's URL in this case)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-with-action.sub.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-with-action.sub.html?action=";
+ document.body.appendChild(iframe);
+ });
+}, "An empty-string action should submit the form to the document's URL (= document's base URL in this case)");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ window.success = t.step_func(locationLoaded => {
+ const expected = (new URL("resources/form-no-action.html?name=value", location.href)).href;
+ assert_equals(locationLoaded, expected);
+ resolve();
+ });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/form-no-action.html";
+ document.body.appendChild(iframe);
+ });
+}, "A missing action should submit the form to the document's URL (= document's base URL in this case)");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html
new file mode 100644
index 0000000000..14717c5e6a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-action.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_action</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action="http://www.google.com/"
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+
+ if (typeof(form.action) == "string") {
+ test(function() {
+ assert_equals(form.action, "http://www.google.com/", "action attribute is not correct.");
+ });
+ } else {
+ test(function() {
+ assert_unreached("action attribute is not exist.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html
new file mode 100644
index 0000000000..fcd93ce2ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-autocomplete.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>form autocomplete attribute</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#the-form-element">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#attr-fe-autocomplete">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<form name="missing_attribute">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_on" autocomplete="on">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_off" autocomplete="off">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<form name="autocomplete_invalid" autocomplete="foobar">
+ <input>
+ <input autocomplete="on">
+ <input autocomplete="off">
+ <input autocomplete="foobar">
+</form>
+<script>
+ function autocompletetest(form, expectedValues, desc) {
+ test(function(){
+ assert_equals(form.autocomplete, expectedValues[0]);
+ assert_equals(form.elements[0].autocomplete, expectedValues[1]);
+ assert_equals(form.elements[1].autocomplete, expectedValues[2]);
+ assert_equals(form.elements[2].autocomplete, expectedValues[3]);
+ assert_equals(form.elements[3].autocomplete, expectedValues[4]);
+ }, desc);
+ }
+
+ autocompletetest(document.forms.missing_attribute, ["on", "", "on", "off", ""], "form autocomplete attribute missing");
+ autocompletetest(document.forms.autocomplete_on, ["on", "", "on", "off", ""], "form autocomplete attribute on");
+ autocompletetest(document.forms.autocomplete_off, ["off", "", "on", "off", ""], "form autocomplete attribute off");
+ autocompletetest(document.forms.autocomplete_invalid, ["on", "", "on", "off", ""], "form autocomplete attribute invalid");
+
+ var keywords = [ "on", "off", "name", "honorific-prefix", "given-name", "additional-name", "family-name", "honorific-suffix", "nickname", "username", "new-password", "current-password", "one-time-code", "organization-title", "organization", "street-address", "address-line1", "address-line2", "address-line3", "address-level4", "address-level3", "address-level2", "address-level1", "country", "country-name", "postal-code", "cc-name", "cc-given-name", "cc-additional-name", "cc-family-name", "cc-number", "cc-exp", "cc-exp-month", "cc-exp-year", "cc-csc", "cc-type", "transaction-currency", "transaction-amount", "language", "bday", "bday-day", "bday-month", "bday-year", "sex", "url", "photo", "tel", "tel-country-code", "tel-national", "tel-area-code", "tel-local", "tel-local-prefix", "tel-local-suffix", "tel-extension", "email", "impp", "webauthn" ];
+
+ keywords.forEach(function(keyword) {
+ test(function(){
+ var input = document.createElement("input");
+ // Include whitespace to test splitting tokens on whitespace.
+ // Convert to uppercase to ensure that the tokens are normalized to lowercase.
+ input.setAttribute("autocomplete", " " + keyword.toUpperCase() + "\t");
+ assert_equals(input.autocomplete, keyword);
+ }, keyword + " is an allowed autocomplete field name");
+ });
+
+
+test(() => {
+ const select = document.createElement("select");
+ select.setAttribute("autocomplete", " \n");
+ assert_equals(select.autocomplete, "");
+}, "Test whitespace-only attribute value");
+
+test(() => {
+ const select = document.createElement("select");
+
+ select.setAttribute("autocomplete", "foo off");
+ assert_equals(select.autocomplete, "");
+
+ // Normal category; max=3
+ select.setAttribute("autocomplete", "foo section-foo billing name");
+ assert_equals(select.autocomplete, "");
+
+ // Contact category; max=4
+ select.setAttribute("autocomplete", "foo section-bar billing work tel");
+ assert_equals(select.autocomplete, "");
+
+ // Credential category; max=5
+ select.setAttribute("autocomplete", "foo section-bar billing work tel webauthn");
+ assert_equals(select.autocomplete, "");
+}, "Test maximum number of tokens");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", "call-sign");
+ assert_equals(textarea.autocomplete, "");
+}, "Unknown field");
+
+test(() => {
+ const hidden = document.createElement("input");
+ hidden.type = "hidden";
+ hidden.setAttribute("autocomplete", "on");
+ assert_equals(hidden.autocomplete, "");
+ hidden.setAttribute("autocomplete", "off");
+ assert_equals(hidden.autocomplete, "");
+}, "Test 'wearing the autofill anchor mantle' with off/on");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", " HOME\ntel");
+ assert_equals(textarea.autocomplete, "home tel");
+
+ textarea.setAttribute("autocomplete", "shipping country");
+ assert_equals(textarea.autocomplete, "shipping country");
+
+ textarea.setAttribute("autocomplete", "billing work email");
+ assert_equals(textarea.autocomplete, "billing work email");
+
+ textarea.setAttribute("autocomplete", " section-FOO bday");
+ assert_equals(textarea.autocomplete, "section-foo bday");
+}, "Serialize combinations of section, mode, contact, and field");
+
+test(() => {
+ const textarea = document.createElement("textarea");
+
+ textarea.setAttribute("autocomplete", "\tusername webauthn");
+ assert_equals(textarea.autocomplete, "username webauthn");
+
+ textarea.setAttribute("autocomplete", " section-LOGIN shipping work tel webauthn ");
+ assert_equals(textarea.autocomplete, "section-login shipping work tel webauthn");
+}, "Serialize combinations of section, mode, contact, field, and credential");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html
new file mode 100644
index 0000000000..941ab94d45
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-checkvalidity.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_checkValidity</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+
+ try
+ {
+ var ret = form.checkValidity();
+
+ test(function() {
+ assert_equals(ret, true, "calling of checkValidity method is failed.");
+ });
+ }
+ catch (e) {
+ test(function() {
+ assert_unreached("Error is raised.");
+ });
+ }
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html
new file mode 100644
index 0000000000..693560188a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-filter.html
@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>form.elements must contain all listed elements with the form owner</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<!--
+ Elements with data-in are expected to be in the form.elements collection.
+ The choice of other elements besides "listed elements" (i.e. img, label, meter, progress) was
+ because those are ones that appear in form-associated or labelable element categories.
+-->
+
+<button data-in form="form" id="before-button1"></button>
+<fieldset data-in form="form" id="before-fieldset1"></fieldset>
+<object data-in form="form" id="before-object1"></object>
+<output data-in form="form" id="before-output1"></output>
+<select data-in form="form" id="before-select1">
+ <option form="form" id="before-option1">x</option>
+</select>
+<textarea data-in form="form" id="before-textarea1"></textarea>
+
+<input data-in form="form" id="before-input1">
+<input data-in type="hidden" form="form" id="before-input2">
+<input data-in type="search" form="form" id="before-input3">
+<input data-in type="tel" form="form" id="before-input4">
+<input data-in type="url" form="form" id="before-input5">
+<input data-in type="email" form="form" id="before-input6">
+<input data-in type="password" form="form" id="before-input7">
+<input data-in type="date" form="form" id="before-input8">
+<input data-in type="month" form="form" id="before-input9">
+<input data-in type="week" form="form" id="before-input10">
+<input data-in type="time" form="form" id="before-input11">
+<input data-in type="datetime-local" form="form" id="before-input12">
+<input data-in type="number" form="form" id="before-input13">
+<input data-in type="range" form="form" id="before-input14">
+<input data-in type="color" form="form" id="before-input15">
+<input data-in type="checkbox" form="form" id="before-input16">
+<input data-in type="radio" form="form" id="before-input17">
+<input data-in type="file" form="form" id="before-input18">
+<input data-in type="submit" form="form" id="before-input19">
+<input data-in type="reset" form="form" id="before-input20">
+<input data-in type="button" form="form" id="before-input21">
+
+<img form="form" id="before-img1">
+<label form="form" id="before-label1"></label>
+<meter form="form" id="before-meter1"></meter>
+<progress form="form" id="before-progress1"></progress>
+
+<form id="form">
+ <button data-in id="button1"></button>
+ <fieldset data-in id="fieldset1"></fieldset>
+ <object data-in id="object1"></object>
+ <output data-in id="output1"></output>
+ <select data-in id="select1">
+ <option id="option1">x</option>
+ </select>
+ <textarea data-in id="textarea1"></textarea>
+
+ <input data-in id="input1">
+ <input data-in type="hidden" id="input2">
+ <input data-in type="search" id="input3">
+ <input data-in type="tel" id="input4">
+ <input data-in type="url" id="input5">
+ <input data-in type="email" id="input6">
+ <input data-in type="password" id="input7">
+ <input data-in type="date" id="input8">
+ <input data-in type="month" id="input9">
+ <input data-in type="week" id="input10">
+ <input data-in type="time" id="input11">
+ <input data-in type="datetime-local" id="input12">
+ <input data-in type="number" id="input13">
+ <input data-in type="range" id="input14">
+ <input data-in type="color" id="input15">
+ <input data-in type="checkbox" id="input16">
+ <input data-in type="radio" id="input17">
+ <input data-in type="file" id="input18">
+ <input data-in type="submit" id="input19">
+ <input data-in type="reset" id="input20">
+ <input data-in type="button" id="input21">
+
+ <img id="img1">
+ <label id="label1"></label>
+ <meter id="meter1"></meter>
+ <progress id="progress1"></progress>
+</form>
+
+<button data-in form="form" id="after-button1"></button>
+<fieldset data-in form="form" id="after-fieldset1"></fieldset>
+<object data-in form="form" id="after-object1"></object>
+<output data-in form="form" id="after-output1"></output>
+<select data-in form="form" id="after-select1">
+ <option form="form" id="after-option1">x</option>
+</select>
+<textarea data-in form="form" id="after-textarea1"></textarea>
+
+<input data-in form="form" id="after-input1">
+<input data-in type="hidden" form="form" id="after-input2">
+<input data-in type="search" form="form" id="after-input3">
+<input data-in type="tel" form="form" id="after-input4">
+<input data-in type="url" form="form" id="after-input5">
+<input data-in type="email" form="form" id="after-input6">
+<input data-in type="password" form="form" id="after-input7">
+<input data-in type="date" form="form" id="after-input8">
+<input data-in type="month" form="form" id="after-input9">
+<input data-in type="week" form="form" id="after-input10">
+<input data-in type="time" form="form" id="after-input11">
+<input data-in type="datetime-local" form="form" id="after-input12">
+<input data-in type="number" form="form" id="after-input13">
+<input data-in type="range" form="form" id="after-input14">
+<input data-in type="color" form="form" id="after-input15">
+<input data-in type="checkbox" form="form" id="after-input16">
+<input data-in type="radio" form="form" id="after-input17">
+<input data-in type="file" form="form" id="after-input18">
+<input data-in type="submit" form="form" id="after-input19">
+<input data-in type="reset" form="form" id="after-input20">
+<input data-in type="button" form="form" id="after-input21">
+
+<img form="form" id="after-img1">
+<label form="form" id="after-label1"></label>
+<meter form="form" id="after-meter1"></meter>
+<progress form="form" id="after-progress1"></progress>
+
+<button id="after-unassociated-button1"></button>
+<fieldset id="after-unassociated-fieldset1"></fieldset>
+<object id="after-unassociated-object1"></object>
+<output id="after-unassociated-output1"></output>
+<select id="after-unassociated-select1">
+ <option id="after-unassociated-option1">x</option>
+</select>
+<textarea id="after-unassociated-textarea1"></textarea>
+
+<input id="after-unassociated-input1">
+<input type="hidden" id="after-unassociated-input2">
+<input type="search" id="after-unassociated-input3">
+<input type="tel" id="after-unassociated-input4">
+<input type="url" id="after-unassociated-input5">
+<input type="email" id="after-unassociated-input6">
+<input type="password" id="after-unassociated-input7">
+<input type="date" id="after-unassociated-input8">
+<input type="month" id="after-unassociated-input9">
+<input type="week" id="after-unassociated-input10">
+<input type="time" id="after-unassociated-input11">
+<input type="datetime-local" id="after-unassociated-input12">
+<input type="number" id="after-unassociated-input13">
+<input type="range" id="after-unassociated-input14">
+<input type="color" id="after-unassociated-input15">
+<input type="checkbox" id="after-unassociated-input16">
+<input type="radio" id="after-unassociated-input17">
+<input type="file" id="after-unassociated-input18">
+<input type="submit" id="after-unassociated-input19">
+<input type="reset" id="after-unassociated-input20">
+<input type="button" id="after-unassociated-input21">
+
+<img id="after-unassociated-img1">
+<label id="after-unassociated-label1"></label>
+<meter id="after-unassociated-meter1"></meter>
+<progress id="after-unassociated-progress1"></progress>
+
+<form id="form2">
+ <span id="shadow-1"></span>
+</form>
+<span id="shadow-2"></span>
+
+<script>
+"use strict";
+test(() => {
+ const elements = document.querySelector("#form").elements;
+ const ids = Array.from(elements).map(el => el.id);
+
+ const allCorrectIDs = Array.from(document.querySelectorAll("[data-in]")).map(el => el.id);
+
+ assert_array_equals(ids, allCorrectIDs);
+});
+
+test(() => {
+ const shadowRoot1 = document.querySelector("#shadow-1").attachShadow({mode: "open"});
+ const input1 = document.createElement("input");
+ shadowRoot1.appendChild(input1);
+
+ const shadowRoot2 = document.querySelector("#shadow-2").attachShadow({mode: "open"});
+ const input2 = document.createElement("input");
+ input2.setAttribute("form", "form2");
+ shadowRoot2.appendChild(input2);
+
+ assert_equals(document.querySelector("#form2").elements.length, 0);
+ assert_equals(input1.form, null);
+ assert_equals(input2.form, null);
+}, "form.elements only includes elements from the same shadow tree");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html
new file mode 100644
index 0000000000..c8b4a6c71e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-interfaces-01.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>form.elements: interfaces</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var form = document.createElement("form");
+ ["HTMLFormControlsCollection", "HTMLCollection"].forEach(function(i) {
+ test(function() {
+ assert_true(i in window, "Interface should exist")
+ assert_true(form.elements instanceof window[i],
+ "elements should implement the interface")
+ }, "Testing interface " + i)
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html
new file mode 100644
index 0000000000..7921627265
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-matches.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>form.elements: matches</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id="form">
+<input type="image">
+</form>
+</div>
+<script>
+test(function() {
+ assert_equals(document.getElementById("form").elements.length, 0);
+}, "input type=image should not be present in the form.elements collection")
+test(function() {
+ var form = document.getElementById("form");
+ var i = document.createElement("input");
+ i.name = "2";
+ form.appendChild(i);
+ var j = document.createElement("input");
+ j.name = "03";
+ form.appendChild(j);
+ assert_equals(form.elements[-1], undefined, '[-1]');
+ assert_equals(form.elements["-1"], undefined, '["-1"]');
+ assert_equals(form.elements[0], i, '[0]');
+ assert_equals(form.elements["0"], i, '["0"]');
+ assert_equals(form.elements[1], j, '[1]');
+ assert_equals(form.elements["1"], j, '["1"]');
+ assert_equals(form.elements[2], undefined, '[2]');
+ assert_equals(form.elements["2"], undefined, '["2"]');
+ assert_equals(form.elements[03], undefined, '[03]');
+ assert_equals(form.elements["03"], j, '["03"]');
+ assert_equals(form.elements.item(-1), null, 'item(-1)');
+ assert_equals(form.elements.item(0), i, 'item(0)');
+ assert_equals(form.elements.item(1), j, 'item(1)');
+ assert_equals(form.elements.item(2), null, 'item(2)');
+ assert_equals(form.elements.namedItem("2"), i, 'namedItem("2")');
+ assert_equals(form.elements.namedItem("03"), j, 'namedItem("03")');
+ assert_equals(form.elements.namedItem("3"), null, 'namedItem("3")');
+ assert_array_equals(form.elements, [i, j]);
+ form.removeChild(i);
+ form.removeChild(j);
+}, "form.elements should include elements whose name starts with a number");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html
new file mode 100644
index 0000000000..0b5aeb8ef5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-01.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>form.elements: namedItem</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<input name=b id=i1>
+<input name=b id=i2>
+</form>
+</div>
+<script>
+test(function() {
+ assert_true("RadioNodeList" in window, "RadioNodeList should exist");
+}, "RadioNodeList should exist")
+test(function() {
+ var nl = document.forms.form.elements["b"];
+ assert_true(nl instanceof NodeList, "Should get a NodeList");
+ if ("RadioNodeList" in window) {
+ assert_true(nl instanceof RadioNodeList, "Should get a RadioNodeList");
+ }
+ assert_array_equals(nl,
+ [document.getElementById("i1"),
+ document.getElementById("i2")]);
+
+ var el = nl[0];
+ el.parentNode.removeChild(el);
+ assert_true(nl instanceof NodeList, "Should get a NodeList");
+ if ("RadioNodeList" in window) {
+ assert_true(nl instanceof RadioNodeList, "Should get a RadioNodeList");
+ }
+ assert_array_equals(nl, [document.getElementById("i2")]);
+ assert_equals(document.forms.form.elements["b"], document.getElementById("i2"));
+}, "elements collection should return elements or RadioNodeLists")
+test(function() {
+ var fs = document.forms.form.appendChild(document.createElement("fieldset"));
+ fs.name = "fs";
+ assert_equals(document.forms.form.elements.fs, fs);
+ fs.parentNode.removeChild(fs);
+}, "elements collection should include fieldsets")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html
new file mode 100644
index 0000000000..c25e554de1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-nameditem-02.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>form.elements: parsing</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-form-elements">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#parsing-main-intr">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<table>
+<tr>
+<td><input type="radio" name="radio1" id="r1" value=1></td>
+<td><input type="radio" name="radio2" id="r2" value=2></td>
+<input type="radio" name="radio0" id="r0" value=0>
+</tr>
+</table>
+</form>
+</div>
+<script>
+test(function() {
+ var form = document.getElementById("form");
+ assert_array_equals(form.elements,
+ [document.getElementById("r0"),
+ document.getElementById("r1"),
+ document.getElementById("r2")]);
+}, "form.elements should work correctly in the face of table syntax errors")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html
new file mode 100644
index 0000000000..1c1aa5d3dd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-elements-sameobject.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Testing [SameObject] on the 'elements' attribute on the 'form' element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+
+<form>
+ <input>
+</form>
+
+<script>
+test(function() {
+ var form = document.querySelector('form');
+ var elements = form.elements;
+ assert_equals(elements, form.elements);
+ form.appendChild(document.createElement('input'));
+ assert_equals(elements, form.elements);
+}, "[SameObject] should apply to 'elements' attr on <form>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html
new file mode 100644
index 0000000000..a108ce8a93
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element-shadow.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>form.elements: indexed access reflects DOM order, not flat tree</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<form id="target">
+ <div id="host">
+ <template shadowrootmode="open">
+ <slot name="first"></slot>
+ <slot name="second"></slot>
+ </template>
+ <input id="first" slot="second">
+ <input id="second" slot="first">
+ </div>
+</form>
+<script>
+test(function() {
+ let target = document.getElementById("target");
+ let host = document.getElementById("host");
+ assert_true(!!host.shadowRoot, "Should have a shadow tree");
+ assert_equals(target.elements[0], first, "form.elements reflects DOM order, not flat tree order");
+ assert_equals(target.elements[1], second);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html
new file mode 100644
index 0000000000..5ea96d3d1b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-indexed-element.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>form.elements: indexed</title>
+<link rel="author" title="Ivan.Yang" href="mailto:jsyangwenjie@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test">
+<form id=form>
+<input type="radio" name="radio1" id="r1" value=1>
+<input type="radio" name="radio2" id="r2" value=2>
+</form>
+</div>
+<script>
+test(function() {
+ var form = document.getElementById("form");
+ assert_equals(form[0], document.getElementById("r1"));
+ assert_equals(form[1], document.getElementById("r2"));
+ assert_equals(form[2], undefined);
+ assert_equals(form[-1], undefined);
+}, "form.elements should be accessed correctly by index")
+
+test(function(){
+ var form = document.getElementById("form");
+ var old_item = form[0];
+ var old_desc = Object.getOwnPropertyDescriptor(form, 0);
+ assert_equals(old_desc.value, old_item);
+ assert_true(old_desc.enumerable);
+ assert_true(old_desc.configurable);
+ assert_false(old_desc.writable);
+
+ Object.prototype[0] = 5;
+ this.add_cleanup(function () { delete Object.prototype[0]; });
+ assert_equals(form[0], old_item);
+
+ delete form[0];
+ assert_equals(form[0], old_item);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form[0];
+ });
+ assert_equals(form[0], old_item);
+}, 'Trying to delete an indexed property name should never work');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html
new file mode 100644
index 0000000000..3326809fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-length.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Forms</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <p>
+ <h3>Form_length</h3>
+ </p>
+
+ <hr>
+
+ <div id="log"></div>
+
+ <form method="post"
+ enctype="application/x-www-form-urlencoded"
+ action=""
+ id="input_form">
+ <p><input type=hidden name="custname"></p>
+ <p><input type=hidden name="custtel"></p>
+ <p><input type=hidden name="custemail"></p>
+
+ </form>
+ <script>
+
+ var form = document.getElementById("input_form");
+ var len = form.length;
+
+ test(function() {
+ assert_equals(len, 3, "length attribute is not correct.");
+ });
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html
new file mode 100644
index 0000000000..7b7d573615
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-nameditem.html
@@ -0,0 +1,418 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Form named getter</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<!-- XXX Nothing tests id attributes yet. -->
+<!-- XXX We also need tests for moving inputs and forms in the DOM. -->
+<form>
+<input type=button name=button>
+<input type=radio name=radio value=x>
+<input type=radio name=radio value=y>
+<input type=radio name=radio value=z>
+</form>
+
+<form>
+<button name=l1></button>
+<fieldset name=l2></fieldset>
+<input type=hidden name=l3>
+<input type=text name=l4>
+<input type=search name=l5>
+<input type=tel name=l6>
+<input type=url name=l7>
+<input type=email name=l8>
+<input type=password name=l9>
+<input type=datetime name=l10>
+<input type=date name=l11>
+<input type=month name=l12>
+<input type=week name=l13>
+<input type=time name=l14>
+<input type=datetime-local name=l15>
+<input type=number name=l16>
+<input type=range name=l17>
+<input type=color name=l18>
+<input type=checkbox name=l19>
+<input type=radio name=l20>
+<input type=file name=l21>
+<input type=submit name=l22>
+<input type=image name=l23>
+<input type=reset name=l24>
+<input type=button name=l25>
+<input type=foo name=l26>
+<input name=l27>
+<object name=l28></object>
+<output name=l29></output>
+<select name=l30></select>
+<textarea name=l31></textarea>
+</form>
+
+<form>
+<!-- EventTarget -->
+<input type=radio name=addEventListener>
+<input type=radio name=removeEventListener>
+<input type=radio name=dispatchEvent>
+
+<!-- Node -->
+<input type=radio name=nodeType>
+<input type=radio name=nodeName>
+<input type=radio name=ownerDocument>
+
+<!-- Element -->
+<input type=radio name=namespaceURI>
+<input type=radio name=prefix>
+<input type=radio name=localName>
+
+<!-- HTMLElement -->
+<input type=radio name=title>
+<input type=radio name=lang>
+<input type=radio name=dir>
+
+<!-- HTMLFormElement -->
+<input type=radio name=acceptCharset>
+<input type=radio name=action>
+<input type=radio name=autocomplete>
+<input type=radio name=enctype>
+<input type=radio name=encoding>
+<input type=radio name=method>
+<input type=radio name=name>
+<input type=radio name=noValidate>
+<input type=radio name=target>
+<input type=radio name=elements>
+<input type=radio name=length>
+<input type=radio name=submit>
+<input type=radio name=reset>
+<input type=radio name=checkValidity>
+</form>
+
+<img name=x>
+<form></form><!-- no child nodes -->
+<img name=y>
+<form><!-- a child node --></form>
+<img name=z>
+
+<input form=a name=b>
+<form id=a></form>
+<input form=c name=d>
+<input form=c name=d>
+<form id=c></form>
+<script>
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.item, undefined)
+ assert_false("item" in form)
+}, "Forms should not have an item method")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.namedItem, undefined)
+ assert_false("namedItem" in form)
+}, "Forms should not have a namedItem method")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ var button = document.getElementsByTagName("input")[0]
+ assert_equals(button.type, "button")
+ assert_equals(form.button, button)
+ var desc = Object.getOwnPropertyDescriptor(form, "button");
+ assert_equals(desc.value, button);
+ assert_false(desc.writable);
+ assert_true(desc.configurable);
+ assert_false(desc.enumerable);
+ assert_equals(form.button.length, undefined)
+}, "Name for a single element should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.radio.item(-1), null)
+ assert_array_equals([0, 1, 2].map(function(i) {
+ return form.radio.item(i).value
+ }), ["x", "y", "z"])
+ assert_equals(form.radio.item(3), null)
+}, "Calling item() on the NodeList returned from the named getter should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ assert_equals(form.radio.length, 3)
+ assert_equals(form.radio[-1], undefined)
+ assert_array_equals([0, 1, 2].map(function(i) {
+ return form.radio[i].value
+ }), ["x", "y", "z"])
+ assert_equals(form.radio[3], undefined)
+}, "Indexed getter on the NodeList returned from the named getter should work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[0]
+ var indices = [-1, 0, 1, 2, 3]
+ indices.forEach(function(i) {
+ assert_throws_js(TypeError, function() {
+ form.radio(i)
+ })
+ })
+}, "Invoking a legacycaller on the NodeList returned from the named getter " +
+ "should not work")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1]
+ for (var i = 1; i <= 31; ++i) {
+ if (i == 23) {
+ // input type=image
+ assert_equals(form["l" + i], undefined)
+ } else {
+ assert_equals(form["l" + i], form.children[i - 1])
+ }
+ }
+}, "All listed elements except input type=image should be present in the form")
+
+test(function() {
+ var names = [
+ // EventTarget
+ "addEventListener", "removeEventListener", "dispatchEvent",
+ // Node
+ "nodeType", "nodeName", "ownerDocument",
+ // Element
+ "namespaceURI", "prefix", "localName",
+ // HTMLElement
+ "title", "lang", "dir",
+ // HTMLFormElement
+ "acceptCharset", "action", "autocomplete", "enctype", "encoding", "method",
+ "name", "noValidate", "target", "elements", "length", "submit", "reset",
+ "checkValidity"
+ ]
+ var form = document.getElementsByTagName("form")[2]
+ names.forEach(function(name, i) {
+ assert_equals(form[name], form.children[i])
+ })
+}, "Named elements should override builtins")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[3]
+ assert_equals(form.x, undefined, "x should not be associated with the form")
+ assert_equals(form.y, undefined, "y should not be associated with the form")
+ assert_equals(form.z, undefined, "z should not be associated with the form")
+ assert_equals(form[0], undefined, "The form should not have supported property indices")
+ assert_equals(form.length, 0)
+}, "Named items outside the form should not be returned (no children)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[4]
+ assert_equals(form.x, undefined, "x should not be associated with the form")
+ assert_equals(form.y, undefined, "y should not be associated with the form")
+ assert_equals(form.z, undefined, "z should not be associated with the form")
+ assert_equals(form[0], undefined, "The form should not have supported property indices")
+ assert_equals(form.length, 0)
+}, "Named items outside the form should not be returned (one child)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[5]
+ assert_equals(form.id, "a")
+
+ var input = document.getElementsByName("b")[0]
+ assert_equals(input.localName, "input")
+ assert_equals(input.getAttribute("form"), "a")
+
+ assert_equals(form.b, input);
+}, "The form attribute should be taken into account for named getters (single element)")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[6]
+ assert_equals(form.id, "c")
+
+ var input1 = document.getElementsByName("d")[0]
+ assert_equals(input1.localName, "input")
+ assert_equals(input1.getAttribute("form"), "c")
+
+ var input2 = document.getElementsByName("d")[1]
+ assert_equals(input2.localName, "input")
+ assert_equals(input2.getAttribute("form"), "c")
+
+ var desc = Object.getOwnPropertyDescriptor(form, "d");
+ assert_equals(desc.value, form.d);
+ assert_false(desc.writable);
+ assert_true(desc.configurable);
+ assert_false(desc.enumerable);
+
+ assert_true(form.d instanceof NodeList, "form.d should be a NodeList")
+ assert_array_equals(form.d, [input1, input2])
+}, "The form attribute should be taken into account for named getters (multiple elements)")
+
+test(function() {
+ var f = document.body.appendChild(document.createElement("form"))
+ f.id = "f"
+ var g = f.appendChild(document.createElement("form"))
+ g.id = "g"
+ var input = g.appendChild(document.createElement("input"))
+ input.name = "x"
+ assert_equals(f.x, undefined)
+ assert_equals(g.x, input)
+}, "Input should only be a named property on the innermost form that contains it")
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["l1"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "l1");
+ assert_equals(old_desc.value, old_item);
+ assert_false(old_desc.enumerable);
+ assert_true(old_desc.configurable);
+ assert_false(old_desc.writable);
+
+ form["l1"] = 5;
+ assert_equals(form["l1"], old_item);
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ form["l1"] = 5;
+ });
+ assert_throws_js(TypeError, function() {
+ Object.defineProperty(form, "l1", { value: 5 });
+ });
+
+ delete form["l1"];
+ assert_equals(form["l1"], old_item);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form["l1"];
+ });
+ assert_equals(form["l1"], old_item);
+
+}, 'Trying to set an expando that would shadow an already-existing named property');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["new-name"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "new-name");
+ assert_equals(old_item, undefined);
+ assert_equals(old_desc, undefined);
+
+ form["new-name"] = 5;
+ assert_equals(form["new-name"], 5);
+
+ var input = document.createElement("input");
+ this.add_cleanup(function () {input.remove();});
+ input.name = "new-name";
+ form.appendChild(input);
+
+ assert_equals(form["new-name"], 5);
+
+ delete form["new-name"];
+ assert_equals(form["new-name"], input);
+}, 'Trying to set an expando that shadows a named property that gets added later');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+ var old_item = form["new-name2"];
+ var old_desc = Object.getOwnPropertyDescriptor(form, "new-name2");
+ assert_equals(old_item, undefined);
+ assert_equals(old_desc, undefined);
+
+ Object.defineProperty(form, "new-name2", { configurable: false, writable:
+ false, value: 5 });
+ assert_equals(form["new-name2"], 5);
+
+ var input = document.createElement("input");
+ this.add_cleanup(function () {input.remove();});
+ input.name = "new-name2";
+ form.appendChild(input);
+
+ assert_equals(form["new-name2"], 5);
+
+ delete form["new-name2"];
+ assert_equals(form["new-name2"], 5);
+
+ assert_throws_js(TypeError, function() {
+ "use strict";
+ delete form["new-name2"];
+ });
+ assert_equals(form["new-name2"], 5);
+}, 'Trying to set a non-configurable expando that shadows a named property that gets added later');
+
+test(function() {
+ var form = document.getElementsByTagName("form")[1];
+
+ var i1 = document.createElement("input");
+ i1.name = "past-name1";
+ i1.id = "past-id1"
+
+ assert_equals(form["past-name1"], undefined);
+ assert_equals(form["past-id1"], undefined);
+ form.appendChild(i1);
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["past-id1"], i1);
+
+ i1.name = "twiddled-name1";
+ i1.id = "twiddled-id1";
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+
+ i1.name = "twiddled-name2";
+ i1.id = "twiddled-id2";
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["twiddled-name2"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+ assert_equals(form["twiddled-id2"], i1);
+
+ i1.removeAttribute("id");
+ i1.removeAttribute("name");
+ assert_equals(form["past-name1"], i1);
+ assert_equals(form["twiddled-name1"], i1);
+ assert_equals(form["twiddled-name2"], i1);
+ assert_equals(form["past-id1"], i1);
+ assert_equals(form["twiddled-id1"], i1);
+ assert_equals(form["twiddled-id2"], i1);
+
+ i1.remove();
+ assert_equals(form["past-name1"], undefined);
+ assert_equals(form["twiddled-name1"], undefined);
+ assert_equals(form["twiddled-name2"], undefined);
+ assert_equals(form["past-id1"], undefined);
+ assert_equals(form["twiddled-id1"], undefined);
+ assert_equals(form["twiddled-id2"], undefined);
+
+ var i2 = document.createElement("input");
+ i2.name = "past-name2";
+ i2.id = "past-id2";
+
+ assert_equals(form["past-name2"], undefined);
+ assert_equals(form["past-id2"], undefined);
+ form.appendChild(i2);
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["past-id2"], i2);
+
+ i2.name = "twiddled-name3";
+ i2.id = "twiddled-id3";
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+
+ i2.name = "twiddled-name4";
+ i2.id = "twiddled-id4";
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["twiddled-name4"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+ assert_equals(form["twiddled-id4"], i2);
+
+ i2.removeAttribute("id");
+ i2.removeAttribute("name");
+ assert_equals(form["past-name2"], i2);
+ assert_equals(form["twiddled-name3"], i2);
+ assert_equals(form["twiddled-name4"], i2);
+ assert_equals(form["past-id2"], i2);
+ assert_equals(form["twiddled-id3"], i2);
+ assert_equals(form["twiddled-id4"], i2);
+
+ i2.setAttribute("form", "c");
+ assert_equals(form["past-name2"], undefined);
+ assert_equals(form["twiddled-name3"], undefined);
+ assert_equals(form["twiddled-name4"], undefined);
+ assert_equals(form["past-id2"], undefined);
+ assert_equals(form["twiddled-id3"], undefined);
+ assert_equals(form["twiddled-id4"], undefined);
+}, "Past names map should work correctly");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html
new file mode 100644
index 0000000000..cbc46cc7d3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/form-requestsubmit.html
@@ -0,0 +1,215 @@
+<!DOCTYPE html>
+<title>form.requestSubmit() tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe name="iframe" src="about:blank"></iframe>
+
+<script>
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', '<form>' +
+ '<input type="reset">' +
+ '<input type="text">' +
+ '<button type="reset"></button>' +
+ '<button type="button"></button>' +
+ '</form>');
+ let form = document.querySelector('form');
+ assert_throws_js(TypeError, () => {
+ form.requestSubmit(document.body);
+ });
+ for (let control of form.elements) {
+ assert_throws_js(TypeError, () => { form.requestSubmit(control); });
+ }
+}, 'Passing an element which is not a submit button should throw');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <input form="" type="submit">
+ <button form="form2" type="submit"></button>
+ </form>
+ <form id="form2"></form>`);
+ let form = document.querySelector('form');
+ let submitButton = document.createElement('button');
+ submitButton.type = 'submit';
+ assert_throws_dom('NotFoundError', () => {
+ form.requestSubmit(submitButton);
+ });
+
+ let buttons = form.querySelectorAll('input, button');
+ assert_equals(buttons.length, 2);
+ for (let control of buttons) {
+ assert_throws_dom('NotFoundError', () => { form.requestSubmit(control) },
+ control.outerHTML);
+ }
+}, 'Passing a submit button not owned by the context object should throw');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<input type=submit form="form1">
+ <form id="form1" target="_blank">
+ <button type="submit"></button>
+ <button></button>
+ <button type="invalid"></button>
+ <input type="submit">
+ <input type="image">
+ </form>`);
+ let form = document.querySelector('form');
+ let didDispatchSubmit = false;
+ form.addEventListener('submit', event => { event.preventDefault(); didDispatchSubmit = true; });
+
+ assert_equals(form.elements.length, 5);
+ for (let control of form.elements) {
+ didDispatchSubmit = false;
+ form.requestSubmit(control);
+ assert_true(didDispatchSubmit, `${control.outerHTML} should submit the form`);
+ }
+ // <input type=image> is not in form.elements.
+ let control = form.querySelector('[type=image]');
+ didDispatchSubmit = false;
+ form.requestSubmit(control);
+ assert_true(didDispatchSubmit, `${control.outerHTML} should submit the form`);
+}, 'requestSubmit() should accept button[type=submit], input[type=submit], and input[type=image]');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', '<form><input required></form>');
+ let form = document.querySelector('form');
+ let invalidControl = form.querySelector('input:invalid');
+ let didDispatchInvalid = false;
+ invalidControl.addEventListener('invalid', e => { didDispatchInvalid = true; });
+
+ form.requestSubmit();
+ assert_true(didDispatchInvalid);
+}, 'requestSubmit() should trigger interactive form validation');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin',
+ '<form><input type=submit></form>');
+ let form = document.querySelector('form');
+ let submitButton = form.elements[0];
+ let submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ form.requestSubmit();
+ e.preventDefault();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(submitCounter, 1, 'requestSubmit() + requestSubmit()');
+
+ submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ submitButton.click();
+ e.preventDefault();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(submitCounter, 1, 'requestSubmit() + click()');
+
+ submitCounter = 0;
+ form.addEventListener('submit', e => {
+ ++submitCounter;
+ form.requestSubmit();
+ e.preventDefault();
+ }, {once: true});
+ submitButton.click();
+ assert_equals(submitCounter, 1, 'click() + requestSubmit()');
+}, 'requestSubmit() doesn\'t run form submission reentrantly');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin',
+ '<form><input type=submit><input required></form>');
+ let form = document.querySelector('form');
+ let submitButton = form.elements[0];
+ let invalidControl = form.elements[1];
+ let invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ form.requestSubmit();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(invalidCounter, 1, 'requestSubmit() + requestSubmit()');
+
+ invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ submitButton.click();
+ }, {once: true});
+ form.requestSubmit();
+ assert_equals(invalidCounter, 1, 'requestSubmit() + click()');
+
+ invalidCounter = 0;
+ invalidControl.addEventListener('invalid', e => {
+ ++invalidCounter;
+ if (invalidCounter < 10)
+ form.requestSubmit();
+ }, {once: true});
+ submitButton.click();
+ assert_equals(invalidCounter, 1, 'click() + requestSubmit()');
+}, 'requestSubmit() doesn\'t run interactive validation reentrantly');
+
+test(() => {
+ let form = document.createElement('form');
+ let submitCounter = 0;
+ form.addEventListener('submit', e => { ++submitCounter; e.preventDefault(); });
+ form.requestSubmit();
+ assert_equals(submitCounter, 0);
+}, 'requestSubmit() for a disconnected form should not submit the form');
+
+async_test(t => {
+ window.addEventListener('load', t.step_func(() => {
+ document.body.insertAdjacentHTML('afterbegin', `
+<form action="/common/blank.html">
+<input required>
+<input type=submit formnovalidate formtarget=iframe name=s value=v>
+</form>`);
+ let form = document.body.querySelector('form');
+ let iframe = document.body.querySelector('iframe');
+ assert_true(form.matches(':invalid'), 'The form is invalid.');
+ // The form should be submitted though it is invalid.
+ iframe.addEventListener('load', t.step_func_done(() => {
+ assert_not_equals(iframe.contentWindow.location.search.indexOf('s=v'), -1);
+ }));
+ form.requestSubmit(form.querySelector('[type=submit]'));
+ }));
+}, 'The value of the submitter should be appended, and form* ' +
+ 'attributes of the submitter should be handled.');
+
+test(() => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <input name="n1" value="v1">
+ <button type="submit" name="n2" value="v2"></button>
+ </form>
+ <form id="form2"></form>`);
+ let form = document.querySelector('form');
+ let formDataInEvent = null;
+ let submitter = form.querySelector('button[type=submit]');
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ formDataInEvent = new FormData(e.target);
+ });
+
+ form.requestSubmit(submitter);
+ assert_equals(formDataInEvent.get('n1'), 'v1');
+ assert_false(formDataInEvent.has('n2'));
+}, 'The constructed FormData object should not contain an entry for the submit button that was used to submit the form.');
+
+async_test(t => {
+ document.body.insertAdjacentHTML('afterbegin', `<form>
+ <button type="submit" name="n1" value="v1" disabled=""></button>
+ </form>`);
+ let form = document.querySelector('form');
+ let formDataInEvent = null;
+ let submitter = form.querySelector('button[type=submit]');
+
+ form.addEventListener("submit", t.step_func_done(ev => {
+ ev.preventDefault();
+ formDataInEvent = new FormData(ev.target);
+ assert_false(formDataInEvent.has('n1'));
+ assert_equals(ev.target, form);
+ }));
+
+ form.requestSubmit(submitter);
+
+}, "Using requestSubmit on a disabled button (via disabled attribute) should trigger submit but not be visible in FormData");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html
new file mode 100644
index 0000000000..b3599a45e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action-with-base.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<base href="target/"></base>
+
+<form>
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html
new file mode 100644
index 0000000000..c0c2ad0330
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-no-action.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<form>
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html
new file mode 100644
index 0000000000..edb101bece
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action-and-base.sub.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<base href="target/"></base>
+
+<form action="{{GET[action]}}">
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html
new file mode 100644
index 0000000000..97e800a561
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/form-with-action.sub.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<form action="{{GET[action]}}">
+ <input type="text" name="name" value="value">
+ <input type="submit" value="Submit">
+</form>
+
+<script>
+"use strict";
+
+if (window.location.search.startsWith("?name=value")) {
+ // The action pointed to ourself, so the form submitted something
+ window.parent.success(window.location.href);
+} else {
+ const form = document.querySelector("form");
+ form.submit();
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html
new file mode 100644
index 0000000000..d509f21924
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-form-element/resources/target/form-action-url-target.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+"use strict";
+window.parent.success(window.location.href);
+</script>