summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/semantics/forms/the-selectlist-element
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/semantics/forms/the-selectlist-element')
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html6
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html119
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html51
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html45
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html244
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html231
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html39
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html57
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html182
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html142
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html29
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html17
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html16
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html35
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html140
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html19
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html23
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html94
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html44
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html67
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html25
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html49
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html16
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html495
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html135
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html115
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html102
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html47
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html61
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html10
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html14
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html12
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html13
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html11
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html28
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html33
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html9
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html63
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html88
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html20
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html224
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html8
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html4
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html2
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js112
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg3
-rw-r--r--testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html43
77 files changed, 3728 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html
new file mode 100644
index 0000000000..fd7f2b7bca
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<button>button</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html
new file mode 100644
index 0000000000..3ab7044366
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/button-type-selectlist-appearance.tentative.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="button-type-selectlist-appearance-ref.html">
+
+<button type=selectlist>button</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html
new file mode 100644
index 0000000000..7cf2aff515
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-ask-for-reset.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: ask-for-reset</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form name="fm1" id="form1">
+ <selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ </selectlist>
+
+ <selectlist id="selectlist2">
+ <option>one</option>
+ <option selected>two</option>
+ </selectlist>
+
+ <selectlist id="selectlist3">
+ <option>one</option>
+ <option selected>two</option>
+ <option selected>three</option>
+ </selectlist>
+</form>
+
+<script>
+function createSelectList(numberOfOptions) {
+ let selectList = document.createElement("selectlist");
+ for (let i = 0; i < numberOfOptions; i++) {
+ let option = document.createElement("option");
+ option.value = i;
+ selectList.appendChild(option);
+ }
+ return selectList;
+}
+
+function checkSelection(selectList, selectedOptionIndex, msg) {
+ for (let i = 0; i < selectList.children.length; i++) {
+ if (i != selectedOptionIndex) {
+ assert_false(selectList.children[i].selected);
+ }
+ }
+ assert_true(selectList.children[selectedOptionIndex].selected, msg);
+ assert_equals(selectList.value, selectList.children[selectedOptionIndex].value);
+}
+
+test(() => {
+ let selectList = createSelectList(5);
+
+ selectList.children[4].selected = true;
+ checkSelection(selectList, 4);
+
+ selectList.children[4].remove();
+ checkSelection(selectList, 0, "After removing the selected option, selection should default to first option.");
+
+ selectList.children[3].selected = true;
+ checkSelection(selectList, 3);
+ selectList.children[0].remove();
+ checkSelection(selectList, 2, "Removing non-selected option should have no effect.");
+}, "ask-for-reset when removing option");
+
+test(() => {
+ let selectList = createSelectList(3);
+ selectList.children[1].selected = true;
+
+ let newOption = document.createElement("option");
+ newOption.selected = true;
+ selectList.appendChild(newOption);
+ checkSelection(selectList, 3, "Inserting a selected option should update selection.");
+
+ let newOption2 = document.createElement("option");
+ newOption2.selected = true;
+ selectList.prepend(newOption2);
+ checkSelection(selectList, 0, "Inserting a selected option should update selection, even though it's not last in tree order.");
+
+ let newOption3 = document.createElement("option");
+ selectList.appendChild(newOption3);
+ checkSelection(selectList, 0, "Inserting a non-selected option should have no effect.");
+}, "ask-for-reset when inserting option");
+
+test(() => {
+ let selectList = createSelectList(3);
+ let options = selectList.children;
+
+ // select options from first to last
+ for (let i = 0; i < options.length; i++) {
+ options[i].selected = true;
+ checkSelection(selectList, i);
+ }
+
+ // select options from last to first
+ for (let i = options.length - 1; i >= 0; i--) {
+ options[i].selected = true;
+ checkSelection(selectList, i);
+ }
+
+ options[2].selected = true;
+ checkSelection(selectList, 2);
+ options[2].selected = false;
+ checkSelection(selectList, 0, "First non-disabled option should be selected.");
+
+ options[0].disabled = true;
+ options[2].selected = true;
+ checkSelection(selectList, 2);
+ options[2].selected = false;
+ checkSelection(selectList, 1, "First non-disabled option should be selected.");
+}, "ask-for-reset when changing selectedness of option");
+
+test(() => {
+ let selectList1 = document.getElementById("selectlist1");
+ let selectList2 = document.getElementById("selectlist2");
+ let selectList3 = document.getElementById("selectlist3");
+
+ document.getElementById("form1").reset();
+
+ assert_equals(selectList1.value, "one", "First non-disabled option should be selected.");
+ assert_equals(selectList2.value, "two", "The selected option should be selected.");
+ assert_equals(selectList3.value, "three", "Last selected option should be selected.")
+}, "ask-for-reset for form");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html
new file mode 100644
index 0000000000..d3e6febd78
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-closes-listbox.tentative.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1408838">
+<link rel=help href="https://github.com/openui/open-ui/issues/639">
+<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>
+
+<selectlist id=defaultbutton-defaultlistbox>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=custombutton-defaultlistbox>
+ <button type=selectlist>custom button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=defaultbutton-customlistbox>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id=custombutton-customlistbox>
+ <button type=selectlist>custom button</button>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+document.querySelectorAll('selectlist').forEach(selectlist => {
+ promise_test(async () => {
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should not be showing at the start of the test.');
+
+ await test_driver.click(selectlist);
+ assert_true(selectlist.matches(':open'),
+ 'The listbox should be showing after clicking the button.');
+
+ await test_driver.click(selectlist);
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should be closed after clicking the button.');
+ }, `${selectlist.id}: Clicking the selectlist's button should toggle the listbox.`);
+});
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html
new file mode 100644
index 0000000000..37d4b1b471
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<button>hello world</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html
new file mode 100644
index 0000000000..50ad339b05
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-appearance.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-button-type-appearance-ref.html">
+
+<selectlist>
+ <button type=selectlist>hello world</button>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html
new file mode 100644
index 0000000000..cc7eb62a63
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-button-type-behavior.tentative.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<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>
+
+<selectlist>
+ <button type=selectlist id=b1>first button</button>
+ <button type=selectlist id=b2>second button</button>
+ <button id=b3>third button</button>
+ <option>option</option>
+</selectlist>
+
+<script>
+const ESC = '\uE00C'
+
+promise_test(async () => {
+ const selectlist = document.querySelector('selectlist');
+ const b1 = document.getElementById('b1');
+ const b2 = document.getElementById('b2');
+ const b3 = document.getElementById('b3');
+
+ assert_false(selectlist.open, 'The selectlist should start closed.');
+ await test_driver.click(b1);
+ assert_true(selectlist.open, 'The selectlist should get opened when the button is clicked.');
+
+ await test_driver.send_keys(selectlist, ESC);
+ assert_false(selectlist.open, 'Pressing escape should close the selectlist.');
+
+ await test_driver.click(b2);
+ assert_true(selectlist.open, 'The selectlist should get opened when a second type=selectlist button is clicked.');
+ await test_driver.send_keys(selectlist, ESC);
+ assert_false(selectlist.open, 'Pressing escape should close the selectlist.');
+
+ await test_driver.click(b3);
+ assert_false(selectlist.open, 'Clicking a button witout type=selectlist should not open the listbox.');
+
+ b1.removeAttribute('type');
+ await test_driver.click(b1);
+ assert_false(selectlist.open, 'If the button is not type=selectlist, it should not open the selectlist.');
+}, '<button type=selectlist> should open the parent selectlist when clicked.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html
new file mode 100644
index 0000000000..faa96e1f27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<style>
+.container {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+}
+</style>
+<div class=container>
+ <div>first child</div>
+ <div>second child</div>
+</div>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html
new file mode 100644
index 0000000000..d8688d5957
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-default-button-slot.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-default-button-slot-ref.html">
+<meta rel=assert title="Child nodes of selectlist should be slotted into the button slot by default">
+
+<selectlist>
+ <div>first child</div>
+ <div>second child</div>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html
new file mode 100644
index 0000000000..f557804d9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-disabled.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<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>
+
+<script>
+focused_element_id = null;
+
+function OnFocus(event) {
+ focused_element_id = event.target.id;
+}
+</script>
+
+<selectlist id="selectlist" onfocus="OnFocus(event)" disabled>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+promise_test(async () => {
+ const selectlist = document.getElementById("selectlist");
+ selectlist.focus();
+ assert_equals(focused_element_id, null);
+}, "Check that disabled <selectlist> cannot be focused");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html
new file mode 100644
index 0000000000..a88a3b1f7d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-events.tentative.html
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: events</title>
+<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>
+
+<selectlist id="selectList0">
+ <div slot="button" behavior="button">
+ <span behavior="selected-value"></span>
+ <button id="selectList0-button">selectList0-button</button>
+ </div>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <option>one</option>
+ <option>
+ two
+ <button id="selectList1-button">selectList1-button</button>
+ </option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectList3">
+ <option>same</option>
+ <option>same</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <option>one</option>
+ <option id="selectList4-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList5WithTabIndex" tabindex="1">
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<input id="input6"/>
+<selectlist id="selectList7">
+ <button slot="button" behavior="button" id="selectList7-button">
+ selectList7-button
+ </button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList0");
+ const selectListButton = document.getElementById("selectList0-button");
+ assert_false(selectList.open);
+ const selectListButtonPromise = new Promise(async resolve => {
+ selectListButton.addEventListener("click", (e) => {
+ assert_false(selectList.open, "Listbox shouldn't have opened yet");
+ // PreventDefaulting the event here should prevent UA controller code
+ // on the button part from opening the listbox.
+ e.preventDefault();
+ resolve();
+ });
+ });
+
+ const selectListPromise = new Promise(async resolve => {
+ selectList.addEventListener("click", (e) => {
+ assert_true(e.defaultPrevented, "Event should have been defaultPrevented by selectListButton click handler");
+ assert_false(selectList.open, "Listbox shouldn't have opened, because click event was defaultPrevented.");
+ resolve();
+ });
+ });
+
+ await clickOn(selectListButton);
+ return Promise.all([selectListButtonPromise, selectListPromise]);
+ }, "Button controller code should not run if the click event is preventDefaulted.");
+
+ // See https://w3c.github.io/webdriver/#keyboard-actions
+ const KEY_CODE_MAP = {
+ 'Tab': '\uE004',
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015',
+ };
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList1");
+ const selectListButton = document.getElementById("selectList1-button");
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ const selectListButtonPromise = new Promise(async resolve => {
+ selectListButton.addEventListener("click", (e) => {
+ assert_true(selectList.open, "Listbox shouldn't have closed yet");
+ // PreventDefaulting the event here should prevent UA controller code
+ // on the listbox part from selecting the option and closing the listbox.
+ e.preventDefault();
+ resolve();
+ });
+ });
+
+ const selectListPromise = new Promise(async resolve => {
+ selectList.addEventListener("click", (e) => {
+ assert_true(e.defaultPrevented, "Event should have been defaultPrevented by selectListButton click handler");
+ assert_true(selectList.open, "Listbox shouldn't have closed, because keydown event was defaultPrevented.");
+ assert_equals(selectList.value, "one", "<selectlist> shouldn't have changed value, because keydown event was defaultPrevented.");
+ resolve();
+ });
+ });
+
+ await clickOn(selectListButton);
+ return Promise.all([selectListButtonPromise, selectListPromise]);
+ }, "Listbox controller code should not run if the click event is preventDefaulted.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList2");
+ const events = [];
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed.");
+ events.push('input');
+ });
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed.");
+ events.push('change');
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open);
+ assert_equals(selectList.value, "one");
+ assert_array_equals(events, [], "input and change shouldn't fire if value wansn't changed.");
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(selectList.value, "one", "value shouldn't change when user switches options with arrow key.");
+ assert_array_equals(events, ['input'], "input event should fire when user switches options with arrow key.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_equals(selectList.value, "two");
+ assert_array_equals(events, ['input', 'input', 'change'], "input and change should fire after pressing enter.");
+ }, "<selectlist> should fire input and change events when new option is selected.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList3");
+ const events = [];
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed.");
+ events.push('input');
+ });
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed.");
+ events.push('change');
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_array_equals(events, ['input'], "input event should have fired after ArrowDown.");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_array_equals(events, ['input', 'input', 'change'], "input and change should fire after pressing Enter.");
+ }, "<selectlist> should fire input and change events even when new selected option has the same value as the old.");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList4");
+ const selectListOption2 = document.getElementById("selectList4-option2");
+ let input_event_count = 0;
+ let change_event_count = 0;
+
+ selectList.addEventListener("input", (e) => {
+ assert_true(e.composed, "input event should be composed");
+ assert_equals(input_event_count, 0, "input event should not fire twice");
+ assert_equals(change_event_count, 0, "input event should not fire before change");
+ input_event_count++;
+ });
+
+ selectList.addEventListener("change", (e) => {
+ assert_false(e.composed, "change event should not be composed");
+ assert_equals(input_event_count, 1, "change event should fire after input");
+ assert_equals(change_event_count, 0, "change event should not fire twice");
+ change_event_count++;
+ });
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(input_event_count, 1, "input event shouldn't fire when selected option didn't change");
+ assert_equals(change_event_count, 1, "change event shouldn't fire when selected option didn't change");
+ }, "<selectlist> should fire input and change events when option in listbox is clicked");
+
+ promise_test(async() => {
+ const selectList = document.getElementById("selectList2");
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "<Space> should open selectlist");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "<Enter> should close selectlist");
+ }, "Check that <Space> opens <selectlist>.");
+
+ promise_test(async() => {
+ const selectList = document.getElementById("selectList5WithTabIndex");
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "<Space> should open selectlist");
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "<Enter> should close selectlist");
+ }, "Check that <Space> opens <selectlist> when <selectlist> specifies tabindex");
+
+ promise_test(async() => {
+ const input6 = document.getElementById("input6");
+ const selectList = document.getElementById("selectList7");
+ const selectListButton = document.getElementById("selectList7-button")
+
+ var keydown_count = 0;
+ selectListButton.addEventListener("keydown", (e) => {
+ keydown_count++;
+ });
+
+ // Focus selectlist via Tab traversal because focus() does not work when selectlist
+ // has custom slot.
+ // TODO(http://crbug.com/1440573) Fix this.
+ await test_driver.send_keys(input6, KEY_CODE_MAP.Tab);
+
+ await test_driver.send_keys(selectList, "a");
+ assert_equals(keydown_count, 1, "button in shadowroot should have observed keydown");
+}, "Test that <selectlist> button slot receives key events.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html
new file mode 100644
index 0000000000..46d932c4b2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ const button = selectlist.querySelector('.fake-selectlist-internal-selectlist-button');
+ button.style.width = "400px";
+ button.style.height = "50px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html
new file mode 100644
index 0000000000..d9e52a9fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-explicit-size.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist respects explicit size -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-explicit-size-ref.tentative.html">
+
+<style>
+selectlist {
+ width:400px;
+ height:50px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html
new file mode 100644
index 0000000000..46d6dbdb9b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size-ref.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ selectlist.style.fontSize = "48px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html
new file mode 100644
index 0000000000..9c30e71bda
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-font-size.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist respects explicit size -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-font-size-ref.tentative.html">
+
+<style>
+selectlist {
+ font-size:48px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html
new file mode 100644
index 0000000000..c1872b9303
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-attribute.tentative.html
@@ -0,0 +1,231 @@
+<!-- This test is tentative until the <selectlist> gets adopted by standards. When that happens,
+ this test can be folded back into these tests:
+ html/semantics/forms/form-control-infrastructure/form_attribute.html
+ html/semantics/forms/form-control-infrastructure/form.html -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div>
+ <form id="f1"></form>
+ <form id="f2">
+ <input id="i1" />
+ <input id="i2" form="f1" />
+ <input id="i3" />
+ </form>
+ <script>
+ test(function() {
+ var i1 = document.getElementById("i1");
+ var i2 = document.getElementById("i2");
+ var i3 = document.getElementById("i3");
+ var f1 = document.getElementById("f1");
+ var f2 = document.getElementById("f2");
+
+ assert_equals(i1.form, f2,
+ "i1 must be associated with f2 by the parser");
+ assert_equals(i2.form, f1,
+ "i2 is not associated with f2 by the parser " +
+ "since it has the form attribute set to f1");
+
+ f1.appendChild(i1);
+ i3.setAttribute("form", "f1");
+
+ assert_equals(i1.form, f1,
+ "i1's form owner must be reset when parent changes");
+ assert_equals(i3.form, f1,
+ "i3's form owner must be reset when the form" +
+ "attribute is set");
+
+ assert_equals(i2.form, f1);
+ }, "Tests for parser inserted controls");
+ </script>
+ </div>
+
+ <div id="placeholder">
+ </div>
+
+ <script>
+ var reassociateableElements = [
+ "selectlist",
+ ];
+
+ var form1 = null;
+ var form2 = null;
+ var placeholder = document.getElementById("placeholder");
+
+ reassociateableElements.forEach(function(localName) {
+ function testControl(test_, desc) {
+ test(function() {
+ var control = document.createElement(localName);
+
+ while(placeholder.firstChild)
+ placeholder.removeChild(placeholder.firstChild);
+
+ form1 = document.createElement("form");
+ form2 = document.createElement("form");
+ form1.id = "form1";
+ form2.id = "form2";
+ placeholder.appendChild(form1);
+ placeholder.appendChild(form2);
+
+ test_.call(control);
+ }, "[" + localName.toUpperCase() + "] " + desc);
+ }
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Basic form association - control with no form attribute is associated with ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.id = "form-one";
+ assert_equals(this.form, null);
+ }, "Form owner is reset to null when control's form attribute is set to an ID " +
+ "that does not exist in the document");
+
+ testControl(function() {
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner");
+
+ testControl(function() {
+ form1.id = "";
+ this.setAttribute("form", "");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control whose form attribute is an empty string has no form owner " +
+ "even when form with empty attribute is present");
+
+ testControl(function() {
+ form1.id = "FORM1";
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, null);
+ }, "Control's form attribute must be a case sensitive match for the form's id");
+
+ testControl(function() {
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+ }, "Setting the form attribute of a control to the id of a non-ancestor form works");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ this.removeAttribute("form");
+ assert_equals(this.form, form2);
+ }, "Removing form id from a control resets the form owner to ancestor");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ placeholder.removeChild(form1);
+
+ assert_equals(this.form, null);
+ }, "Removing the form owner of a control with form attribute resets " +
+ "the form owner to null");
+
+ testControl(function() {
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+ placeholder.appendChild(form3);
+ form3.appendChild(this);
+ assert_equals(this.form, form3);
+
+ this.setAttribute("form", "form2");
+ assert_equals(this.form, form2);
+
+ this.setAttribute("form", "form1");
+ assert_equals(this.form, form1);
+ }, "Changing form attibute of control resets form owner to correct form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ var form3 = document.createElement("form");
+ form3.id = "form3";
+
+ placeholder.appendChild(form3);
+ placeholder.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+ }, "Moving a control with form attribute within the document " +
+ "does not change the form owner");
+
+ testControl(function() {
+ form1.id = "form-one";
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, null);
+
+ form1.id = "form1";
+ assert_equals(this.form, form1);
+ }, "When the id of a non-ancestor form changes from not being a match for the " +
+ "form attribute to being a match, the control's form owner is reset");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form2.id = "form1";
+ form1.parentNode.insertBefore(form2, form1);
+ assert_equals(this.form, form2);
+
+ form2.parentNode.removeChild(form2);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.appendChild(form2);
+ assert_equals(this.form, form1);
+ }, "When form element with same ID as the control's form attribute is inserted " +
+ "earlier in tree order, the form owner is changed to the inserted form");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form2.appendChild(this);
+ assert_equals(this.form, form1);
+
+ var span = document.createElement("span");
+ span.id = "form1";
+ form1.parentNode.insertBefore(span, form1);
+ assert_equals(this.form, null);
+
+ form1.parentNode.appendChild(span);
+ assert_equals(this.form, form1);
+ }, "When non-form element with same ID as the control's form attribute is " +
+ "inserted earlier in tree order, the control does not have a form owner");
+
+ testControl(function() {
+ this.setAttribute("form", "form1");
+ form1.appendChild(this);
+ assert_equals(this.form, form1);
+
+ form1.parentNode.removeChild(form1);
+ assert_equals(this.form, form1);
+ }, "A control that is not in the document but has the form attribute set " +
+ "is associated with the nearest ancestor form if one exists");
+
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html
new file mode 100644
index 0000000000..07e5be6ef4
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-elements.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form.elements</title>
+<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>
+
+<form id="form0">
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<form>
+
+<script>
+promise_test(async t => {
+ // TODO: Move test to /the-form-element/form-elements-filter.html once
+ // <selectlist> becomes part of the HTML spec.
+ const formElements = document.querySelector("#form0").elements;
+ assert_equals(formElements.length, 1);
+ assert_equals(formElements[0].id, "selectlist0");
+}, "Check that <selectlist> is exposed in form.elements");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html
new file mode 100644
index 0000000000..f98494a950
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-state-restore.tentative.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form state restore</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<input id="emptyOnFirstVisit">
+<form action="support/back.html" id="form0">
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+</form>
+
+<script>
+async_test(t => {
+ window.onload = () => t.step_timeout(() => {
+ let state = document.getElementById('emptyOnFirstVisit');
+ let selectList = document.getElementById("selectlist0");
+
+ if (!state.value) {
+ // First visit.
+ t.step_timeout(() => {
+ state.value = 'visited';
+ assert_equals(selectList.value, "one");
+ selectList.value = "two";
+ // The form is submitted in a timeout to make sure that a new back/forward list item is created.
+ document.getElementById('form0').submit();
+ }, 0);
+ } else {
+ // Went back to this page again, and the form state should be restored.
+ assert_equals(selectList.value, "two");
+ t.done();
+ }
+ }, 1);
+}, "Test restoring state after form submission");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html
new file mode 100644
index 0000000000..4b5e497028
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-form-submission.tentative.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: form submission</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form0">
+ <selectlist name="s0" id="selectlist0">
+ <option selected>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+</form>
+
+<form id="form1">
+ <input type="text" name="i1" value="test">
+ <selectlist id="selectlist1">
+ <option selected>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+</form>
+
+<script>
+
+test(() => {
+ const form0 = document.getElementById("form0");
+ const selectList0 = document.getElementById("selectlist0");
+ assert_equals(selectList0.value, "one");
+
+ const formData = new FormData(form0);
+ let entries = 0;
+ for (let entry of formData.entries()) {
+ assert_equals(entry[0], "s0");
+ assert_equals(entry[1], "one");
+ entries++;
+ }
+ assert_equals(entries, 1);
+}, "Test that HTMLSelectList.value is used for form submission");
+
+test(() => {
+ const form1 = document.getElementById("form1");
+ const selectList1 = document.getElementById("selectlist1");
+ assert_equals(selectList1.value, "one");
+
+ const formData = new FormData(form1);
+ let entries = 0;
+ for (let entry of formData.entries()) {
+ assert_equals(entry[0], "i1");
+ assert_equals(entry[1], "test");
+ entries++;
+ }
+ assert_equals(entries, 1);
+}, "Test that HTMLSelectList.value is not used for form submission without name attribute");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html
new file mode 100644
index 0000000000..a70ab7e6a5
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard-behavior.tentative.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1422275">
+<link rel=help href="https://github.com/openui/open-ui/issues/433#issuecomment-1452461404">
+<link rel=help href="https://github.com/openui/open-ui/issues/386#issuecomment-1452469497">
+<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>
+<script src="/resources/testdriver-actions.js"></script>
+
+<form></form>
+
+<div id=notform>
+ <selectlist id=defaultbutton>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+
+ <selectlist id=custombutton>
+ <button type=selectlist>custom button</button>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+</div>
+
+<script>
+const Enter = '\uE007';
+const Escape = '\uE00C';
+const ArrowLeft = '\uE012';
+const ArrowUp = '\uE013';
+const ArrowRight = '\uE014';
+const ArrowDown = '\uE015';
+const Space = ' ';
+const form = document.querySelector('form');
+const notform = document.getElementById('notform');
+
+for (const id of ['defaultbutton', 'custombutton']) {
+ const selectlist = document.getElementById(id);
+
+ async function closeListbox() {
+ await test_driver.click(selectlist);
+ }
+
+ function addCloseCleanup(t) {
+ t.add_cleanup(async () => {
+ if (selectlist.matches(':open')) {
+ await closeListbox();
+ }
+ if (selectlist.matches(':open')) {
+ throw new Error('selectlist failed to close!');
+ }
+ selectlist.value = 'one';
+ });
+ }
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed,
+ // then we shouldn't need to explicitly focus the custom button like this
+ // anymore.
+ const customButton = selectlist.querySelector('button');
+ if (customButton) {
+ customButton.focus();
+ } else {
+ selectlist.focus();
+ }
+ assert_false(selectlist.matches(':open'),
+ 'The selectlist should initially be closed.');
+ await test_driver.send_keys(selectlist, Space);
+ assert_true(selectlist.matches(':open'),
+ 'The selectlist should be open after pressing space.');
+ }, `${id}: When the listbox is closed, spacebar should open the listbox.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ selectlist.value = 'two';
+ selectlist.focus();
+ assert_false(selectlist.matches(':open'),
+ 'The selectlist should initially be closed.');
+
+ await test_driver.send_keys(selectlist, ArrowLeft);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow left should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow left should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowUp);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow up should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow up should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowRight);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow right should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow right should not change the selected value.');
+ await closeListbox();
+
+ await test_driver.send_keys(selectlist, ArrowDown);
+ assert_true(selectlist.matches(':open'),
+ 'Arrow down should open the listbox.');
+ assert_equals(selectlist.value, 'two',
+ 'Arrow down should not change the selected value.');
+ }, `${id}: When the listbox is closed, all arrow keys should open the listbox.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+
+ // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed,
+ // then we shouldn't need to explicitly use the custom button like this
+ // anymore.
+ const customButton = selectlist.querySelector('button');
+ if (customButton) {
+ await test_driver.send_keys(customButton, Enter);
+ } else {
+ await test_driver.send_keys(selectlist, Enter);
+ }
+ assert_false(selectlist.matches(':open'),
+ 'Enter should not open the listbox when outside a form.');
+
+ form.appendChild(selectlist);
+ let formWasSubmitted = false;
+ form.addEventListener('submit', event => {
+ event.preventDefault();
+ formWasSubmitted = true;
+ }, {once: true});
+ if (customButton) {
+ await test_driver.send_keys(customButton, Enter);
+ } else {
+ await test_driver.send_keys(selectlist, Enter);
+ }
+ assert_true(formWasSubmitted,
+ 'Enter should submit the form when the listbox is closed.');
+ assert_false(selectlist.matches(':open'),
+ 'Enter should not open the listbox when it is in a form.');
+ }, `${id}: When the listbox is closed, the enter key should submit the form or do nothing.`);
+
+ promise_test(async t => {
+ addCloseCleanup(t);
+ const optionOne = selectlist.querySelector('.one');
+ const optionTwo = selectlist.querySelector('.two');
+ const optionThree = selectlist.querySelector('.three');
+
+ selectlist.value = 'two';
+ await test_driver.click(selectlist);
+ assert_true(selectlist.matches(':open'),
+ 'The selectlist should open when clicked.');
+ assert_equals(document.activeElement, optionTwo,
+ 'The selected option should receive initial focus.');
+
+ await test_driver.send_keys(document.activeElement, ArrowDown);
+ assert_equals(document.activeElement, optionThree,
+ 'The next option should receive focus when the down arrow key is pressed.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, ArrowUp);
+ assert_equals(document.activeElement, optionTwo,
+ 'The previous option should receive focus when the up arrow key is pressed.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, ArrowUp);
+ assert_equals(document.activeElement, optionOne,
+ 'The first option should be selected.');
+ assert_equals(selectlist.value, 'two',
+ 'The selectlists value should not change when focusing another option.');
+
+ await test_driver.send_keys(document.activeElement, Enter);
+ assert_false(selectlist.matches(':open'),
+ 'The listbox should be closed after pressing enter.');
+ assert_equals(selectlist.value, 'one',
+ 'The selectlists value should change after pressing enter on a different option.');
+ }, `${id}: When the listbox is open, the enter key should commit the selected option.`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html
new file mode 100644
index 0000000000..5d1c65e941
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-keyboard.tentative.html
@@ -0,0 +1,142 @@
+<!doctype html>
+<title>HTMLSelectListElement Test: keyboard accessibility</title>
+<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>
+ <selectlist id="selectList0">
+ <button id="selectList0-button0" type=selectlist>button</button>
+ <option class=one>one</option>
+ <option class=two>two</option>
+ <option class=three>three</option>
+ </selectlist>
+
+ <selectlist id="selectList1">
+ <option id="selectList1-child0">one</option>
+ </selectlist>
+
+ <selectlist id="selectList2" disabled>
+ <button id="selectList2-button0" type=selectlist>button</button>
+ <option disabled>one</option>
+ <option>two</option>
+ <option>three</option>
+ </selectlist>
+
+ <selectlist id="selectList3">
+ <button id="selectList3-button0" type=selectlist>button</button>
+ <option class=one>one</option>
+ <option disabled>two</option>
+ <option class=three>three</option>
+ </selectlist>
+<script>
+// See https://w3c.github.io/webdriver/#keyboard-actions
+const KEY_CODE_MAP = {
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015'
+};
+
+function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+promise_test(async t => {
+ const selectList = document.querySelector("#selectList0");
+ const button = document.querySelector("#selectList0-button0");
+ assert_false(selectList.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(button, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key shouldn't open selectlist");
+ await test_driver.send_keys(button, KEY_CODE_MAP.Space);
+ assert_true(selectList.open, "Space key should open selectlist");
+ assert_equals(selectList.value, "one");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.two'),
+ "Down arrow should focus the next option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.three'),
+ "Down arrow should focus the next option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList.querySelector('.three'),
+ "Down arrow should do nothing if already at the last option.");
+ assert_equals(selectList.value, "one", "Down arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.two'),
+ "Up arrow should focus the previous option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.one'),
+ "Up arrow should focus the previous option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList.querySelector('.one'),
+ "Up arrow should do nothing if already at the first option.");
+ assert_equals(selectList.value, "one", "Up arrow should not commit the newly focused option.");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key should close selectlist");
+
+ await test_driver.send_keys(button, KEY_CODE_MAP.Space);
+ assert_true(selectList.open, "Space key should open selectlist");
+
+ // This behavior is suspicious (since Space key can open the selectlist),
+ // but it maches <select>. See https://github.com/openui/open-ui/issues/386
+ await test_driver.send_keys(selectList, " ");
+ assert_true(selectList.open, "Space key should *not* close selectlist");
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_false(selectList.open, "Enter key should close selectlist");
+}, "Validate Enter, Up/Down Arrow, and Space keyboard accessibility support for <selectlist>");
+
+promise_test(async t => {
+ const selectListOption = document.getElementById("selectList1-child0");
+ const event = document.createEvent("Event");
+ event.initEvent("keydown");
+ selectListOption.dispatchEvent(event);
+}, "Firing a synthetic event at a selectlist's option doesn't crash");
+
+promise_test(async t => {
+ const selectList2 = document.querySelector("#selectList2");
+ const selectList2Button = document.querySelector("#selectList2-button0");
+ assert_false(selectList2.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(selectList2Button, KEY_CODE_MAP.Enter);
+ assert_false(selectList2.open, "Enter key should not open a disabled selectlist");
+ await clickOn(selectList2);
+ assert_false(selectList2.open, "Click should not open a disabled selectlist");
+ assert_equals(selectList2.value, "one");
+
+ const selectList3 = document.querySelector("#selectList3");
+ const selectList3Button = document.querySelector("#selectList3-button0");
+ assert_false(selectList3.open, "selectlist should not be initially open");
+
+ await test_driver.send_keys(selectList3Button, KEY_CODE_MAP.Enter);
+ assert_false(selectList3.open, "Enter key shouldn't open selectlist");
+
+ await test_driver.send_keys(selectList3Button, KEY_CODE_MAP.Space);
+ assert_true(selectList3.open, "Space key should open selectlist");
+ assert_equals(selectList3.value, "one");
+
+ await test_driver.send_keys(selectList3, KEY_CODE_MAP.ArrowDown);
+ assert_equals(document.activeElement, selectList3.querySelector('.three'),
+ "Down arrow should go to next non-disabled option");
+
+ await test_driver.send_keys(selectList3, KEY_CODE_MAP.ArrowUp);
+ assert_equals(document.activeElement, selectList3.querySelector('.one'),
+ "Up arrow should go to the previous non-disabled option");
+}, "Validate Enter, Up/Down Arrow keyboard accessibility support for disabled <selectlist>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html
new file mode 100644
index 0000000000..819da49fdd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-labels.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: labels</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<label id="label1" for="selectlist1">Label 1</label>
+<selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+<label id="label2" for="selectlist1">Label 2</label>
+<label id="label3" for="invalid-selectlist1">Label 3</label>
+
+<script>
+
+test(() => {
+ let selectList = document.getElementById("selectlist1");
+
+ assert_true(selectList.labels instanceof NodeList);
+ assert_equals(selectList.labels.length, 2);
+ assert_equals(selectList.labels[0], document.getElementById("label1"));
+ assert_equals(selectList.labels[1], document.getElementById("label2"));
+}, "Validate selectlist.labels");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html
new file mode 100644
index 0000000000..5533a97f60
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+const selectlist = createFakeSelectlist('button', /*includeListbox=*/true);
+document.body.appendChild(selectlist);
+
+selectlist.querySelector('button').remove();
+selectlist.insertAdjacentHTML('afterbegin', `<button>button</button>`);
+
+const listbox = selectlist.querySelector('.fake-selectlist-listbox');
+listbox.innerHTML = `
+ <option id=optone tabindex=0>one</option>
+ <option>two</option>
+`;
+document.getElementById('optone').focus();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html
new file mode 100644
index 0000000000..14223633b9
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-element.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-listbox-element-ref.html">
+
+<selectlist>
+ <button type=selectlist>button</button>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+document.querySelector('button').click();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html
new file mode 100644
index 0000000000..1a9e81d817
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-listbox-fallback-change-crash.tentative.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html class="test-wait">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1400522">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+
+<selectlist id="selectList" style="position: absolute;">
+ <div slot="button">
+ <button id="b1" behavior="button"></button>
+ </div>
+ <selectlist>
+ <div slot="button">
+ <button id="b2" behavior="button">x</button>
+ </div>
+ <div id="listbox" popover slot="listbox" behavior="listbox">
+ <option>y</option>
+ </div>
+ </selectlist>
+</selectlist>
+
+<script type="module">
+ const raf = () => new Promise(resolve => requestAnimationFrame(resolve));
+
+ document.querySelector('#b1').click();
+ document.querySelector('#b2').click();
+
+ document.querySelector('#selectList').style.top = '-25px';
+
+ await raf();
+ await raf();
+
+ document.querySelector('#selectList').style.top = '0px';
+
+ document.documentElement.classList.remove('test-wait');
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html
new file mode 100644
index 0000000000..36bae1a82b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-many-options.tentative.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: many options</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<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>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ }
+
+ #selectList0-popover {
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox" id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+<br>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+
+ await clickOn(selectList0);
+ assert_equals(Math.round(selectList0.getBoundingClientRect().bottom), Math.round(selectList0Popover.getBoundingClientRect().top));
+ assert_equals(Math.round(selectList0.getBoundingClientRect().left), Math.round(selectList0Popover.getBoundingClientRect().left));
+ assert_equals(window.innerHeight, Math.round(selectList0Popover.getBoundingClientRect().bottom));
+ }, "The popover should be bottom left positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html
new file mode 100644
index 0000000000..b1df8a6581
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned-ref.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+div {
+ width:300px;
+ display:flex;
+ justify-content:flex-end;
+}
+selectlist {
+ width:100px;
+}
+selectlist::part(button) {
+ border-style:none;
+ background-color:rgba(0,0,0,0)
+}
+</style>
+<div>
+<selectlist>
+ <option></option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html
new file mode 100644
index 0000000000..c2540acb36
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-end-aligned.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!-- Tests that <selectlist> marker is end-aligned -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-marker-end-aligned-ref.tentative.html">
+
+<style>
+div {
+ width:300px;
+ display:flex;
+ justify-content:flex-end;
+}
+selectlist {
+ width:200px;
+}
+selectlist::part(button) {
+ border-style:none;
+ background-color:rgba(0,0,0,0)
+}
+</style>
+<div>
+<selectlist>
+ <option></option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html
new file mode 100644
index 0000000000..1a1302b96d
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-internal-selectlist-button-icon')
+ .style.backgroundColor = 'red';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html
new file mode 100644
index 0000000000..dedabc9bab
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-part.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-marker-part-ref.html">
+
+<style>
+selectlist::part(marker) {
+ background-color: red;
+}
+</style>
+<selectlist>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html
new file mode 100644
index 0000000000..e910df4c5a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+
+ const oldMarker = selectlist.querySelector('.fake-selectlist-internal-selectlist-button-icon');
+ const newMarker = document.createElement('div');
+ newMarker.textContent = 'marker';
+
+ replaceChildElement(newMarker, oldMarker);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html
new file mode 100644
index 0000000000..de43a02c33
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-slot.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-marker-slot-ref.html">
+
+<selectlist>
+ <div slot=marker>marker</div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html
new file mode 100644
index 0000000000..dda53db5bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<style>
+selectlist {
+ width:100px;
+}
+</style>
+<div>
+<selectlist>
+ <option>&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html
new file mode 100644
index 0000000000..345c205983
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-marker-visible-overflow.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests that the marker is visible when the <selectlist> contains more text than it can show -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-marker-visible-overflow-ref.tentative.html">
+
+<style>
+selectlist {
+ width:100px;
+}
+</style>
+<div>
+<selectlist>
+ <option>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html
new file mode 100644
index 0000000000..c29413f180
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-nested.tentative.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: nested selects</title>
+<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>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <selectlist id="nested0">
+ <option id="child1">one</option>
+ <option id="child2">two</option>
+ </selectlist>
+ <option id="child3">three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList1">
+ <div popover slot="listbox" behavior="listbox">
+ <select>
+ <option>one</option>
+ <option>two</option>
+ </select>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div slot="button">
+ <selectlist id="nested2">
+ <button type=selectlist id="selectList2-button0">button0</button>
+ <option id="nested2-option1">one</option>
+ </selectlist>
+ <button type=selectlist id="selectList2-button1">button1</button>
+ </div>
+ <option>two</option>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const nested0 = document.getElementById("nested0");
+ const child2 = document.getElementById("child2");
+ assert_equals(selectList0.value, "three", "Options nested in another <selectlist> should not get controller code from outer <selectlist>");
+ await clickOn(selectList0);
+ assert_true(selectList0.open);
+ assert_false(nested0.open);
+
+ await clickOn(nested0);
+ assert_true(nested0.open);
+
+ await clickOn(child2);
+ assert_false(nested0.open);
+ assert_equals(nested0.value, "two");
+ assert_true(selectList0.open, "click on option in inner <selectlist> should not close outer <selectlist>");
+ assert_equals(selectList0.value, "three", "click on option in inner <selectlist> should not change value of outer <selectlist>");
+ }, "A <selectlist> shouldn't apply controller code to parts nested in a <selectlist> child");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ assert_equals(selectList0.value, "three");
+ }, "A <selectlist> shouldn't apply controller code to parts nested in a <select> child");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const nested2 = document.getElementById("nested2");
+ const button0 = document.getElementById("selectList2-button0");
+ const button1 = document.getElementById("selectList2-button1");
+ const nested2Option1 = document.getElementById("nested2-option1");
+ assert_false(selectList2.open);
+ assert_false(nested2.open);
+
+ await clickOn(button0);
+ assert_false(selectList2.open, "Clicking the button of a nested <selectlist> should not open the outer <selectlist>");
+ assert_true(nested2.open, "Clicking the button of a nested <selectlist> should open the outer <selectlist>");
+
+ await clickOn(nested2Option1);
+ assert_false(nested2.open);
+
+ await clickOn(button1);
+ assert_true(selectList2.open);
+ assert_false(nested2.open);
+ }, "A nested button part in a nested <selectlist> shouldn't get controller code even if it comes first in document order");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html
new file mode 100644
index 0000000000..126bbc5dd7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed-ref.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel="stylesheet" href="/fonts/ahem.css">
+
+<selectlist id="selectList0">
+ <option>option with image displayed</option>
+</selectlist>
+<div id=fakelistbox>
+ option with image displayed
+ <img src="/images/green-256x256.png">
+</div>
+
+<style>
+ html,selectlist {
+ font-family: Ahem;
+ }
+ #fakelistbox {
+ /* Per spec: */
+ display: block;
+ position: fixed;
+ top: 30px;
+ left: 0;
+ font-size: 0.765625em /* 0.875 * 0.875 */;
+ /* Per settings in test file: */
+ width: fit-content;
+ height: fit-content;
+ background: light-dark(white, black);
+ color: light-dark(black, white);
+ border: 1px solid rgba(0, 0, 0, 1);
+ border-radius: 0px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+
+ selectlist {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 30px;
+ }
+</style>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html
new file mode 100644
index 0000000000..2d51002fb2
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-displayed.tentative.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>HTMLSelectListElement Test: option arbitrary content displayed</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel=match href="selectlist-option-arbitrary-content-displayed-ref.tentative.html">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+ html,selectlist {
+ font-family: Ahem;
+ }
+ selectlist {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ height: 30px;
+ }
+
+ [popover] {
+ width: fit-content;
+ height: fit-content;
+ background: white;
+ color: black;
+ border: 1px solid rgba(0, 0, 0, 1);
+ border-radius: 0px;
+ box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11);
+ box-sizing: border-box;
+ overflow: auto;
+ padding: 4px;
+ }
+
+ option {
+ background-color: white !important;
+ padding: 0px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <option>
+ option with image displayed
+ <img src="/images/green-256x256.png">
+ </option>
+ </div>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ async function test() {
+ const selectList0 = document.getElementById("selectList0");
+
+ await clickOn(selectList0);
+ document.documentElement.classList.remove('reftest-wait');
+ }
+
+ test();
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html
new file mode 100644
index 0000000000..fa44198fff
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed-ref.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+
+<option>
+ option with image not displayed
+</option>
+
+<selectlist>
+</selectlist>
+
+<option>
+ option with image not displayed
+</option> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html
new file mode 100644
index 0000000000..e7cacdba27
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-arbitrary-content-not-displayed.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: option arbitrary content not displayed</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<link rel=match href="selectlist-option-arbitrary-content-not-displayed-ref.tentative.html">
+
+<option>
+ option with image not displayed
+ <img src="/images/green-256x256.png">
+</option>
+
+<selectlist id="selectList0">
+ <option id="selectList0-option">
+ option with image not displayed
+ <img src="/images/green-256x256.png">
+ </option>
+</selectlist>
+
+<script>
+ const selectList0Option = document.getElementById("selectList0-option");
+
+ // removing an option from <selectlist> should revert back to its original display behavior
+ selectList0Option.remove();
+ document.body.append(selectList0Option);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html
new file mode 100644
index 0000000000..993ef007e6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-focusable.tentative.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: option facusable</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<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>
+
+<selectlist id="selectlist0">
+ <option>one</option>
+ <option id="selectlist0-option2">two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+// See https://w3c.github.io/webdriver/#keyboard-actions
+const KEY_CODE_MAP = {
+ 'Enter': '\uE007',
+ 'Space': '\uE00D',
+ 'ArrowUp': '\uE013',
+ 'ArrowDown': '\uE015'
+};
+
+function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+}
+
+promise_test(async t => {
+ const selectList = document.querySelector("#selectlist0");
+ assert_false(selectList.open, "selectlist should not be initially open");
+
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ assert_equals(selectList.value, "one");
+
+ const option2 = document.querySelector('#selectlist0-option2');
+ option2.focus();
+ assert_equals(document.activeElement, option2);
+
+ await test_driver.send_keys(selectList, KEY_CODE_MAP.Enter);
+ assert_equals(selectList.value, "two");
+}, "Validate <option> is focusable when is a descendant of <selectlist>");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html
new file mode 100644
index 0000000000..1ab1d54722
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<selectlist>
+ <option>textcontent</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html
new file mode 100644
index 0000000000..c719ee0e07
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-option-label-rendering.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-option-label-rendering-ref.html">
+<link rel=assert title="option elements should not render their label attributes when used in selectlist.">
+
+<selectlist>
+ <option label=label>textcontent</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html
new file mode 100644
index 0000000000..df87060359
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x-ref.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+selectlist {
+ width:30px;
+ height:20px;
+}
+selectlist::part(button) {
+ background-color:blue;
+}
+</style>
+<selectlist>
+ <option>&nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html
new file mode 100644
index 0000000000..93c1b948a0
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-overflow-x.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Tests that selectlist button part text is truncated by the button. -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-overflow-x-ref.tentative.html">
+<style>
+selectlist {
+ width:30px;
+ height:20px;
+}
+selectlist::part(button) {
+ background-color:blue;
+}
+</style>
+<selectlist>
+ <option>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html
new file mode 100644
index 0000000000..104ae841f7
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-parts-structure.tentative.html
@@ -0,0 +1,495 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: part structure</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<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>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox">
+ <option id="selectList0-child1">one</option>
+ <option id="selectList0-child2">two</option>
+ <div behavior="option" id="selectList0-child3">three</div>
+ </div>
+ <option id="selectList0-child4">four</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <listbox id="selectList1-popover">
+ <button type=selectlist id="selectList1-button">
+ Custom button
+ </button>
+ <option>one</option>
+ <option id="selectList1-child2">two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <button type=selectlist id="selectList2-button">
+ Custom button
+ <listbox id="selectList2-popover">
+ Custom listbox
+ </listbox>
+ </button>
+ <option>three</option>
+ <div>
+ This is some text.
+ <option id="selectList2-child4">four</option>
+ More text.
+ </div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div slot="button" id="selectList3-button-slot">
+ <button type=selectlist id="selectList3-button0">button0</button>
+ </div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <button type=selectlist id="selectList4-button0">button0</button>
+ <div slot="listbox" id="selectList4-listbox-slot">
+ <div popover behavior="listbox" id="selectList4-listbox0">
+ <option>one</option>
+ <option id="selectList4-option2">two</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList5">
+ <div slot="button" id="selectList5-button-slot">
+ <button type=selectlist id="selectList5-button0">button0</button>
+ <div behavior="selected-value" id="selectList5-selectedValue0"></div>
+ </div>
+ <option>one</option>
+ <option id="selectList5-option0">two</option>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList6">
+ <div slot="button"></div>
+ <div popover slot="listbox" behavior="listbox"></div>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList7">
+ <div slot="listbox"></div>
+ <button type=selectlist></button>
+</selectlist>
+
+<!-- No associated JS test -- just don't crash when parsing! -->
+<selectlist id="selectList8">
+ <div slot="listbox"></div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList9">
+ <div slot="listbox" id="selectList9-listbox-slot">
+ <div popover behavior="listbox" id="selectList9-originalListbox">
+ <option>one</option>
+ <option id="selectList9-option2">two</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList11">
+ <div popover slot="listbox" behavior="listbox">
+ <option>one</option>
+ </div>
+ <div slot="button" behavior="listbox" id="selectList11-button">Test</div>
+</selectlist>
+
+<selectlist id="selectList12">
+ <button type=selectlist id="selectList12-button0">button0</button>
+ <listbox id="selectList12-listbox">
+ <option id="selectList12-option1">one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList13">
+ <div slot="button" id="selectList12-button-slot">
+ <div id="selectList13-removeContent-button">
+ <button type=selectlist id="selectList13-button0">button0</button>
+ <button type=selectlist id="selectList13-button1">button1</button>
+ </div>
+ <button type=selectlist id="selectList13-button2">button2</button>
+ </div>
+ <div slot="listbox" id="selectList13-listbox-slot">
+ <div id="selectList13-removeContent-listbox">
+ <div popover behavior="listbox" id="selectList13-originalListbox">
+ <option id="selectList13-option1">one</option>
+ <option id="selectList13-option2">two</option>
+ </div>
+ </div>
+ <div popover behavior="listbox" id="selectList13-newListbox">
+ <option>three</option>
+ <option id="selectList13-option4">four</option>
+ </div>
+ </div>
+</selectlist>
+
+<selectlist id="selectList14">
+ <button type=selectlist id="selectList14-button0">button0</button>
+ <option>one</option>
+ <option id="selectList14-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList15">
+ <div slot="button" id="selectList15-div0"></div>
+ <option>one</option>
+</selectlist>
+
+<selectlist id="selectList16">
+ <div slot="button">
+ <div id="selectList16-div0">
+ <button type=selectlist id="selectList16-button0">button</button>
+ </div>
+ </div>
+ <option>one</option>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child1 = document.getElementById("selectList0-child1");
+ const selectList0Child2 = document.getElementById("selectList0-child2");
+ const selectList0Child3 = document.getElementById("selectList0-child3");
+ assert_equals(selectList0.value, "one");
+ await clickOn(selectList0);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.value, "two");
+
+ await clickOn(selectList0);
+ await clickOn(selectList0Child3);
+ assert_equals(selectList0.value, "two", "Clicking a non-HTMLOptionElement labeled as an option should do nothing");
+
+ await clickOn(selectList0Child1);
+ assert_equals(selectList0.value, "one");
+ assert_false(selectList0.open);
+ }, "HTMLOptionElements (and not other element types) should receive option controller code");
+
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child4 = document.getElementById("selectList0-child4");
+
+ assert_equals(selectList0.value, "one");
+ await clickOn(selectList0);
+ assert_true(selectList0.open);
+ selectList0Child4.click();
+ assert_equals(selectList0.value, "one", "Clicking an option outside of the popover should not change the value");
+ }, "To receive option part controller code, an option must be a descendant of the listbox part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+ const selectList1Button = document.getElementById("selectList1-button");
+ const selectList1Child2 = document.getElementById("selectList1-child2");
+ assert_false(selectList1Popover.matches(':popover-open'));
+ selectList1Button.click();
+ assert_false(selectList1Popover.matches(':popover-open'), "Clicking a button part that is a descendant of the listbox part should have no effect");
+
+ assert_equals(selectList1.value, "one");
+ await clickOn(selectList1);
+ assert_true(selectList1Popover.matches(':popover-open'));
+ await clickOn(selectList1Child2);
+ assert_equals(selectList1.value, "two", "Clicking an <option> should change the value");
+ }, "To receive button part controller code, an element labeled as a button must not be a descendant of the listbox part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+ const selectList2Button = document.getElementById("selectList2-button");
+ const selectList2Child2 = document.getElementById("selectList2-child2");
+ const selectList2Child4 = document.getElementById("selectList2-child4");
+
+ assert_false(selectList2Popover.matches(':popover-open'));
+ await clickOn(selectList2Button);
+ assert_false(selectList2Popover.matches(':popover-open'), "Clicking a button part should not show an invalid listbox part");
+
+ assert_equals(selectList2.value, "three");
+ await clickOn(selectList2Child4);
+ assert_equals(selectList2.value, "four", "Clicking an <option> that is a descendant of a valid listbox part should update the value");
+ }, "To receive listbox part controller code, an element labeled as a listbox must not be a descendant of the button part in a flat tree traversal");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3ButtonSlot = document.getElementById("selectList3-button-slot");
+ const selectList3Button0 = document.getElementById("selectList3-button0");
+
+ assert_false(selectList3.open);
+
+ let button1 = document.createElement("div");
+ button1.innerText = "button1";
+ button1.setAttribute("behavior", "button");
+ selectList3ButtonSlot.appendChild(button1);
+
+ await clickOn(button1);
+ assert_false(selectList3.open, "A button part should only get controller code if it's first in document order, even if added dynamically");
+
+ await clickOn(selectList3Button0);
+ assert_true(selectList3.open, "A button part should get controller code if it's first in document order");
+ }, "Button controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+ promise_test(async () => {
+ const selectList4 = document.getElementById("selectList4");
+ const selectList4Button0 = document.getElementById("selectList4-button0");
+ const selectList4ListboxSlot = document.getElementById("selectList4-listbox-slot");
+ const selectList4Option2 = document.getElementById("selectList4-option2");
+
+ assert_false(selectList4.open);
+
+ let listbox2 = document.createElement("div");
+ listbox2.innerHTML = `
+ <option>three</option>
+ <option id="selectList4-option4">four</option>
+ `;
+ listbox2.setAttribute("behavior", "listbox");
+ selectList4ListboxSlot.appendChild(listbox2);
+
+ await clickOn(selectList4Button0);
+ assert_true(selectList4.open);
+
+ const selectList4Option4 = document.getElementById("selectList4-option4");
+ await clickOn(selectList4Option4);
+ assert_equals(selectList3.value, "one", "An option in a listbox should not get controller code if its listbox isn't first in document order, even if added dynamically");
+
+ await clickOn(selectList4Button0);
+ assert_true(selectList4.open);
+
+ await clickOn(selectList4Option2);
+ assert_equals(selectList4.value, "two", "An option in a listbox should get controller code if its listbox is first in document order, even if another listbox was added dynamically");
+}, "Listbox controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+promise_test(async () => {
+ const selectList5 = document.getElementById("selectList5");
+ const selectList5ButtonSlot = document.getElementById("selectList5-button-slot");
+ const selectList5Button0 = document.getElementById("selectList5-button0");
+ const selectList5SelectedValue0 = document.getElementById("selectList5-selectedValue0");
+
+ assert_false(selectList3.open);
+ assert_equals(selectList5SelectedValue0.innerText, "one");
+
+ let selectedValue1 = document.createElement("div");
+ selectList5ButtonSlot.appendChild(selectedValue1);
+
+ await clickOn(selectList5Button0);
+ assert_true(selectList5.open);
+
+ await clickOn(document.getElementById("selectList5-option0"));
+ assert_false(selectList5.open);
+ assert_equals(selectList5SelectedValue0.innerText, "two", "first selected-value part in flat tree order should get controller code");
+ assert_equals(selectedValue1.innerText, "", "Dynamically inserted selected-value part shouldn't get controller code if it's not first in flat tree order");
+ }, "selected-value controller code should be applied in flat tree traversal order regardless of dynamic insertion order");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList9");
+ const originalListbox = document.getElementById("selectList9-originalListbox");
+ const option2 = document.getElementById("selectList9-option2");
+ assert_equals(selectList.value, "one", "Initial value should be the first option");
+
+ let newListbox = document.createElement("div");
+ newListbox.setAttribute("popover", "auto");
+ newListbox.setAttribute("behavior", "listbox");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ newListbox.appendChild(newOption);
+ let newOption2 = document.createElement("option");
+ newOption2.innerText = "four";
+ newListbox.appendChild(newOption2);
+ originalListbox.parentElement.insertBefore(newListbox, originalListbox);
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "Menu should open when clicked");
+
+ option2.click(); // clickOn doesn't work because the old options are not displayed
+ assert_equals(selectList.value, "three", "Elements in second popover should no longer be option parts");
+ assert_true(selectList.open, "Clicking non-part options shouldn't close the popover");
+
+ await clickOn(newOption2);
+ assert_false(selectList.open);
+ assert_equals(selectList.value, "four", "New options should get controller code after listbox switch");
+ }, "Ensure that option controller code is updated when listbox changes");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList11");
+ const selectList11Button= document.getElementById("selectList11-button");
+
+ await clickOn(selectList11Button);
+ assert_false(selectList.open, "Controller code not applied due to part attribute not being button");
+ selectList11Button.remove();
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "Default button part should be used");
+ }, "Ensure that controller code is applied when slot and part attributes are different");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList12");
+ const originalListbox = document.getElementById("selectList12-listbox");
+ assert_equals(selectList.value, "one", "Initial value should be the first option");
+
+ const selectListButton0 = document.getElementById("selectList12-button0");
+ const selectListOption1 = document.getElementById("selectList12-option1");
+
+ assert_false(selectList.open);
+ let button1 = document.createElement("button");
+ button1.innerText = "button1";
+ button1.setAttribute("type", "selectlist");
+ selectList.insertBefore(button1, selectListButton0);
+ button1.click();
+ assert_true(selectList.open, "Controller code should be applied to the new first button in document order");
+ await clickOn(selectListOption1);
+ assert_false(selectList.open, "listbox should close after clicking option1.");
+ selectListButton0.click();
+ assert_true(selectList.open, "listbox should open after clicking button0.");
+ selectListButton0.click();
+ assert_false(selectList.open, "listbox should close after clicking button0 again.");
+
+ let button2 = document.createElement("button");
+ button2.innerText = "button2";
+ selectList.insertBefore(button2, button1);
+ button2.click();
+ assert_false(selectList.open, "Controller code should not be applied to button2 since it doesn't have type=selectlist.");
+ button2.setAttribute("type", "selectlist");
+ button2.click();
+ assert_true(selectList.open, "Controller code should be applied to the new button part");
+ await clickOn(selectListOption1);
+ assert_false(selectList.open);
+
+ let newListbox = document.createElement("listbox");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ newListbox.appendChild(newOption);
+ let newOption2 = document.createElement("option");
+ newOption2.innerText = "four";
+ newListbox.appendChild(newOption2);
+ originalListbox.parentElement.insertBefore(newListbox, originalListbox);
+ assert_equals(selectList.value, "three", "New value should be the first option");
+
+ newListbox.innerHTML = "<option>five</option><option>six</option>";
+ assert_equals(selectList.value, "five", "New value should be the first option");
+
+ selectList.innerHTML = "<option>seven</option><option id='selectList12-option2'>eight</option>";
+ assert_equals(selectList.value, "seven", "New value should be the first option");
+ const selectListOption2 = document.getElementById("selectList12-option2");
+ await clickOn(selectList);
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(selectList.value, "eight", "Controller code should be applied to new options");
+
+ selectListOption2.slot = "button";
+ assert_equals(selectList.value, "seven", "Previous selected option should become invalid");
+ }, "Ensure that controller code is synchronously applied");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList13");
+ assert_equals(selectList.value, "one");
+
+ const selectListButton0 = document.getElementById("selectList13-button0");
+ const selectListButton1 = document.getElementById("selectList13-button1");
+ selectListButton1.click();
+ assert_false(selectList.open);
+ selectListButton0.click();
+ assert_true(selectList.open, "First button should receive controller code");
+ await clickOn(document.getElementById("selectList13-option2"));
+ assert_equals(selectList.value, "two");
+ let divButtonToRemove = document.getElementById("selectList13-removeContent-button");
+ divButtonToRemove.innerHTML = "";
+ selectListButton0.click();
+ assert_false(selectList.open, "The first button is invalid");
+ const selectListButton2 = document.getElementById("selectList13-button2");
+ selectListButton2.click();
+ assert_true(selectList.open, "The button part should be updated")
+ await clickOn(document.getElementById("selectList13-option1"));
+ assert_equals(selectList.value, "one");
+
+ const selectListOption4 = document.getElementById("selectList13-option4");
+ selectListOption4.click();
+ assert_equals(selectList.value, "one");
+ let divListboxToRemove = document.getElementById("selectList13-removeContent-listbox");
+ divListboxToRemove.innerHTML = "";
+ assert_equals(selectList.value, "three", "The listbox part should be updated");
+ selectListOption4.click();
+ assert_equals(selectList.value, "four", "Controller code should be applied to the new options");
+
+ let selectListNewListbox = document.getElementById("selectList13-newListbox");
+ selectListNewListbox.innerHTML = "";
+ assert_equals(selectList.value, "");
+ selectListOption4.click();
+ assert_equals(selectList.value, "");
+ }, "Controller code should be updated when nested parts are removed");
+
+ promise_test(async () => {
+ let selectList = document.getElementById("selectList14");
+ assert_equals(selectList.value, "one");
+ const selectListButton0 = document.getElementById("selectList14-button0");
+ const selectListOption2 = document.getElementById("selectList14-option2");
+
+ selectListButton0.click();
+ assert_true(selectList.open);
+ await clickOn(selectListOption2);
+ assert_equals(selectList.value, "two");
+
+ document.body.removeChild(selectList);
+ selectList.removeChild(selectListOption2);
+ assert_equals(selectList.value, "one");
+ let newOption = document.createElement("option");
+ newOption.innerText = "three";
+ selectList.appendChild(newOption);
+ newOption.click();
+ assert_equals(selectList.value, "three", "New option should receive controller code");
+
+ let doc = document.implementation.createHTMLDocument('');
+ let selectList1 = doc.createElement('selectlist');
+ let firstOption = doc.createElement('option');
+ firstOption.innerText = 'one';
+ let secondOption = doc.createElement('option');
+ secondOption.innerText = 'two';
+ selectList1.appendChild(firstOption);
+ selectList1.appendChild(secondOption);
+ assert_equals(selectList1.value, "one");
+ secondOption.click();
+ assert_equals(selectList1.value, "two");
+ document.body.appendChild(selectList1);
+ selectList1.removeChild(secondOption);
+ assert_equals(selectList1.value, "one");
+ }, "Moving a selectlist between documents should keep controller code active");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList15");
+ const selectListButtonContainer = document.getElementById("selectList15-div0");
+
+ const outerDiv = document.createElement("div");
+ const button = document.createElement("input");
+ button.type = button.value = "button";
+ button.setAttribute("behavior", "button");
+ outerDiv.appendChild(button);
+ selectListButtonContainer.appendChild(outerDiv);
+
+ await clickOn(selectList);
+ assert_true(selectList.open, "New button should receive controller code");
+ }, "New parts should be detected even when in the subtree of an inserted node");
+
+ promise_test(async () => {
+ const selectList = document.getElementById("selectList16");
+ const selectListButtonContainer = document.getElementById("selectList16-div0");
+ const selectListButton = document.getElementById("selectList16-button0");
+
+ selectListButtonContainer.remove();
+
+ selectListButton.click();
+ assert_false(selectList.open, "Removed button should no longer have controller code");
+ }, "Part removals should be detected even when in the subtree of a removed node");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html
new file mode 100644
index 0000000000..692eed7caa
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position-with-zoom.tentative.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: popover position with zoom</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<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>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ zoom: 2;
+ }
+
+ #selectList1 {
+ position: absolute;
+ bottom: 0px;
+ left: 0px;
+ zoom: 1.5;
+ }
+
+ #selectList1-popover {
+ zoom: 2;
+ }
+
+ #selectList2 {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ zoom: 3;
+ }
+
+ #selectList3 {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ zoom: 4;
+ }
+
+ #selectList3-popover {
+ zoom: 1.5;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <button type=selectlist id="selectList0-button">Custom bottom left</button>
+ <listbox id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+<br>
+
+<selectlist id="selectList1">
+ <button type=selectlist id="selectList1-button">Custom top left</button>
+ <listbox id="selectList1-popover">
+ <option>top left</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <button type=selectlist id="selectList2-button">Custom bottom right</button>
+ <listbox id="selectList2-popover">
+ <option>bottom right</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList3">
+ <button type=selectlist id="selectList3-button">Custom top right</button>
+ <listbox id="selectList3-popover">
+ <option>top right</option>
+ <option>two</option>
+ <option>three</option>
+ </listbox>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+ const selectList0Button = document.getElementById("selectList0-button");
+
+ await clickOn(selectList0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().bottom - selectList0Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().left - selectList0Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be bottom left positioned");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+ const selectList1Button = document.getElementById("selectList1-button");
+
+ selectList1Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().top - selectList1Popover.getBoundingClientRect().bottom * 2)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().left - selectList1Popover.getBoundingClientRect().left * 2)), 0);
+ }, "The popover should be top left positioned");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+ const selectList2Button = document.getElementById("selectList2-button");
+
+ selectList2Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().bottom - selectList2Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().right - selectList2Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be bottom right positioned");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Popover = document.getElementById("selectList3-popover");
+ const selectList3Button = document.getElementById("selectList3-button");
+
+ selectList3Button.click();
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().top - selectList3Popover.getBoundingClientRect().bottom * 1.5)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().right - selectList3Popover.getBoundingClientRect().right * 1.5)), 0);
+ }, "The popover should be top right positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html
new file mode 100644
index 0000000000..a19e2b0d28
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover-position.tentative.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<title>HTMLSelectListElement Test: popover position</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<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>
+
+<style>
+ #selectList0 {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ }
+
+ #selectList1 {
+ position: absolute;
+ bottom: 0px;
+ left: 0px;
+ }
+
+ #selectList2 {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ }
+
+ #selectList3 {
+ position: absolute;
+ bottom: 0px;
+ right: 0px;
+ }
+</style>
+
+<selectlist id="selectList0">
+ <div popover slot="listbox" behavior="listbox" id="selectList0-popover">
+ <option>bottom left</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+<br>
+
+<selectlist id="selectList1">
+ <div popover slot="listbox" behavior="listbox" id="selectList1-popover">
+ <option>top left</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div popover slot="listbox" behavior="listbox" id="selectList2-popover">
+ <option>bottom right</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div popover slot="listbox" behavior="listbox" id="selectList3-popover">
+ <option>top right</option>
+ <option>two</option>
+ <option>three</option>
+ </div>
+</selectlist>
+
+<script>
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Popover = document.getElementById("selectList0-popover");
+
+ await clickOn(selectList0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().bottom - selectList0Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList0.getBoundingClientRect().left - selectList0Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be bottom left positioned");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const selectList1Popover = document.getElementById("selectList1-popover");
+
+ await clickOn(selectList1);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().top - selectList1Popover.getBoundingClientRect().bottom)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList1.getBoundingClientRect().left - selectList1Popover.getBoundingClientRect().left)), 0);
+ }, "The popover should be top left positioned");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ const selectList2Popover = document.getElementById("selectList2-popover");
+
+ await clickOn(selectList2);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().bottom - selectList2Popover.getBoundingClientRect().top)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList2.getBoundingClientRect().right - selectList2Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be bottom right positioned");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Popover = document.getElementById("selectList3-popover");
+
+ await clickOn(selectList3);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().top - selectList3Popover.getBoundingClientRect().bottom)), 0);
+ assert_equals(Math.abs(Math.trunc(selectList3.getBoundingClientRect().right - selectList3Popover.getBoundingClientRect().right)), 0);
+ }, "The popover should be top right positioned");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html
new file mode 100644
index 0000000000..a26d026649
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-popover.tentative.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: popover</title>
+<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>
+
+<selectlist id="selectList0">
+ <option>one</option>
+ <option id="selectList0-child2">two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+
+<selectlist id="selectList1">
+ <button type=selectlist class=button>
+ Custom button
+ </button>
+ <listbox>
+ <option>one</option>
+ <option class="child2">two</option>
+ <option class="child3">three</option>
+ </listbox>
+</selectlist>
+
+<selectlist id="selectList2">
+ <!-- Swap out the listbox part without providing a replacement -->
+ <div slot="listbox"></div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div slot="listbox">
+ <div popover behavior="listbox" id="selectList3-listbox">
+ <option>one</option>
+ </div>
+ </div>
+</selectlist>
+<script>
+
+ function clickOn(element) {
+ const actions = new test_driver.Actions();
+ return actions.pointerMove(0, 0, {origin: element})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ }
+
+ promise_test(async () => {
+ const selectList0 = document.getElementById("selectList0");
+ const selectList0Child2 = document.getElementById("selectList0-child2");
+ assert_equals(selectList0.value, "one");
+ assert_equals(selectList0.open, false);
+ await clickOn(selectList0);
+ assert_equals(selectList0.open, true);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.value, "two");
+ assert_equals(selectList0.open, false);
+
+ await clickOn(selectList0);
+ assert_equals(selectList0.open, true);
+ await clickOn(selectList0Child2);
+ assert_equals(selectList0.open, false);
+ }, "Opening the popover and clicking an option should change the selectlist's value");
+
+ promise_test(async () => {
+ const selectList1 = document.getElementById("selectList1");
+ const button = selectList1.querySelector(".button");
+ const child2 = selectList1.querySelector(".child2");
+ const child3 = selectList1.querySelector(".child3");
+ assert_equals(selectList1.value, "one");
+ assert_equals(selectList1.open, false);
+ await clickOn(button);
+ assert_equals(selectList1.open, true);
+ await clickOn(child2);
+ assert_equals(selectList1.value, "two", "Clicking an <option> should change the value");
+ assert_equals(selectList1.open, false);
+
+ await clickOn(button);
+ assert_equals(selectList1.open, true);
+ await clickOn(child3);
+ assert_equals(selectList1.value, "three", "Clicking a <div part='option'> should change the value");
+ assert_equals(selectList1.open, false);
+ }, "With custom button and popover: opening the popover and clicking an option should change the selectlist's value");
+
+ promise_test(async () => {
+ const selectList2 = document.getElementById("selectList2");
+ await clickOn(selectList2);
+ assert_equals(selectList2.value, "");
+ assert_equals(selectList2.open, false);
+ }, "Clicking a popover with no listbox part does nothing");
+
+ promise_test(async () => {
+ const selectList3 = document.getElementById("selectList3");
+ const selectList3Listbox = document.getElementById("selectList3-listbox");
+ selectList3Listbox.remove();
+
+ await clickOn(selectList3);
+ assert_equals(selectList3.value, "");
+ assert_equals(selectList3.open, false);
+ }, "Clicking a popover with a listbox that was removed does nothing");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html
new file mode 100644
index 0000000000..bda5842a37
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-light-dismiss-invalidation.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="http://crbug.com/1429839">
+<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>
+
+<selectlist id=selectlist>
+ <option id=optone>one</option>
+ <option id=opttwo>two</option>
+</selectlist>
+<style>
+selectlist {
+ background-color: rgb(0, 0, 255);
+}
+selectlist:closed {
+ background-color: rgb(0, 255, 0);
+}
+selectlist:open {
+ background-color: rgb(255, 0, 0);
+}
+</style>
+<button id=button>hello world</button>
+
+<script>
+promise_test(async () => {
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed at the start of the test.');
+
+ await test_driver.click(selectlist);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The selectlist should match :open when opened.');
+
+ await test_driver.click(opttwo);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed after clicking an option.');
+
+ await test_driver.click(selectlist);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The selectlist should match :open when reopened.');
+
+ await test_driver.click(button);
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 255, 0)',
+ 'The selectlist should match :closed after light dismiss.');
+}, 'selectlist should not match :open when light dismissed.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html
new file mode 100644
index 0000000000..1d5a082c03
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-pseudo-open-closed.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/547">
+<link rel=help href="https://drafts.csswg.org/selectors/#open-state">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=myselectlist>
+ <button type=selectlist id=custombutton>button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+test(() => {
+ assert_false(myselectlist.matches(':open'),
+ 'Selectlist should not match :open while it is closed.');
+ assert_true(myselectlist.matches(':closed'),
+ 'Selectlist should match :closed while it is closed.');
+
+ custombutton.click();
+
+ assert_true(myselectlist.matches(':open'),
+ 'Selectlist should match :open while it is open.');
+ assert_false(myselectlist.matches(':closed'),
+ 'Selectlist should not match :closed while it is open.');
+}, 'Selectlist should support :open and :closed pseudo selectors.');
+</script>
+
+<selectlist id=selectlistinvalidation>
+ <button type=selectlist>button</button>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+<style>
+selectlist:closed {
+ background-color: red;
+}
+selectlist:open {
+ background-color: green;
+}
+</style>
+
+<script>
+test(() => {
+ const selectlist = document.getElementById('selectlistinvalidation');
+ const button = selectlist.querySelector('button');
+ const option = selectlist.querySelector('option');
+
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The style rules from :closed should apply when the selectlist is closed.');
+
+ button.click();
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(0, 128, 0)',
+ 'The style rules from :open should apply when the selectlist is open.');
+
+ option.click();
+ assert_equals(getComputedStyle(selectlist).backgroundColor, 'rgb(255, 0, 0)',
+ 'The style rules from :closed should apply when the selectlist is opened and closed again.');
+}, 'Selectlist :open and :closed should invalidate correctly.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html
new file mode 100644
index 0000000000..ef4408915b
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-required-attribute.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: required attribute</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ selectlist:required {
+ border: 3px dashed rgb(255, 0, 0);
+ }
+
+ selectlist:optional {
+ border: 1px solid rgb(128, 128, 128);
+ }
+</style>
+
+<selectlist id="selectlist0" required>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectlist1">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<selectlist id="selectlist2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+function checkRequired(style) {
+ assert_equals(style.borderWidth, '3px');
+ assert_equals(style.borderStyle, 'dashed');
+ assert_equals(style.borderColor, 'rgb(255, 0, 0)');
+}
+
+function checkOptional(style) {
+ assert_equals(style.borderWidth, '1px');
+ assert_equals(style.borderStyle, 'solid');
+ assert_equals(style.borderColor, 'rgb(128, 128, 128)');
+}
+
+test(() => {
+ const selectList0 = document.getElementById("selectlist0");
+ const selectList1 = document.getElementById("selectlist1");
+ const selectList2 = document.getElementById("selectlist2");
+
+ checkRequired(window.getComputedStyle(selectList0));
+ checkOptional(window.getComputedStyle(selectList1));
+ checkOptional(window.getComputedStyle(selectList2));
+ selectList2.required = true;
+ checkRequired(window.getComputedStyle(selectList2));
+}, "Test required attribute");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html
new file mode 100644
index 0000000000..7c71ea079c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl-ref.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('option');
+ document.body.appendChild(selectlist);
+ const button = selectlist.querySelector('.fake-selectlist-internal-selectlist-button');
+ button.style.direction = "rtl";
+ button.style.width = "300px";
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html
new file mode 100644
index 0000000000..24f7959632
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-rtl.tentative.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!-- Tests selectlist text alignment in rtl -->
+<link rel=author href="mailto:pkotwicz@chromium.org">
+<link rel="match" href="selectlist-rtl-ref.tentative.html">
+
+<style>
+selectlist {
+ direction:rtl;
+ width:300px;
+}
+</style>
+<selectlist>
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html
new file mode 100644
index 0000000000..7d76f1c817
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-selected-value')
+ .style.color = 'blue';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html
new file mode 100644
index 0000000000..a43e43d267
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-behavior.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-behavior-ref.html">
+
+<selectlist>
+ <div style="color:blue" slot=selected-value behavior=selected-value></div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html
new file mode 100644
index 0000000000..3be168beba
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+ selectlist.querySelector('.fake-selectlist-selected-value')
+ .style.backgroundColor = 'red';
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html
new file mode 100644
index 0000000000..8fc05480d1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-part.tentative.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-part-ref.html">
+
+<style>
+selectlist::part(selected-value) {
+ background-color: red;
+}
+</style>
+<selectlist>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html
new file mode 100644
index 0000000000..ecb8c886a1
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="support/fake-selectlist.js"></script>
+<body>
+<script>
+ const selectlist = createFakeSelectlist('hello world');
+ document.body.appendChild(selectlist);
+
+ const oldSelectedValue = selectlist.querySelector('.fake-selectlist-selected-value');
+ const newSelectedValue = document.createElement('div');
+ newSelectedValue.textContent = 'new selected value';
+
+ replaceChildElement(newSelectedValue, oldSelectedValue);
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html
new file mode 100644
index 0000000000..c69a962c29
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selected-value-slot.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=match href="selectlist-selected-value-slot-ref.html">
+
+<selectlist>
+ <div slot=selected-value>new selected value</div>
+ <option>hello world</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html
new file mode 100644
index 0000000000..1c0be58d67
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<button>
+ <span style="color:red">red</span> one
+</button>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html
new file mode 100644
index 0000000000..5d273c21bf
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element-cloning.tentative.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/571">
+<link rel=match href="selectlist-selectedoption-element-cloning-ref.html">
+
+<selectlist>
+ <button type=selectlist>
+ <selectedoption></selectedoption>
+ </button>
+ <option><span style="color:red">red</span> one</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html
new file mode 100644
index 0000000000..593c4a6f21
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-selectedoption-element.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist>
+ <div>Foo <selectedoption></selectedoption> Bar</div>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<script>
+test(() => {
+ const selectlist = document.querySelector('selectlist');
+ const selectedoption = document.querySelector('selectedoption');
+ const div = document.querySelector('div');
+
+ assert_equals(selectlist.value, 'one', 'one should be selected initially.');
+ assert_equals(selectedoption.textContent, 'one', "selectedoption's initial text content should be one.");
+ assert_equals(div.textContent, 'Foo one Bar', 'Outer textContent should include default selectedoption.');
+
+ selectlist.value = 'two';
+ assert_equals(selectlist.value, 'two', "assigning two into selectlists's value should work.");
+ assert_equals(selectedoption.textContent, 'two', "selectedoption's text content should be updated with the new value.");
+ assert_equals(div.textContent, 'Foo two Bar', 'Outer textContent should include new selectedoption.');
+}, "<selectedoption>'s text contents should be replaced with its ancestor <selectlist>'s selected value.");
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html
new file mode 100644
index 0000000000..3b7d9d3d9a
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tab-navigation.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<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>
+
+<input id="input1">
+<selectlist id="selectlist">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<input id="input3">
+
+<script>
+promise_test(async () => {
+ const TAB_KEY = "\uE004";
+
+ const input1 = document.getElementById("input1");
+ const selectlist = document.getElementById("selectlist");
+
+ input1.focus();
+ assert_equals(document.activeElement.id, "input1", "input1 should be active");
+
+ await test_driver.send_keys(input1, TAB_KEY);
+ assert_equals(document.activeElement.id, "selectlist", "selectlist should be active");
+
+ await test_driver.send_keys(selectlist, TAB_KEY);
+ assert_equals(document.activeElement.id, "input3", "input3 should be active");
+}, "Check that <selectlist> occupies just one slot in tab navigation.");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html
new file mode 100644
index 0000000000..c93efe1118
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-tabindex-order.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<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>
+
+<input id="input1">
+<selectlist id="selectlist" tabindex="2">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+</selectlist>
+<input id="input3" tabindex="1">
+
+<script>
+promise_test(async () => {
+ const TAB_KEY = "\uE004";
+
+ const input1 = document.getElementById("input1");
+ const input3 = document.getElementById("input3");
+
+ input1.focus();
+ assert_equals(document.activeElement.id, "input1", "input1 should be active");
+
+ await test_driver.send_keys(input1, TAB_KEY);
+ assert_equals(document.activeElement.id, "input3", "input3 should be active");
+
+ await test_driver.send_keys(input3, TAB_KEY);
+ assert_equals(document.activeElement.id, "selectlist", "selectlist should be active");
+}, "Check that tabindex applies to <selectlist>");
+</script>
+
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html
new file mode 100644
index 0000000000..ab28fbede6
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+.container {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+}
+</style>
+<div class=container>text node</div>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html
new file mode 100644
index 0000000000..ceefeca608
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-text-only.tentative.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/702">
+<link rel=match href="selectlist-text-only-ref.html">
+
+<selectlist>
+ text node
+ <option>option</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html
new file mode 100644
index 0000000000..078e8a4d83
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-user-select.tentative.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/687">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=useragent>
+ <option id=useragentoptionone>one</option>
+ <option id=useragentoptiontwo>two</option>
+</selectlist>
+
+<selectlist id=custom>
+ <div id=custombutton slot=button behavior=button>button</div>
+ <div id=customlistbox popover=auto slot=listbox behavior=listbox>listbox</div>
+</selectlist>
+
+<selectlist id=customwithselection style="user-select:auto">
+ <div id=custombuttonwithselection slot=button behavior=button>button</div>
+ <div id=customlistboxwithselection popover=auto slot=listbox behavior=listbox>listbox</div>
+</selectlist>
+
+<selectlist id=customwithmixedselection>
+ <div id=custombuttonwithmixedselection slot=button behavior=button style="user-select:auto">button</div>
+ <div id=customlistboxwithmixedselection popover=auto slot=listbox behavior=listbox style="user-select:auto">listbox</div>
+</selectlist>
+
+<script>
+test(() => {
+ assert_equals(getComputedStyle(useragent).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(useragentoptionone).userSelect, 'none',
+ 'The first option should have user-select:none.');
+ assert_equals(getComputedStyle(useragentoptiontwo).userSelect, 'none',
+ 'The second option should have user-select:none.');
+}, 'Option elements should have user-select:none without slotting buttons or listboxes.');
+
+test(() => {
+ assert_equals(getComputedStyle(custom).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(custombutton).userSelect, 'none',
+ 'The custom button should have user-select:none.');
+ assert_equals(getComputedStyle(customlistbox).userSelect, 'none',
+ 'The custom listbox should have user-select:none.');
+}, 'Slotted in buttons and listboxes should have user-select:none.');
+
+test(() => {
+ assert_equals(getComputedStyle(customwithselection).userSelect, 'auto',
+ 'The selectlist should have user-select:auto.');
+ assert_equals(getComputedStyle(custombuttonwithselection).userSelect, 'auto',
+ 'The custom button should have user-select:auto.');
+ assert_equals(getComputedStyle(customlistboxwithselection).userSelect, 'auto',
+ 'The custom listbox should have user-select:auto.');
+}, 'Setting user-select:auto on selectlists should re-enable selection.');
+
+test(() => {
+ assert_equals(getComputedStyle(customwithmixedselection).userSelect, 'none',
+ 'The selectlist should have user-select:none.');
+ assert_equals(getComputedStyle(custombuttonwithmixedselection).userSelect, 'auto',
+ 'The custom button should have user-select:auto.');
+ assert_equals(getComputedStyle(customlistboxwithmixedselection).userSelect, 'auto',
+ 'The custom listbox should have user-select:auto.');
+}, 'Children of selectlist should be able to opt-in to user-select.');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html
new file mode 100644
index 0000000000..2b2033f668
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-validity.tentative.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html lang="en">
+<title>HTMLSelectListElement Test: validity</title>
+<link rel="author" title="Ionel Popescu" href="mailto:iopopesc@microsoft.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id="selectlist1" required>
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+</selectlist>
+
+<form>
+ <selectlist id="selectlist2" required>
+ </selectlist>
+</form>
+
+<script>
+
+test(() => {
+ let selectList = document.createElement('selectlist');
+ assert_true(selectList.willValidate, "A selectlist element is a submittable element that is a candidate for constraint validation.");
+ let option = document.createElement('option');
+ selectList.appendChild(option);
+ assert_true(selectList.checkValidity(), "Always valid when the selectlist isn't a required value.");
+
+ selectList.required = true;
+ assert_equals(selectList.value, "");
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist.");
+
+ let emptyOption = document.createElement('option');
+ selectList.appendChild(emptyOption);
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist even if there are multiple options.");
+ emptyOption.selected = true;
+ assert_true(selectList.checkValidity(), "An empty non-placeholder option should be a valid choice.");
+
+ let filledOption = document.createElement('option');
+ filledOption.value = "test";
+ selectList.appendChild(filledOption);
+ filledOption.selected = true;
+ assert_equals(selectList.value, "test", "The non-empty value should be set.");
+ assert_true(selectList.checkValidity(), "A non-empty non-placeholder option should be a valid choice.");
+
+ selectList.removeChild(option);
+ selectList.appendChild(emptyOption);
+ emptyOption.selected = true;
+ assert_equals(selectList.value, "", "The empty value should be set.");
+ assert_true(selectList.checkValidity(), "Only the first option can be seen as a placeholder.");
+
+ selectList.removeChild(filledOption);
+ assert_false(selectList.checkValidity(), "A selected placeholder option should invalidate the selectlist.");
+
+ emptyOption.value = "test2";
+ assert_equals(selectList.value, "test2");
+ assert_true(selectList.checkValidity(), "A non-empty option value should be a valid choice.");
+
+ emptyOption.removeAttribute("value");
+ assert_equals(selectList.value, "");
+ assert_false(selectList.checkValidity());
+ emptyOption.innerText = "test";
+ assert_equals(selectList.value, "test");
+ assert_true(selectList.checkValidity(), "A non-empty option should be a valid choice.");
+
+ const selectList1 = document.getElementById('selectlist1');
+ assert_equals(selectList1.value, "one");
+ assert_true(selectList1.checkValidity(), "A selectlist with non-empty placeholder option should be valid.");
+}, "Validation for placeholder option");
+
+test(() => {
+ const selectList2 = document.getElementById('selectlist2');
+ assert_equals(selectList2.value, "");
+ assert_false(selectList2.checkValidity());
+ let form = document.querySelector('form');
+ let invalidControl = form.querySelector('selectlist:invalid');
+ assert_equals(selectList2, invalidControl);
+ let didDispatchInvalid = false;
+ invalidControl.addEventListener('invalid', e => { didDispatchInvalid = true; });
+ let didDispatchSubmit = false;
+ form.addEventListener('submit', event => { event.preventDefault(); didDispatchSubmit = true; });
+
+ form.requestSubmit();
+ assert_true(didDispatchInvalid);
+ assert_false(didDispatchSubmit);
+}, "Check form not submitted for invalid selectlist");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html
new file mode 100644
index 0000000000..243067937c
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-option.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/664">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id=selectlist>
+ <option id=optone>innertext one</option>
+ <option id=opttwo value=valueattribute>innertext two</option>
+</selectlist>
+
+<script>
+test(() => {
+ assert_equals(selectlist.value, 'innertext one',
+ 'The first option should be selected initially.');
+ selectlist.value = 'valueattribute';
+ assert_equals(selectlist.value, 'valueattribute',
+ 'Assigning value should look at the options value, not innertext');
+}, 'selectlist.value should reflect option.value');
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html
new file mode 100644
index 0000000000..20e72ac1dc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-value-selectedOption.tentative.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<title>HTMLSelectListElement Test: value and selectedOption</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<selectlist id="selectList0"></selectlist>
+
+<selectlist id="selectList1">
+ <option>one</option>
+ <option>two</option>
+ <div>I'm a div with no part attr</div>
+ <option id="selectList1-option3">three</option>
+ <option>four</option>
+</selectlist>
+
+<selectlist id="selectList2">
+ <div behavior="option">one</div>
+ <div behavior="option">two</div>
+ <div>I'm a div with no part attr</div>
+ <div behavior="option">three</div>
+ <div behavior="option">four</div>
+</selectlist>
+
+<selectlist id="selectList3">
+ <div>I'm a div with no part attr</div>
+ <option id="selectList3-child1">one</option>
+ <option id="selectList3-child2">two</option>
+ <option id="selectList3-child3">three</option>
+</selectlist>
+
+<selectlist id="selectList4">
+ <div slot="button" behavior="button">
+ <div behavior="selected-value" id="selectList4-custom-selected-value">Default custom selected-value text</div>
+ </div>
+ <option id="selectList4-option1">one</option>
+ <option id="selectList4-option2">two</option>
+</selectlist>
+
+<selectlist id="selectList5">
+ <div slot="button" behavior="button">
+ <div behavior="selected-value" id="selectList5-custom-selected-value">Default custom selected-value text</div>
+ </div>
+ <div popover slot="listbox" behavior="listbox">
+ <option id="selectList5-option1">one</option>
+ <option id="selectList5-option2">two</option>
+ </div>
+</selectlist>
+
+<selectlist id="selectList6">
+ <option id="selectList6-option1">one</option>
+ <option id="selectList6-option2" selected>two</option>
+ <option id="selectList6-option3">three</option>
+</selectlist>
+
+<selectlist id="selectList7">
+ <option id="selectList7-option1">one</option>
+ <option id="selectList7-option2" selected value="test">two</option>
+ <option>three</option>
+</selectlist>
+
+<script>
+
+test(() => {
+ const selectList0 = document.getElementById("selectList0");
+ assert_equals(selectList0.value, "");
+ assert_equals(selectList0.selectedOption, null);
+ selectList0.value = "something";
+ assert_equals(selectList0.value, "", "If there is no matching option, selectlist should be cleared");
+ assert_equals(selectList0.selectedOption, null);
+}, "Test that HTMLSelectList with no options has empty string for value and null for selectedOption");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ assert_equals(selectList1.value, "one", "value should start with the text of the first option part");
+
+ selectList1.value = "three";
+ assert_equals(selectList1.value, "three", "value can be set to the text of an option part");
+ assert_equals(selectList1.selectedOption, document.getElementById("selectList1-option3"));
+
+ selectList1.value = "I'm a div with no part attr";
+ assert_equals(selectList1.value, "", "If there is no matching option selectlist should be cleared");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption with HTMLOptionElement element option parts");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ selectList1.value = "one";
+ assert_equals(selectList1.value, "one");
+
+ selectList1.value = null;
+ assert_equals(selectList1.value, "");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption when value is null");
+
+test(() => {
+ const selectList1 = document.getElementById("selectList1");
+ selectList1.value = "one";
+ assert_equals(selectList1.value, "one");
+
+ selectList1.value = undefined;
+ assert_equals(selectList1.value, "");
+ assert_equals(selectList1.selectedOption, null);
+}, "Test value and selectedOption when value is undefined");
+
+test(() => {
+ const selectList2 = document.getElementById("selectList2");
+ assert_equals(selectList2.value, "", "Non-HTMLOptionElements shouldn't be treated as option parts");
+ assert_equals(selectList2.selectedOption, null);
+
+ selectList2.value = "three";
+ assert_equals(selectList2.value, "", "value can't be set when there are no option parts'");
+ assert_equals(selectList2.selectedOption, null);
+}, "Test value with non-HTMLOptionElement elements labeled as parts");
+
+test(() => {
+ const selectList3 = document.getElementById("selectList3");
+ assert_equals(selectList3.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child1"));
+
+ document.getElementById("selectList3-child3").remove();
+ assert_equals(selectList3.value, "one", "Removing a non-selected option should not change the value");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child1"));
+
+ document.getElementById("selectList3-child1").remove();
+ assert_equals(selectList3.value, "two", "When the selected option is removed, the new first option should become selected");
+ assert_equals(selectList3.selectedOption, document.getElementById("selectList3-child2"));
+
+ document.getElementById("selectList3-child2").remove();
+ assert_equals(selectList3.value, "", "When all options are removed, value should be the empty string");
+ assert_equals(selectList3.selectedOption, null);
+}, "Test that value and selectedOption are updated when options are removed");
+
+test(() => {
+ const selectList4 = document.getElementById("selectList4");
+ let customSelectedValuePart = document.getElementById("selectList4-custom-selected-value");
+ assert_equals(selectList4.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList4.selectedOption, document.getElementById("selectList4-option1"));
+ assert_equals(customSelectedValuePart.innerText, "one", "Custom selected value part should be set to initial value of selectlist");
+
+ selectList4.value = "two";
+ assert_equals(customSelectedValuePart.innerText, "two", "Custom selected value part should be updated when value of selectlist changes");
+ assert_equals(selectList4.selectedOption, document.getElementById("selectList4-option2"));
+}, "Test that slotted-in selected-value part is updated to value of selectlist");
+
+test(() => {
+ const selectList5 = document.getElementById("selectList5");
+ let customSelectedValuePart = document.getElementById("selectList5-custom-selected-value");
+ assert_equals(selectList5.value, "one", "value should start with the text of the first option part");
+ assert_equals(selectList5.selectedOption, document.getElementById("selectList5-option1"));
+ assert_equals(customSelectedValuePart.innerText, "one", "Custom selected value part should be set to initial value of selectlist");
+
+ selectList5.value = "two";
+ assert_equals(customSelectedValuePart.innerText, "two", "Custom selected value part should be updated when value of selectlist changes");
+ assert_equals(selectList5.selectedOption, document.getElementById("selectList5-option2"));
+}, "Test that option parts in a slotted-in listbox are reflected in the value property");
+
+test(() => {
+ let selectList = document.createElement('selectlist');
+ assert_equals(selectList.value, "");
+ let option = document.createElement('option');
+ option.innerText = "one";
+ selectList.appendChild(option);
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, option);
+
+ let newOption = document.createElement('option');
+ newOption.innerText = 'two';
+ selectList.appendChild(newOption);
+ selectList.value = "two";
+ assert_equals(selectList.value, "two");
+ assert_equals(selectList.selectedOption, newOption);
+
+ option.click();
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, option);
+}, "Test that value and selectedOption are correctly updated");
+
+test(() => {
+ const selectList = document.getElementById("selectList6");
+ let selectListOption1 = document.getElementById("selectList6-option1");
+
+ assert_equals(selectList.value, "two");
+ assert_equals(selectList.selectedOption, document.getElementById("selectList6-option2"));
+ assert_false(selectListOption1.selected);
+ selectListOption1.selected = true;
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+
+ let newOption = document.createElement("option");
+ newOption.innerText = "four";
+ newOption.selected = true;
+ selectList.appendChild(newOption);
+ assert_equals(selectList.value, "four");
+ assert_equals(selectList.selectedOption, newOption);
+ assert_false(selectListOption1.selected);
+
+ selectList.value = "three";
+ assert_equals(selectList.selectedOption, document.getElementById("selectList6-option3"));
+ assert_false(newOption.selected);
+}, "Test that HTMLOption.selected updates selectlist.value and selectlist.selectedOption");
+
+test(() => {
+ const selectList = document.getElementById("selectList7");
+ let selectListOption1 = document.getElementById("selectList7-option1");
+
+ assert_equals(selectList.value, "test");
+ assert_equals(selectList.selectedOption, document.getElementById("selectList7-option2"));
+ assert_false(selectListOption1.selected);
+ selectListOption1.selected = true;
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+
+ selectListOption1.value = "new test";
+ assert_equals(selectList.value, "new test");
+ assert_equals(selectList.selectedOption, selectListOption1);
+ selectListOption1.removeAttribute("value");
+ assert_equals(selectList.value, "one");
+ assert_equals(selectList.selectedOption, selectListOption1);
+ selectListOption1.value = "";
+ assert_equals(selectList.value, "");
+ assert_equals(selectList.selectedOption, selectListOption1);
+}, "Test that HTMLOption.value updates selectlist.value");
+
+</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html
new file mode 100644
index 0000000000..9973696ddd
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-lr.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/600">
+<link rel=mismatch href="selectlist-writingmode-tb-ref.html">
+
+<selectlist style="writing-mode: vertical-lr">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html
new file mode 100644
index 0000000000..dc74203e69
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-rl.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/600">
+<link rel=mismatch href="selectlist-writingmode-tb-ref.html">
+
+<selectlist style="writing-mode: vertical-rl">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html
new file mode 100644
index 0000000000..db922f5f9f
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/selectlist-writingmode-tb-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<selectlist style="writing-mode: horizontal-tb">
+ <option>hello</option>
+</selectlist>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html
new file mode 100644
index 0000000000..9cc5a1d603
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/back.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script>history.back()</script>
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js
new file mode 100644
index 0000000000..77b3f74921
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/fake-selectlist.js
@@ -0,0 +1,112 @@
+function replaceChildElement(newChild, oldChild) {
+ oldChild.parentElement.replaceChild(newChild, oldChild);
+}
+
+function createFakeSelectlist(selectedValueText, includeListbox) {
+ const selectlist = document.createElement('div');
+ selectlist.classList.add('fake-selectlist');
+ selectlist.innerHTML = `
+ <button class="fake-selectlist-internal-selectlist-button">
+ <div class="fake-selectlist-selected-value"></div>
+ <div class="fake-selectlist-internal-selectlist-button-icon"></div>
+ </button>
+ `;
+ if (includeListbox) {
+ const listbox = document.createElement('div');
+ listbox.classList.add('fake-selectlist-listbox');
+ listbox.anchorElement = selectlist;
+ selectlist.appendChild(listbox);
+ }
+ selectlist.appendChild(createFakeSelectlistStyles());
+
+ const selectedvalue = selectlist.querySelector('.fake-selectlist-selected-value');
+ if (selectedValueText) {
+ selectedvalue.textContent = selectedValueText;
+ }
+
+ return selectlist;
+}
+
+function createFakeSelectlistStyles() {
+ const style = document.createElement('style');
+ style.textContent = `
+ .fake-selectlist {
+ display: inline-flex;
+ font-family: sans-serif;
+ font-size: 0.875em;
+ user-select: none;
+ }
+
+ .fake-selectlist-internal-selectlist-button {
+ color: fieldtext;
+ background-color: field;
+ appearance: none;
+ cursor: default;
+ font-size: inherit;
+ text-align: inherit;
+ display: inline-flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ align-items: center;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ padding: 0.25em;
+ border-width: 1px;
+ border-style: solid;
+ border-color: buttonborder;
+ border-image: initial;
+ border-radius: 0.25em;
+ }
+
+ .fake-selectlist-selected-value {
+ color: FieldText;
+ flex-grow:1;
+ }
+
+ .fake-selectlist-internal-selectlist-button-icon {
+ background-image: url(support/selectlist_button_icon.svg);
+ background-origin: content-box;
+ background-repeat: no-repeat;
+ background-size: contain;
+ height: 1.0em;
+ margin-inline-start: 4px;
+ opacity: 1;
+ outline: none;
+ padding-bottom: 2px;
+ padding-inline-start: 3px;
+ padding-inline-end: 3px;
+ padding-top: 2px;
+ width: 1.2em;
+ }
+
+ .fake-selectlist-listbox {
+ font-family: sans-serif;
+ box-shadow: rgba(0, 0, 0, 0.13) 0px 12.8px 28.8px, rgba(0, 0, 0, 0.11) 0px 0px 9.2px;
+ box-sizing: border-box;
+ background-color: canvas;
+ min-inline-size: anchor-size(self-inline);
+ min-block-size: 1lh;
+ position: fixed;
+ width: fit-content;
+ height: fit-content;
+ color: canvastext;
+ overflow: auto;
+ border-width: initial;
+ border-style: solid;
+ border-color: initial;
+ border-image: initial;
+ border-radius: 0.25em;
+ padding: 0.25em;
+ margin: 0px;
+ inset: auto;
+
+ top: anchor(bottom);
+ }
+
+ .fake-selectlist option {
+ font-size: 0.875em;
+ padding: 0.25em;
+ }
+ `;
+ return style;
+}
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg
new file mode 100644
index 0000000000..1a6c0193e8
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/support/selectlist_button_icon.svg
@@ -0,0 +1,3 @@
+<svg width="20" height="14" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">\
+ <path d="M4 6 L10 12 L 16 6" stroke="WindowText" stroke-width="3" stroke-linejoin="round"/>\
+</svg> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html
new file mode 100644
index 0000000000..1706ed34cc
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/forms/the-selectlist-element/tab-closes-listbox.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/openui/open-ui/issues/599">
+<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1359089">
+<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>
+
+<selectlist id=defaultlistbox>
+ <option>one</option>
+ <option>two</option>
+</selectlist>
+
+<selectlist id=customlistbox>
+ <listbox>
+ <option>one</option>
+ <option>two</option>
+ </listbox>
+</selectlist>
+
+<script>
+const tabKey = '\uE004';
+
+document.querySelectorAll('selectlist').forEach(selectlist => {
+ promise_test(async () => {
+ selectlist.focus();
+ await test_driver.send_keys(selectlist, ' ');
+ assert_true(selectlist.open, 'Listbox should be open after pressing space.');
+
+ selectlist.addEventListener('keydown', event => {
+ if (event.key === 'Tab') {
+ event.preventDefault();
+ }
+ }, {once: true});
+ await test_driver.send_keys(document.activeElement, tabKey);
+ assert_true(selectlist.open, 'Listbox should stay open when the tab keydown is preventDefaulted.');
+
+ await test_driver.send_keys(document.activeElement, tabKey);
+ assert_false(selectlist.open, 'Listbox should close after pressing the tab key.');
+ }, `${selectlist.id}: Pressing tab should close the listbox.`);
+});
+</script>