summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/selectors
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/semantics/selectors
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/semantics/selectors')
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/META.yml2
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js91
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html54
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html11
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html18
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js27
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html44
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html64
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html47
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html99
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html18
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html80
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html43
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html5
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html51
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html28
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html26
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html24
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html37
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js75
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html43
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html84
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html28
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html21
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html27
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html36
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html118
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html36
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html35
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js20
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html57
-rw-r--r--testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html146
36 files changed, 1585 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/selectors/META.yml b/testing/web-platform/tests/html/semantics/selectors/META.yml
new file mode 100644
index 0000000000..3195b8671c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/META.yml
@@ -0,0 +1,2 @@
+suggested_reviewers:
+ - lilles
diff --git a/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js b/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js
new file mode 100644
index 0000000000..1973398bff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/case-sensitivity/values.window.js
@@ -0,0 +1,91 @@
+// https://html.spec.whatwg.org/#case-sensitivity-of-selectors
+[
+ "accept",
+ "accept-charset",
+ "align",
+ "alink",
+ "axis",
+ "bgcolor",
+ "charset",
+ "checked",
+ "clear",
+ "codetype",
+ "color",
+ "compact",
+ "declare",
+ "defer",
+ "dir",
+ "direction",
+ "disabled",
+ "enctype",
+ "face",
+ "frame",
+ "hreflang",
+ "http-equiv",
+ "lang",
+ "language",
+ "link",
+ "media",
+ "method",
+ "multiple",
+ "nohref",
+ "noresize",
+ "noshade",
+ "nowrap",
+ "readonly",
+ "rel",
+ "rev",
+ "rules",
+ "scope",
+ "scrolling",
+ "selected",
+ "shape",
+ "target",
+ "text",
+ "type",
+ "valign",
+ "valuetype",
+ "vlink",
+].forEach(attributeName => {
+ const xmlDocument = new Document();
+ const htmlDocument = document;
+ [
+ {
+ input: xmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "a"),
+ expected: false,
+ title: "<html:a> in XML",
+ },
+ {
+ input: xmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "unknown"),
+ expected: false,
+ title: "<html:unknown> in XML",
+ },
+ {
+ input: xmlDocument.createElementNS("", "unknown"),
+ expected: false,
+ title: "<:unknown> in XML"
+ },
+ {
+ input: htmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "a"),
+ expected: true,
+ title: "<html:a> in HTML",
+ },
+ {
+ input: htmlDocument.createElementNS("http://www.w3.org/1999/xhtml", "unknown"),
+ expected: true,
+ title: "<html:unknown> in HTML",
+ },
+ {
+ input: htmlDocument.createElementNS("", "unknown"),
+ expected: false,
+ title: "<:unknown> in HTML"
+ },
+ ].forEach(({ input, expected, title }) => {
+ test(t => {
+ t.add_cleanup(() => input.removeAttribute(attributeName));
+ input.setAttribute(attributeName, "HEYÏ");
+ assert_equals(input.matches(`[${attributeName}^=hey]`), expected, `^=hey`);
+ assert_false(input.matches(`[${attributeName}^=heyi]`));
+ }, `${attributeName}'s value is properly ASCII-case-insensitive for ${title}`);
+ });
+});
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html
new file mode 100644
index 0000000000..a75a157c58
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/active-disabled.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/whatwg/html/pull/7465">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<label id=buttonlabel for=disabledbutton>label for disabled button</label>
+<button id=disabledbutton disabled>disabled</button>
+
+<button id=buttonparent disabled>
+ <div id=buttonchild>child of disabled</div>
+</button>
+
+<input id=disabledinput disabled>
+
+<textarea id=disabledtextarea disabled>disabled textarea</textarea>
+
+<script>
+function testElement(description, clickElement, checkElement) {
+ promise_test(async () => {
+ if (!checkElement)
+ checkElement = clickElement;
+
+ await (new test_driver.Actions()
+ .pointerMove(2, 2, {origin: clickElement})
+ .pointerDown())
+ .send();
+
+ assert_true(checkElement.matches(':active'));
+
+ await (new test_driver.Actions()
+ .pointerUp())
+ .send();
+ }, description);
+}
+
+testElement('Clicking on a disabled button should make it match the :active selector.',
+ disabledbutton);
+
+testElement('Clicking the label for a disabled button should make the button match the :active selector.',
+ buttonlabel, disabledbutton);
+
+testElement('Clicking on a child of a disabled button should make the button match the :active selector.',
+ buttonchild, buttonparent);
+
+testElement('Clicking on a disabled input should make it match the :active selector.',
+ disabledinput);
+
+testElement('Clicking on a disabled textarea should make it match the :active selector.',
+ disabledtextarea);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html
new file mode 100644
index 0000000000..b7c3644bdb
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/autofill.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:autofill)</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_valid_selector(":autofill");
+test_valid_selector(":-webkit-autofill", [":autofill", ":-webkit-autofill"]);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html
new file mode 100644
index 0000000000..76a963a600
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-001-manual.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+ <head>
+ <title>CSS Selectors (:checked)</title>
+ <link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/>
+ <link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/selectors/checked/001.html"/>
+ <style type="text/css">
+ :checked, :checked + span { border: solid blue; color: blue; background: navy; }
+ </style>
+ </head>
+ <body>
+ <p>Anything that is checked below should be blue.</p>
+ <p><input checked type="checkbox"> <span>X</span></p>
+ <p><input checked type="radio" name="x"> <span>X</span> <input checked type="radio" name="x"> <span>X</span></p>
+ <p><select><option selected>X</option></select></p>
+ <p><select size="2"><option selected>X</option></select></p>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js
new file mode 100644
index 0000000000..167cbdd37f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-indeterminate.window.js
@@ -0,0 +1,27 @@
+test(() => {
+ const input = document.createElement("input");
+ input.type = "checkbox";
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_false(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+
+ input.checked = true;
+ input.indeterminate = true;
+
+ assert_true(input.matches(":checked:indeterminate"));
+ assert_true(input.matches(":checked"));
+ assert_true(input.matches(":indeterminate"));
+
+ input.indeterminate = false;
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_true(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+
+ input.checked = false;
+
+ assert_false(input.matches(":checked:indeterminate"));
+ assert_false(input.matches(":checked"));
+ assert_false(input.matches(":indeterminate"));
+}, "An element can be :checked and :indeterminate at the same time");
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html
new file mode 100644
index 0000000000..661d9e4355
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked-type-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :checked input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span { color: red }
+ :checked + span { color: green }
+</style>
+<input id="checked" type="text" checked>
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :checked for type=text");
+
+ checked.type = "radio";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :checked for type=radio");
+ }, "Evaluation of :checked changes on input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html
new file mode 100644
index 0000000000..754c2342bc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/checked.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:checked)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+ <option value="option2" id=option2>option2
+ <option value="option2" id=option3 checked>option3
+</select>
+<input type=checkbox id=checkbox1 checked>
+<input type=checkbox id=checkbox2>
+<input type=checkbox id=checkbox3 selected>
+<input type=radio id=radio1 checked>
+<input type=radio id=radio2>
+<form>
+ <p><input type=submit contextmenu=formmenu id="submitbutton"></p>
+ <menu type=context id=formmenu>
+ <!-- historical; these should *not* match -->
+ <menuitem type=checkbox checked default id=menuitem1>
+ <menuitem type=checkbox default id=menuitem2>
+ <menuitem type=checkbox id=menuitem3>
+ <menuitem type=radio checked id=menuitem4>
+ <menuitem type=radio id=menuitem5>
+ </menu>
+</form>
+
+<script>
+ testSelectorIdsMatch(":checked", ["option1", "checkbox1", "radio1"], "':checked' matches checked <input>s in checkbox and radio button states, selected <option>s");
+
+ document.getElementById("checkbox1").removeAttribute("type"); // change type of input
+ document.getElementById("radio1").removeAttribute("type"); // change type of input
+ testSelectorIdsMatch(":checked", ["option1"], "':checked' should no longer match <input>s whose type checkbox/radio has been removed");
+
+ document.getElementById("option2").selected = "selected"; // select option2
+ document.getElementById("checkbox2").click(); // check chekbox2
+ document.getElementById("radio2").click(); // check radio2
+ testSelectorIdsMatch(":checked", ["option2", "checkbox2", "radio2"], "':checked' matches clicked checkbox and radio buttons");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html
new file mode 100644
index 0000000000..3187801f67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/default.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:default)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<form>
+ <button id=button1 type=button>button1</button>
+ <button id=button2 type=submit>button2</button>
+</form>
+<form>
+ <button id=button3 type=reset>button3</button>
+ <button id=button4>button4</button>
+</form>
+<button id=button5 type=submit>button5</button>
+<form id=form1>
+ <input type=text id=input1>
+</form>
+<input type=text id=input2 form=form1>
+<form>
+ <input type=submit id=input3>
+ <input type=submit id=input4>
+</form>
+<form>
+ <input type=image id=input5>
+ <input type=image id=input6>
+</form>
+<form>
+ <input type=submit id=input7>
+</form>
+<input type=checkbox id=checkbox1 checked>
+<input type=checkbox id=checkbox2>
+<input type=checkbox id=checkbox3 default>
+<input type=radio name=radios id=radio1 checked>
+<input type=radio name=radios id=radio2>
+<input type=radio name=radios id=radio3 default>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1>option1
+ <option value="option2" id=option2 selected>option2
+</select>
+<dialog id="dialog">
+ <input type=submit id=input8>
+</dialog>
+<form>
+ <button id=button6 type='invalid'>button6</button>
+ <button id=button7>button7</button>
+</form>
+<form>
+ <button id=button8>button8</button>
+ <button id=button9>button9</button>
+</form>
+
+
+<script>
+ testSelectorIdsMatch(":default", ["button2", "button4", "input3", "input5", "input7", "checkbox1", "radio1", "option2", "button6", "button8"], "':default' matches <button>s that are their form's default button, <input>s of type submit/image that are their form's default button, checked <input>s and selected <option>s");
+
+ document.getElementById("button1").type = "submit"; // change the form's default button
+ testSelectorIdsMatch(":default", ["button1", "button4", "input3", "input5", "input7", "checkbox1", "radio1", "option2", "button6", "button8"], "':default' matches dynamically changed form's default buttons");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html
new file mode 100644
index 0000000000..8f2951f8ef
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-dynamic.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="author" title="Vincent Hilla" href="mailto:vhilla@mozilla.com">
+ <link rel="help" href="https://html.spec.whatwg.org/#the-directionality">
+</head>
+<body>
+ <input id="inp"/>
+ <textarea id="ta"></textarea>
+ <div id="div"></div>
+ <pre id="pre"></pre>
+
+ <script>
+ function doTest(e) {
+ e.dir = "ltr";
+ assert_true(e.matches(":dir(ltr)"), "dir to ltr on " + e.tagName + " element");
+
+ e.dir = "rtl";
+ assert_true(e.matches(":dir(rtl)"), "dir to rtl on " + e.tagName + " element");
+
+ e.dir = "auto";
+ assert_true(e.matches(":dir(ltr)"), "dir to auto, empty text on " + e.tagName + " element");
+
+ e.value = "\u05D0;";
+ e.textContent = "\u05D0;";
+ assert_true(e.matches(":dir(rtl)"), "auto dir, text to Hebrew on " + e.tagName + " element");
+
+ e.dir = "ltr";
+ assert_true(e.matches(":dir(ltr)"), "dir to ltr, Hebrew text on " + e.tagName + " element");
+
+ e.dir = "auto";
+ assert_true(e.matches(":dir(rtl)"), "dir to auto, Hebrew text on " + e.tagName + " element");
+
+ e.removeAttribute("dir");
+ assert_true(e.matches(":dir(ltr)"), "dir removed, Hebrew text on " + e.tagName + " element");
+ }
+
+ const elements = [inp, ta, div, pre];
+ for (const e of elements) {
+ test(() => doTest(e), "Dynamically changing dir, text on " + e.tagName.toLowerCase() + " element");
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html
new file mode 100644
index 0000000000..0c50cec369
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir-html-input-dynamic-text.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1622900">
+<link rel="help" href="https://html.spec.whatwg.org/#the-directionality">
+<input value="ltr" dir="auto">
+<script>
+test(function() {
+ let input = document.querySelector("input");
+ assert_true(input.matches(":dir(ltr)"), "Input with ltr value should match dir(ltr)");
+ input.textContent = "ﷺ";
+ assert_true(input.matches(":dir(ltr)"), "Should still match dir(ltr) after text change");
+ input.value = "ltr2";
+ assert_true(input.matches(":dir(ltr)"), "Should still match dir(ltr) after value change");
+ input.value = "ﷺ";
+ assert_true(input.matches(":dir(rtl)"), "Should match dir(rtl) after value change");
+ input.textContent = "ltr";
+ assert_true(input.matches(":dir(rtl)"), "Should match dir(rtl) after text change");
+}, ":dir on <input> isn't altered by text children")
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html
new file mode 100644
index 0000000000..588c3c6850
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html id=html>
+ <head id=head>
+ <meta charset=utf-8 id=meta>
+ <title id=title>Selector: pseudo-classes (:dir(ltr), :dir(rtl))</title>
+ <link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+ <link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+ <script src="/resources/testharness.js" id=script1></script>
+ <script src="/resources/testharnessreport.js" id=script2></script>
+ <script src="utils.js" id=script3></script>
+ <style id=style>
+ #span1 {direction: rtl;}
+ #span5, #span6 {display: none;}
+ </style>
+ </head>
+ <body id=body>
+ <div id="log"></div>
+ <bdo dir="rtl" id=bdo1>WERBEH</bdo>
+ <bdo dir="ltr" id=bdo2>HEBREW</bdo>
+ <bdi id=bdi1>HEBREW</bdi>
+ <bdi dir="rtl" id=bdi2>WERBEH</bdi>
+ <bdi dir="ltr" id=bdi3>HEBREW</bdi>
+ <bdi id=bdi4>إيان</bdi>
+ <span id=span1>WERBEH</span>
+ <span dir="rtl" id=span2>WERBEH</span>
+ <span dir="ltr" id=span3>HEBREW</span>
+ &#x202E;<span id=span4>WERBEH</span>&#x202C;
+ <span dir="rtl" id=span5>WERBEH</span>
+ <span dir="ltr" id=span6>HEBREW</span>
+ <span dir="rtl" id=span7>
+ <input type=tel id=input-tel1>
+ <input type=tel id=input-tel2 dir="invalid">
+ </span>
+ <input type=tel id=input-tel3 dir="rtl">
+ <bdo dir="auto" id=bdo3>HEBREW</bdo>
+ <bdo dir="auto" id=bdo4>إيان</bdo>
+ <bdo dir="ltr" id=bdo5>עברית</bdo>
+ <textarea dir="auto" id="ta1">إيان</textarea>
+ <textarea dir="auto" id="ta2">HEBREWإيان</textarea>
+ <textarea dir="auto" id="ta3">إيان</textarea>
+ <pre dir="auto" id="pre1">إيان</pre>
+ <pre dir="auto" id="pre2">HEBREWإيان</pre>
+
+ <script id=script4>
+ ta3.value = "HEBREW";
+
+ const rtlElements = [
+ "bdo1",
+ "bdi2",
+ "bdi4",
+ "span2",
+ "span5",
+ "span7",
+ "input-tel3",
+ "bdo4",
+ "ta1",
+ "pre1",
+ ];
+
+ testSelectorIdsMatch(":dir(rtl)", rtlElements, "':dir(rtl)' matches all elements whose directionality is 'rtl'.");
+
+ const ltrElements = [
+ "html",
+ "head",
+ "meta",
+ "title",
+ "link1",
+ "link2",
+ "script1",
+ "script2",
+ "script3",
+ "style",
+ "body",
+ "log",
+ "bdo2",
+ "bdi1",
+ "bdi3",
+ "span1",
+ "span3",
+ "span4",
+ "span6",
+ "input-tel1",
+ "input-tel2",
+ "bdo3",
+ "bdo5",
+ "ta2",
+ "ta3",
+ "pre2",
+ "script4",
+ ];
+
+ testSelectorIdsMatch(":dir(ltr)", ltrElements, "':dir(ltr)' matches all elements whose directionality is 'ltr'.");
+
+ const bdo = document.createElement("bdo");
+ bdo.setAttribute("dir", "ltr");
+ testSelectorIdsMatch(":dir(ltr)", ltrElements, "':dir(ltr)' doesn't match elements not in the document.");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html
new file mode 100644
index 0000000000..61bbd574a3
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/dir01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=iso-8859-8 id=meta>
+<title id=title>Selector: pseudo-classes (:dir(ltr), :dir(rtl)) in iso-8859-8 documents</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js" id=script1></script>
+<script src="/resources/testharnessreport.js" id=script2></script>
+<script src="utils.js" id=script3></script>
+<div id="log"></div>
+<div>This text is left to right<div id=div1 style="direction:rtl">this is right to left</div></div>
+<div>This text is left to right<span id=div2 style="direction:rtl">this is left to right</span></div>
+
+<script>
+ var ltr = new Array(),
+ all = document.querySelectorAll('*');
+ for(var i = all.length; i--; ltr.unshift(all[i]));
+ testSelectorElementsMatch(":dir(ltr)", ltr, "direction doesn't affect :dir()");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html
new file mode 100644
index 0000000000..142a12dd12
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/disabled.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:disabled)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<style>
+ #input4 {display:none;}
+</style>
+<div id="log"></div>
+<button id=button1 type=submit>button1</button>
+<button id=button2 disabled>button2</button>
+<input id=input1>
+<input id=input2 disabled>
+<input id=input3 readonly>
+<input id=input4>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+</select>
+<select disabled id=select2>
+ <optgroup label="options" disabled id=optgroup2>
+ <option value="option2" disabled id=option2>option2
+</select>
+<textarea id=textarea1>textarea1</textarea>
+<textarea disabled id=textarea2>textarea2</textarea>
+<fieldset id=fieldset1></fieldset>
+<fieldset disabled id=fieldset2>
+ <legend><input type=checkbox id=club></legend>
+ <p><label>Name on card: <input id=clubname required></label></p>
+ <p><label>Card number: <input id=clubnum required pattern="[-0-9]+"></label></p>
+</fieldset>
+<label disabled></label>
+<object disabled></object>
+<output disabled></output>
+<img disabled/>
+<meter disabled></meter>
+<progress disabled></progress>
+
+<script>
+ testSelectorIdsMatch(":disabled", ["button2", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should match only disabled elements");
+
+ document.getElementById("button2").removeAttribute("disabled");
+ testSelectorIdsMatch(":disabled", ["input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements whose disabled attribute has been removed");
+
+ document.getElementById("button1").setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
+
+ document.getElementById("button1").setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set twice");
+
+ document.getElementById("input2").setAttribute("type", "submit"); // change input type to submit
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
+
+ var input = document.createElement("input");
+ input.setAttribute("disabled", "disabled");
+ testSelectorIdsMatch(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should not match elements not in the document");
+
+ var fieldset = document.createElement("fieldset");
+ fieldset.id = "fieldset_nested";
+ fieldset.innerHTML = `
+ <input id=input_nested>
+ <button id=button_nested>button nested</button>
+ <select id=select_nested>
+ <optgroup label="options" id=optgroup_nested>
+ <option value="options" id=option_nested>option nested</option>
+ </optgroup>
+ </select>
+ <textarea id=textarea_nested>textarea nested</textarea>
+ <object id=object_nested></object>
+ <output id=output_nested></output>
+ <fieldset id=fieldset_nested2>
+ <input id=input_nested2>
+ </fieldset>
+ `;
+ document.getElementById("fieldset2").appendChild(fieldset);
+ testSelectorIdsMatch("#fieldset2 :disabled", ["clubname", "clubnum", "fieldset_nested", "input_nested", "button_nested", "select_nested", "textarea_nested", "fieldset_nested2", "input_nested2"], "':disabled' should match elements that are appended to a disabled fieldset dynamically");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html
new file mode 100644
index 0000000000..0ad0e1b402
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/enabled.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:enabled)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<a id=link3></a>
+<area id=link4></area>
+<link id=link5></link>
+<a href="http://www.w3.org" id=link6></a>
+<area href="http://www.w3.org" id=link7></area>
+<link href="http://www.w3.org" id=link8></link>
+<button id=button1>button1</button>
+<button id=button2 disabled>button2</button>
+<input id=input1>
+<input id=input2 disabled>
+<select id=select1>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1 selected>option1
+</select>
+<select disabled id=select2>
+ <optgroup label="options" disabled id=optgroup2>
+ <option value="option2" disabled id=option2>option2
+</select>
+<textarea id=textarea1>textarea1</textarea>
+<textarea disabled id=textarea2>textarea2</textarea>
+<form>
+ <p><input type=submit contextmenu=formmenu id=submitbutton></p>
+ <menu type=context id=formmenu>
+ <!-- historical; these should *not* match -->
+ <menuitem command="submitbutton" default id=menuitem1>
+ <menuitem command="resetbutton" disabled id=menuitem2>
+ </menu>
+</form>
+<fieldset id=fieldset1></fieldset>
+<fieldset disabled id=fieldset2></fieldset>
+
+<script>
+ testSelectorIdsMatch(":enabled", ["button1", "input1", "select1", "optgroup1", "option1", "textarea1", "submitbutton", "fieldset1"], "':enabled' elements that are not disabled");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html
new file mode 100644
index 0000000000..80a75bb99e
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-autofocus.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus for autofocus)</title>
+<link rel="author" title="Kent Tamura" href="mailto:tkent@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<link rel=help href="https://html.spec.whatwg.org/multipage/forms.html#autofocusing-a-form-control:-the-autofocus-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// This test can't be merged to focus.html because element.focus() may affect
+// autofocus behavior.
+var autofocusTest = async_test(":focus selector should work with an autofocused element.");
+var input = document.createElement("input");
+input.autofocus = true;
+input.addEventListener("focus", function() {
+ autofocusTest.step(function() {
+ assert_array_equals(document.querySelectorAll(":focus"), [input])
+ autofocusTest.done();
+ });
+}, false);
+document.body.appendChild(input);
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html
new file mode 100644
index 0000000000..a269f1c671
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus-iframe.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org">
+<input id="inputiframe" type=text value="foobar" />
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html
new file mode 100644
index 0000000000..a319b24ef0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/focus.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:focus)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<body id=body tabindex=0>
+ <div id="log"></div>
+ <button id=button1 type=submit>button1</button>
+ <input id=input1>
+ <input id=input2 disabled>
+ <textarea id=textarea1>textarea1</textarea>
+ <input type=checkbox id=checkbox1 checked>
+ <input type=radio id=radio1 checked>
+ <div tabindex=0 id=div1>hello</div>
+ <div contenteditable id=div2>content</div>
+ <iframe src="focus-iframe.html" id=iframe></iframe>
+
+ <script>
+ setup({explicit_done: true});
+
+ onload = function() {
+ if (document.hasFocus() || frames[0].document.hasFocus()) {
+ run_test()
+ } else {
+ window.onfocus = run_test;
+ }
+ }
+
+ function run_test() {
+ document.getElementById("input1").focus(); // set the focus on input1
+ testSelectorIdsMatch(":focus", ["input1"], "input1 has the focus");
+
+ document.getElementById("div1").focus();
+ testSelectorIdsMatch(":focus", ["div1"], "tabindex attribute makes the element focusable");
+
+ document.getElementById("div2").focus();
+ testSelectorIdsMatch(":focus", ["div2"], "editable elements are focusable");
+
+ document.body.focus();
+ testSelectorIdsMatch(":focus", ["body"], "':focus' matches focussed body with tabindex");
+
+ document.getElementById("iframe").contentDocument.getElementById("inputiframe").focus();
+ testSelectorIdsMatch(":focus", [], "':focus' doesn't match focused elements in iframe");
+
+ done();
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html
new file mode 100644
index 0000000000..68d95fe240
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group-ref.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test reference</title>
+<style>
+label[for="two"] {
+ background: green;
+}
+</style>
+<form>
+ <input type="radio" name="a" id="one" value="1">
+ <label for="one">One</label>
+
+ <input type="radio" name="a" id="two" value="2" checked>
+ <label for="two">Two</label>
+
+ <input type="radio" name="a" id="three" value="3">
+ <label for="three">Three</label>
+
+ <input type="radio" name="a" id="four" value="4">
+ <label for="four">Four</label>
+</form>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html
new file mode 100644
index 0000000000..a49b957021
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio-group.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>:indeterminate and input type=radio</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1861346">
+<link rel="match" href="indeterminate-radio-group-ref.html">
+<style>
+input:checked + label {
+ background: green;
+}
+input:indeterminate + label {
+ background: red;
+}
+</style>
+<form>
+ <input type="radio" name="a" id="one" value="1">
+ <label for="one">One</label>
+
+ <input type="radio" name="a" id="two" value="2" checked>
+ <label for="two">Two</label>
+
+ <input type="radio" name="a" id="three" value="3">
+ <label for="three">Three</label>
+
+ <input type="radio" name="a" id="four" value="4">
+ <label for="four">Four</label>
+</form>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html
new file mode 100644
index 0000000000..4a7b2d6ece
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-radio.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>:indeterminate and input type=radio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+#test {
+ color: green;
+}
+input:indeterminate + #test {
+ color: red;
+}
+</style>
+<input type="radio" name="radios">
+<div id="test"></div>
+<input type="radio" name="radios" checked>
+<script type="text/javascript">
+test(function() {
+ document.getElementsByTagName("input")[0].indeterminate = true;
+ var target = document.getElementById("test");
+ var val = getComputedStyle(target, null).getPropertyValue("color");
+ assert_equals(val, "rgb(0, 128, 0)",
+ "The indeterminate IDL attribute should not cause the " +
+ ":indeterminate pseudo-class to match on input type=radio");
+})
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html
new file mode 100644
index 0000000000..b3e4cce302
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate-type-change.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :indeterminate input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span { color: red }
+ :indeterminate + span { color: green }
+</style>
+<input id="indeterminate" type="text">
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :indeterminate for type=text");
+
+ indeterminate.type = "radio";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :indeterminate for type=radio");
+ }, "Evaluation of :indeterminate changes on input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html
new file mode 100644
index 0000000000..df04846676
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/indeterminate.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:indeterminate)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=checkbox id=checkbox1>
+<input type=checkbox id=checkbox2>
+<input type=radio id=radio1 checked>
+<input type=radio name=radiogroup id=radio2>
+<input type=radio name=radiogroup id=radio3>
+<input type=radio name=group2 id=radio4>
+<input type=radio name=group2 id=radio5>
+<progress id="progress1"></progress>
+<progress id="progress2" value=10></progress>
+
+<script>
+ testSelectorIdsMatch(":indeterminate", ["radio2", "radio3", "radio4", "radio5", "progress1"], "':progress' matches <input>s radio buttons whose radio button group contains no checked input and <progress> elements without value attribute");
+
+ document.getElementById("radio2").setAttribute("checked", "checked");
+ testSelectorIdsMatch(":indeterminate", ["radio4", "radio5", "progress1"], "dynamically check a radio input in a radio button group");
+
+ document.getElementById("radio4").click();
+ testSelectorIdsMatch(":indeterminate", ["progress1"], "click on radio4 which is in the indeterminate state");
+
+ document.getElementById("progress1").setAttribute("value", "20");
+ testSelectorIdsMatch(":indeterminate", [], "adding a value to progress1 should put it in a determinate state");
+
+ document.getElementById("progress2").removeAttribute("value");
+ testSelectorIdsMatch(":indeterminate", ["progress2"], "removing progress2's value should put it in an indeterminate state");
+
+ document.getElementById("checkbox1").indeterminate = true; // set checkbox1 in the indeterminate state
+ testSelectorIdsMatch(":indeterminate", ["checkbox1", "progress2"], "':progress' also matches <input> checkbox whose indeterminate IDL is set to true");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js
new file mode 100644
index 0000000000..b5d9898a64
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/input-checkbox-switch.tentative.window.js
@@ -0,0 +1,75 @@
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+
+ assert_false(input.matches(":indeterminate"));
+}, "Switch control does not match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+
+ assert_false(input.matches(":indeterminate"));
+
+ input.switch = false;
+ assert_true(input.matches(":indeterminate"));
+}, "Checkbox that is no longer a switch control does match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.indeterminate = true;
+
+ assert_true(input.matches(":indeterminate"));
+
+ input.setAttribute("switch", "blah");
+ assert_false(input.matches(":indeterminate"));
+}, "Checkbox that becomes a switch control does not match :indeterminate");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.indeterminate = true;
+
+ assert_true(document.body.matches(":has(:indeterminate)"));
+
+ input.switch = true;
+ assert_false(document.body.matches(":has(:indeterminate)"));
+}, "Parent of a checkbox that becomes a switch control does not match :has(:indeterminate)");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true
+ input.checked = true;
+
+ assert_true(document.body.matches(":has(:checked)"));
+
+ input.switch = false;
+ assert_true(document.body.matches(":has(:checked)"));
+
+ input.checked = false;
+ assert_false(document.body.matches(":has(:checked)"));
+}, "Parent of a switch control that becomes a checkbox continues to match :has(:checked)");
+
+test(t => {
+ const input = document.body.appendChild(document.createElement("input"));
+ t.add_cleanup(() => input.remove());
+ input.type = "checkbox";
+ input.switch = true;
+ input.indeterminate = true;
+ assert_false(input.matches(":indeterminate"));
+ input.type = "text";
+ input.removeAttribute("switch");
+ input.type = "checkbox";
+ assert_true(input.matches(":indeterminate"));
+}, "A switch control that becomes a checkbox in a roundabout way");
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html
new file mode 100644
index 0000000000..9c1be9ca27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:in-range, :out-of-range) input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ }
+ #t1:in-range + span {
+ color: green;
+ }
+ #t2:out-of-range + span {
+ color: green;
+ }
+</style>
+<input id="t1" type="text" min="0" max="10" value="5">
+<span id="sibling1">This text should be green.</span>
+<input id="t2" type="text" min="0" max="10" value="50">
+<span id="sibling2">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling1).color, "rgb(255, 0, 0)",
+ "Not matching :in-range for type=text");
+
+ t1.type = "number";
+
+ assert_equals(getComputedStyle(sibling1).color, "rgb(0, 128, 0)",
+ "Matching :in-range for type=number");
+ }, "Evaluation of :in-range changes for input type change.");
+
+ test(() => {
+ assert_equals(getComputedStyle(sibling2).color, "rgb(255, 0, 0)",
+ "Not matching :out-of-range for type=text");
+
+ t2.type = "number";
+
+ assert_equals(getComputedStyle(sibling2).color, "rgb(0, 128, 0)",
+ "Matching :in-range for type=number");
+ }, "Evaluation of :out-of-range changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html
new file mode 100644
index 0000000000..e9acbb3741
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/inrange-outofrange.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:in-range, :out-of-range)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id="link1">
+<link rel="author" title="Chris Rebert" href="http://chrisrebert.com" id="link2">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#selector-in-range" id="link3">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#selector-out-of-range" id="link4">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=number value=0 min=0 max=10 id=number1>
+<input type=number value=0 min=0 max=10 id=number2 disabled>
+<input type=number value=0 min=1 max=10 id=number3>
+<input type=number value=11 min=0 max=10 id=number4>
+<input type=number value=0 min=0 max=10 id=number5 readonly>
+
+<input type="date" min="2005-10-10" max="2020-10-10" value="2010-10-10" id="datein">
+<input type="date" min="2010-10-10" max="2020-10-10" value="2005-10-10" id="dateunder">
+<input type="date" min="2010-10-10" max="2020-10-10" value="2030-10-10" id="dateover">
+
+<input type="time" min="01:00:00" max="05:00:00" value="02:00:00" id="timein">
+<input type="time" min="02:00:00" max="05:00:00" value="01:00:00" id="timeunder">
+<input type="time" min="02:00:00" max="05:00:00" value="07:00:00" id="timeover">
+
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W07" id="weekin">
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W02" id="weekunder">
+<input type="week" min="2016-W05" max="2016-W10" value="2016-W26" id="weekover">
+
+<input type="month" min="2000-04" max="2000-09" value="2000-06" id="monthin">
+<input type="month" min="2000-04" max="2000-09" value="2000-02" id="monthunder">
+<input type="month" min="2000-04" max="2000-09" value="2000-11" id="monthover">
+
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2012-11-28T23:59:59" id="datetimelocalin">
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2008-03-01T23:59:59" id="datetimelocalunder">
+<input type="datetime-local" min="2008-03-12T23:59:59" max="2015-02-13T23:59:59" value="2016-01-01T23:59:59" id="datetimelocalover">
+
+<!-- None of the following have range limitations since they have neither min nor max attributes -->
+<input type="number" value="0" id="numbernolimit">
+<input type="date" value="2010-10-10" id="datenolimit">
+<input type="time" value="02:00:00" id="timenolimit">
+<input type="week" value="2016-W07" id="weeknolimit">
+<input type="month" value="2000-06" id="monthnolimit">
+<input type="datetime-local" value="2012-11-28T23:59:59" id="datetimelocalnolimit">
+
+<!-- range inputs have default minimum of 0 and default maximum of 100 -->
+<input type="range" value="50" id="range0">
+
+<!-- range input's value gets immediately clamped to the nearest boundary point -->
+<input type="range" min="2" max="7" value="5" id="range1">
+<input type="range" min="2" max="7" value="1" id="range2">
+<input type="range" min="2" max="7" value="9" id="range3">
+
+<!-- None of the following input types can have range limitations -->
+<input min="1" value="0" type="text">
+<input min="1" value="0" type="search">
+<input min="1" value="0" type="url">
+<input min="1" value="0" type="tel">
+<input min="1" value="0" type="email">
+<input min="1" value="0" type="password">
+<input min="1" value="#000000" type="color">
+<input min="1" value="0" type="checkbox">
+<input min="1" value="0" type="radio">
+<input min="1" value="0" type="file">
+<input min="1" value="0" type="submit">
+<input min="1" value="0" type="image">
+<!-- The following types are also barred from constraint validation -->
+<input min="1" value="0" type="hidden">
+<input min="1" value="0" type="button">
+<input min="1" value="0" type="reset">
+
+<script>
+ testSelectorIdsMatch(":in-range", ["number1", "datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' matches all elements that are candidates for constraint validation, have range limitations, and that are neither suffering from an underflow nor suffering from an overflow");
+
+ testSelectorIdsMatch(":out-of-range", ["number3", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' matches all elements that are candidates for constraint validation, have range limitations, and that are either suffering from an underflow or suffering from an overflow");
+
+ document.getElementById("number1").value = -10;
+ testSelectorIdsMatch(":in-range", ["datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' update number1's value < min");
+ testSelectorIdsMatch(":out-of-range", ["number1", "number3", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' update number1's value < min");
+
+ document.getElementById("number3").min = 0;
+ testSelectorIdsMatch(":in-range", ["number3", "datein", "timein", "weekin", "monthin", "datetimelocalin", "range0", "range1", "range2", "range3"], "':in-range' update number3's min < value");
+ testSelectorIdsMatch(":out-of-range", ["number1", "number4", "dateunder", "dateover", "timeunder", "timeover", "weekunder", "weekover", "monthunder", "monthover", "datetimelocalunder", "datetimelocalover"], "':out-of-range' update number3's min < value");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html
new file mode 100644
index 0000000000..92345602a8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/invalid-after-clone.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<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>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<input>
+<textarea></textarea>
+<script>
+promise_test(async () => {
+ for (let tag of ["input", "textarea"]) {
+ let element = document.querySelector(tag);
+ await test_driver.send_keys(element, 'something');
+
+ assert_true(element.validity.valid, tag + ' should be valid');
+
+ element.maxLength = 0;
+ assert_true(element.matches(":invalid"), tag + ' should match :invalid');
+ assert_false(element.validity.valid, tag + ' should be invalid');
+
+ let clone = element.cloneNode(true);
+ assert_true(clone.matches(":invalid"), tag + ' clone should match :invalid');
+ assert_false(clone.validity.valid, tag + 'clone should be invalid');
+ }
+}, 'Cloned invalid inputs / textareas with interactive changes get their validity state copied correctly');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html
new file mode 100644
index 0000000000..e9733eca70
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/link.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:link)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<link rel=stylesheet href="non-existent.css" id=link3>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<a id=link4></a>
+<area id=link5></area>
+<link id=link6></link>
+<a href="http://www.w3.org" id=link7></a>
+<area href="http://www.w3.org" id=link8></area>
+<link href="http://www.w3.org" id=link9></link>
+<a href="http://[" id=link10></a>
+
+<script>
+ testSelectorIdsMatch(":link", ["link7", "link8", "link10"], "Only <a>s and <area>s that have a href attribute match ':link'");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html
new file mode 100644
index 0000000000..206ae80c75
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-class :placeholder-shown input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ }
+ :placeholder-shown + span {
+ color: green;
+ }
+</style>
+<input id="input" type="submit" placeholder="placeholder"></input>
+<span id="sibling">This text should be green.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :placeholder-shown for type=submit");
+
+ input.type = "text";
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :placeholder-shown for type=text");
+ }, "Evaluation of :placeholder-shown changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html
new file mode 100644
index 0000000000..90ef1d25d4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:read-write, :read-only) input type change</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ background-color: pink;
+ }
+ :required + span {
+ color: green;
+ }
+ :not(:optional) + span {
+ background-color: lime;
+ }
+</style>
+<input id="hiddenInput" type="hidden" required>
+<span id="sibling">This text should be green on lime background.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :required for type=hidden");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(255, 192, 203)",
+ "Matching :optional for type=hidden");
+
+ hiddenInput.type = "text";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :required for type=text");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(0, 255, 0)",
+ "Matching :not(:optional) for type=text");
+ }, "Evaluation of :required and :optional changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html
new file mode 100644
index 0000000000..fb8a5b9ad7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/readwrite-readonly.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:read-write, :read-only)</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+
+<div id=set0>
+<!-- The readonly attribute does not apply to the following input types -->
+<input id=checkbox1 type=checkbox>
+<input id=hidden1 type=hidden value=abc>
+<input id=range1 type=range>
+<input id=color1 type=color>
+<input id=radio1 type=radio>
+<input id=file1 type=file>
+<input id=submit1 type=submit>
+<input id=image1 type=image>
+<input id=button1 type=button value="Button">
+<input id=reset1 type=reset>
+</div>
+
+<div id=set1>
+<input id=input1>
+<input id=input2 readonly>
+<input id=input3 disabled>
+<input id=input4 type=checkbox>
+<input id=input5 type=checkbox readonly>
+</div>
+
+<div id=set2>
+<textarea id=textarea1>textarea1</textarea>
+<textarea readonly id=textarea2>textarea2</textarea>
+</div>
+
+<div id=set3>
+<textarea id=textarea3>textarea3</textarea>
+<textarea disabled id=textarea4>textarea4</textarea>
+</div>
+
+<div id=set4>
+<p id=p1>paragraph1.</p>
+<p id=p2 contenteditable>paragraph2.</p>
+</div>
+
+<div id=set5>
+ <div id=cd1 contenteditable>
+ <p id=p3></p>
+ <input id=ci1 readonly>
+ <input id=ci2 disabled>
+ <input id=ci3>
+ <input id=ci4>
+ <textarea id=ct1 readonly></textarea>
+ <textarea id=ct2 disabled></textarea>
+ <textarea id=ct3></textarea>
+ <textarea id=ct4></textarea>
+ </div>
+</div>
+
+<script>
+ testSelectorIdsMatch("#set0 :read-write", [], "The :read-write pseudo-class must not match input elements to which the readonly attribute does not apply");
+
+ testSelectorIdsMatch("#set0 :read-only", ["checkbox1", "hidden1", "range1", "color1", "radio1", "file1", "submit1", "image1", "button1", "reset1"], "The :read-only pseudo-class must match input elements to which the readonly attribute does not apply");
+
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements to which the readonly attribute applies, and that are mutable");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements to which the readonly attribute applies, and that are mutable");
+
+ document.getElementById("input1").setAttribute("readonly", "readonly");
+ testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been added");
+
+ document.getElementById("input1").removeAttribute("readonly");
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must not match input elements after the readonly attribute has been removed");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the readonly attribute has been removed");
+
+ document.getElementById("input1").disabled = true;
+ testSelectorIdsMatch("#set1 :read-write", [], "The :read-write pseudo-class must not match input elements after the disabled attribute has been added");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input1", "input2", "input3", "input4", "input5"], "The :read-only pseudo-class must match input elements after the disabled attribute has been added");
+
+ document.getElementById("input1").disabled = false;
+
+ testSelectorIdsMatch("#set1 :read-write", ["input1"], "The :read-write pseudo-class must match input elements after the disabled attribute has been removed");
+
+ testSelectorIdsMatch("#set1 :read-only", ["input2", "input3", "input4", "input5"], "The :read-only pseudo-class must not match input elements after the disabled attribute has been removed");
+
+ testSelectorIdsMatch("#set2 :read-write", ["textarea1"], "The :read-write pseudo-class must match textarea elements that do not have a readonly attribute, and that are not disabled");
+
+ testSelectorIdsMatch("#set2 :read-only", ["textarea2"], "The :read-only pseudo-class must match textarea elements that have a readonly attribute, or that are disabled");
+
+ document.getElementById("textarea1").setAttribute("readonly", "readonly");
+ testSelectorIdsMatch("#set2 :read-write", [], "The :read-write pseudo-class must match textarea elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set2 :read-only", ["textarea1", "textarea2"], "The :read-only pseudo-class must match textarea elements after the readonly attribute has been added");
+
+ testSelectorIdsMatch("#set3 :read-write", ["textarea3"], "The :read-write pseudo-class must not match textarea elements that are disabled");
+
+ testSelectorIdsMatch("#set3 :read-only", ["textarea4"], "The :read-only pseudo-class must match textarea elements that are disabled");
+
+ testSelectorIdsMatch("#set4 :read-write", ["p2"], "The :read-write pseudo-class must match elements that are editable");
+
+ testSelectorIdsMatch("#set4 :read-only", ["p1"], "The :read-only pseudo-class must not match elements that are editable");
+
+ document.designMode = "on";
+
+ testSelectorIdsMatch("#set4 :read-write", ["p1", "p2"], "The :read-write pseudo-class must match elements that are editing hosts");
+
+ testSelectorIdsMatch("#set4 :read-only", [], "The :read-only pseudo-class must not match elements that are editing hosts");
+
+ document.designMode = "off";
+
+ testSelectorIdsMatch("#set5 :read-write", ["cd1", "p3", "ci3", "ci4", "ct3", "ct4"], "The :read-write pseudo-class must match elements that are inside editing hosts, but not match inputs and textareas inside that aren't");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html
new file mode 100644
index 0000000000..fe3d6e2f42
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional-hidden.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Selector: pseudo-classes (:required, :optional) for hidden input</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#pseudo-classes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ span {
+ color: red;
+ background-color: pink;
+ }
+ :required + span {
+ color: green;
+ }
+ :not(:optional) + span {
+ background-color: lime;
+ }
+</style>
+<input id="hiddenInput" type="hidden" required>
+<span id="sibling">This text should be green on lime background.</span>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(sibling).color, "rgb(255, 0, 0)",
+ "Not matching :required for type=hidden");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(255, 192, 203)",
+ "Matching :optional for type=hidden");
+
+ hiddenInput.type = "text";
+
+ assert_equals(getComputedStyle(sibling).color, "rgb(0, 128, 0)",
+ "Matching :required for type=text");
+ assert_equals(getComputedStyle(sibling).backgroundColor, "rgb(0, 255, 0)",
+ "Matching :not(:optional) for type=text");
+ }, "Evaluation of :required and :optional changes for input type change.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html
new file mode 100644
index 0000000000..f06fdfa1e0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/required-optional.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:required, :optional)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<div id="log"></div>
+<input type=text id=text1 value="foobar" required>
+<input type=text id=text2 required>
+<input type=text id=text3>
+<select id=select1 required>
+ <optgroup label="options" id=optgroup1>
+ <option value="option1" id=option1>option1
+</select>
+<select id=select2>
+ <optgroup label="options" id=optgroup2>
+ <option value="option2" id=option2>option2
+</select>
+<textarea required id=textarea1>textarea1</textarea>
+<textarea id=textarea2>textarea2</textarea>
+
+<script>
+ testSelectorIdsMatch(":required", ["text1", "text2", "select1", "textarea1"], "':required' matches required <input>s, <select>s and <textarea>s");
+ testSelectorIdsMatch(":optional", ["text3", "select2", "textarea2"], "':optional' matches elements <input>s, <select>s and <textarea>s that are not required");
+
+ document.getElementById("text1").removeAttribute("required");
+ testSelectorIdsMatch(":required", ["text2", "select1", "textarea1"], "':required' doesn't match elements whose required attribute has been removed");
+ testSelectorIdsMatch(":optional", ["text1", "text3", "select2", "textarea2"], "':optional' matches elements whose required attribute has been removed");
+
+ document.getElementById("select2").setAttribute("required", "required");
+ testSelectorIdsMatch(":required", ["text2", "select1", "select2", "textarea1"], "':required' matches elements whose required attribute has been added");
+ testSelectorIdsMatch(":optional", ["text1", "text3", "textarea2"], "':optional' doesn't match elements whose required attribute has been added");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js
new file mode 100644
index 0000000000..7a2fb77f10
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/utils.js
@@ -0,0 +1,20 @@
+function getElementsByIds(ids) {
+ var result = [];
+ ids.forEach(function(id) {
+ result.push(document.getElementById(id));
+ });
+ return result;
+}
+
+function testSelectorIdsMatch(selector, ids, testName) {
+ test(function(){
+ var elements = document.querySelectorAll(selector);
+ assert_array_equals([...elements], getElementsByIds(ids));
+ }, testName);
+}
+
+function testSelectorElementsMatch(selector, elements, testName) {
+ test(function(){
+ assert_array_equals([...document.querySelectorAll(selector)], elements);
+ }, testName);
+}
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html
new file mode 100644
index 0000000000..6ad329438a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid-fieldset-disconnected.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:valid, :invalid) on disconnected fieldset element</title>
+<link rel=help href="https://html.spec.whatwg.org/multipage/semantics-other.html#selector-valid">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<fieldset id=fieldset1>
+ <input id=input1>
+</fieldset>
+
+<fieldset id=fieldset2>
+ <select id=select1 required multiple>
+ <option>foo
+ </select>
+</fieldset>
+
+<script>
+test(() => {
+ const fieldset = document.querySelector("#fieldset1");
+ const input = document.querySelector("#input1");
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+
+ fieldset.remove();
+ input.setCustomValidity("foo");
+
+ assert_false(fieldset.matches(":valid"));
+ assert_true(fieldset.matches(":invalid"));
+
+ input.setCustomValidity("");
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+}, "<input> element becomes invalid inside disconnected <fieldset>");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset2");
+ const select = document.querySelector("#select1");
+
+ assert_false(fieldset.matches(":valid"));
+ assert_true(fieldset.matches(":invalid"));
+
+ fieldset.remove();
+ select.required = false;
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+
+ select.required = true;
+ select.firstElementChild.selected = true;
+
+ assert_true(fieldset.matches(":valid"));
+ assert_false(fieldset.matches(":invalid"));
+}, "<select> element becomes valid inside disconnected <fieldset>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html
new file mode 100644
index 0000000000..d93407707f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/selectors/pseudo-classes/valid-invalid.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Selector: pseudo-classes (:valid, :invalid)</title>
+<link rel="author" title="Denis Ah-Kang" href="mailto:denis@w3.org" id=link1>
+<link rel=help href="https://html.spec.whatwg.org/multipage/#pseudo-classes" id=link2>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<style>
+ #styleTests form, #styleTests fieldset, #failExample { background-color:red; }
+ #styleTests > :valid, #validExample { background-color:green; }
+ #styleTests > :invalid, #invalidExample { background-color:lime; }
+</style>
+</head>
+<body>
+<div id="log"></div>
+<div id='simpleConstraints'>
+ <input type=text id=text1 value="foobar" required>
+ <input type=text id=text2 required>
+</div>
+<div id='FormSelection'>
+ <form id=form1>
+ <input type=text id=text3 value="foobar" required>
+ </form>
+ <form id=form2>
+ <input type=text id=text4 required>
+ </form>
+</div>
+<div id='FieldSetSelection'>
+ <fieldset id=fieldset1>
+ <input type=text id=text5 value="foobar" required>
+ </fieldset>
+ <fieldset id=fieldset2>
+ <input type=text id=text6 required>
+ </fieldset>
+</div>
+<div id='patternConstraints'>
+ <input type=text id=text7 value="AAA" pattern="[0-9][A-Z]{3}">
+ <input type=text id=text8 value="0AAA" pattern="[0-9][A-Z]{3}">
+</div>
+<div id='numberConstraints'>
+ <input type=number id=number1 value=0 min=1>
+ <input type=number id=number2 value=1 min=1>
+</div>
+<div id='styleTests'>
+ <form>
+ </form>
+ <form>
+ <input type=text min=8 value=4>
+ </form>
+ <form>
+ <input type=number min=8 value=4>
+ </form>
+ <fieldset>
+ </fieldset>
+ <fieldset>
+ <input type=text min=8 value=4>
+ </fieldset>
+ <fieldset>
+ <input type=number min=8 value=4>
+ </fieldset>
+ <div id='validExample'></div>
+ <div id='invalidExample'></div>
+ <div id='failExample'></div>
+</div>
+<script>
+ testSelectorIdsMatch("#simpleConstraints :valid", ["text1"], "':valid' matches elements that satisfy their constraints");
+
+ testSelectorIdsMatch("#FormSelection :valid", ["form1", "text3"], "':valid' matches form elements that are not the form owner of any elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#FieldSetSelection :valid", ["fieldset1", "text5"], "':valid' matches fieldset elements that have no descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#patternConstraints :valid", [ "text8" ], "':valid' matches elements that satisfy their pattern constraints");
+
+ testSelectorIdsMatch("#numberConstraints :valid", [ "number2" ], "':valid' matches elements that satisfy their number constraints");
+
+
+ testSelectorIdsMatch("#simpleConstraints :invalid", ["text2"], "':invalid' matches elements that do not satisfy their simple text constraints");
+
+ testSelectorIdsMatch("#FormSelection :invalid", ["form2", "text4"], "':invalid' matches form elements that are the form owner of one or more elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#FieldSetSelection :invalid", ["fieldset2", "text6"], "':invalid' matches fieldset elements that have of one or more descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints");
+
+ testSelectorIdsMatch("#patternConstraints :invalid", ["text7"], "':invalid' matches elements that do not satisfy their pattern constraints");
+
+ testSelectorIdsMatch("#numberConstraints :invalid", ["number1"], "':invalid' matches elements that do not satisfy their number constraints");
+
+ document.getElementById("text7").value="0BBB";
+ testSelectorIdsMatch("#patternConstraints :valid", [ "text7", "text8" ], "':valid' matches new elements that satisfy their constraints");
+ testSelectorIdsMatch("#patternConstraints :invalid", [], "':invalid' doesn't match new elements that satisfy their constraints");
+
+ document.getElementById("text8").value="BBB";
+ testSelectorIdsMatch("#patternConstraints :valid", ["text7"], "':valid' doesn't match new elements that do not satisfy their constraints");
+ testSelectorIdsMatch("#patternConstraints :invalid", ["text8"], "':invalid' matches new elements that do not satisfy their constraints");
+
+ function getBGColor(elem) {
+ return getComputedStyle(elem).backgroundColor;
+ }
+
+ function testStyles(type) {
+ var elems = document.querySelectorAll("#styleTests " + type),
+ empty = elems[0],
+ valid = elems[1],
+ invalid = elems[2],
+ validInput = valid.querySelector("input"),
+ invalidInput = invalid.querySelector("input"),
+ expectedValidBGColor = getBGColor(document.getElementById("validExample")),
+ expectedInvalidBGColor = getBGColor(document.getElementById("invalidExample")),
+ expectedFailBGColor = getBGColor(document.getElementById("failExample"));
+
+ test(function() {
+ assert_equals(getBGColor(empty), expectedValidBGColor, "wrong background-color");
+ }, 'empty ' + type + ' correctly styled on page-load');
+
+ test(function() {
+ assert_equals(getBGColor(valid), expectedValidBGColor, "wrong background-color");
+ }, 'valid ' + type + ' correctly styled on page-load');
+ test(function() {
+ assert_equals(getBGColor(invalid), expectedInvalidBGColor, "wrong background-color");
+ }, 'invalid ' + type + ' correctly styled on page-load');
+
+ test(function() {
+ empty.appendChild(validInput.cloneNode());
+ assert_equals(getBGColor(empty), expectedValidBGColor, "wrong background-color");
+ }, 'programmatically adding valid to empty ' + type + ' results in correct style');
+ test(function() {
+ empty.appendChild(invalidInput.cloneNode());
+ assert_equals(getBGColor(empty), expectedInvalidBGColor, "wrong background-color");
+ }, 'programmatically adding invalid to empty ' + type + ' results in correct style');
+
+ validInput.type = "number";
+ invalidInput.type = "text";
+ test(function() {
+ assert_equals(getBGColor(valid), expectedInvalidBGColor, "wrong background-color");
+ }, 'programmatically-invalidated ' + type + ' correctly styled');
+ test(function() {
+ assert_equals(getBGColor(invalid), expectedValidBGColor, "wrong background-color");
+ }, 'programmatically-validated ' + type + ' correctly styled');
+ }
+ test(testStyles.bind(undefined, "form"), ":valid/:invalid styling for <form>");
+ test(testStyles.bind(undefined, "fieldset"), ":valid/:invalid styling for <fieldset>");
+</script>
+</body>
+</html>