summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/interaction/focus/processing-model
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/interaction/focus/processing-model')
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html109
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html65
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html17
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/legend.html20
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html62
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html40
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html76
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html6
-rw-r--r--testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html46
9 files changed, 441 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html b/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html
new file mode 100644
index 0000000000..2413fe2667
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/focus-fixup-rule-one-no-dialogs.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Focus fixup rule one (no &lt;dialog>s involved)</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#attr-fieldset-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>
+ <button id="button1">Button 1</button>
+ <button id="button2">Button 2</button>
+ <button id="button3">Button 3</button>
+ <fieldset id="fieldset1"><button id="button4">Button 4</button></fieldset>
+ <fieldset id="fieldset2" disabled><legend><button id="button5">Button 5</button></legend></fieldset>
+ <div id="div" tabindex="0">Div</div>
+ <div id="editable" contenteditable=true>editor</div>
+</div>
+
+<script>
+"use strict";
+
+test(() => {
+ const button = document.querySelector("#button1");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.disabled = true;
+
+ assert_not_equals(document.activeElement, button, "After disabling, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling, the body must be focused");
+
+}, "Disabling the active element (making it inert)");
+
+test(() => {
+ const button = document.querySelector("#button2");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.hidden = true;
+
+ assert_not_equals(document.activeElement, button, "After hiding, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After hiding, the body must be focused");
+
+}, "Hiding the active element");
+
+test(() => {
+ const button = document.querySelector("#button3");
+ button.focus();
+
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ button.remove();
+
+ assert_not_equals(document.activeElement, button, "After removing, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After removing, the body must be focused");
+
+}, "Removing the active element from the DOM");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset1");
+ const button = document.querySelector("#button4");
+ button.focus();
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ fieldset.disabled = true;
+
+ assert_not_equals(document.activeElement, button, "After disabling ancestor fieldset, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling ancestor fieldset, the body must be focused");
+}, "Disabling <fieldset> affects its descendants");
+
+test(() => {
+ const fieldset = document.querySelector("#fieldset2");
+ const button = document.querySelector("#button5");
+ button.focus();
+ assert_equals(document.activeElement, button, "Sanity check: the button must start focused");
+
+ fieldset.insertBefore(document.createElement("legend"), fieldset.firstChild);
+
+ assert_not_equals(document.activeElement, button, "After changing a legend element, the button must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After changing a legend element, the body must be focused");
+}, "Changing the first legend element in disabled <fieldset>");
+
+test(() => {
+ const div = document.querySelector("#div");
+ div.focus();
+
+ assert_equals(document.activeElement, div, "Sanity check: the div must start focused");
+
+ div.removeAttribute("tabindex");
+
+ assert_not_equals(document.activeElement, div, "After removing tabindex, the div must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After removing tabindex, the body must be focused");
+
+}, "Removing the tabindex attribute from a div");
+
+test(() => {
+ const div = document.querySelector("#editable");
+ div.focus();
+ assert_equals(document.activeElement, div, "Sanity check: the div must start focused");
+
+ div.contentEditable = false;
+
+ assert_not_equals(document.activeElement, div, "After disabling contentEditable, the div must no longer be focused");
+ assert_equals(document.activeElement, document.body, "After disabling contentEditable, the body must be focused");
+}, "Disabling contenteditable");
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html b/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html
new file mode 100644
index 0000000000..aa7e66fffe
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/focusVisible.html
@@ -0,0 +1,65 @@
+<!doctype html>
+
+<title>focus(options) - focusVisible</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>
+
+<style>
+ div {
+ height: 10px;
+ border: 1px solid black;
+ }
+</style>
+
+<button>ABC</button>
+<input>
+<div id="contenteditable" contenteditable></div>
+<div id="tabindex" tabindex=0></div>
+<div id="not_focusable"></div>
+
+<div id="reset_focus" tabindex=0></div>
+
+<script>
+const reset_focus = document.getElementById("reset_focus");
+
+async function check_focus_visible(element, options, { shouldBeVisible, shouldBeFocused }) {
+ // Reset focus by clicking on a div, which should not show focus rings.
+ await test_driver.click(reset_focus);
+
+ assert_equals(document.activeElement, reset_focus, "Reset should be focused");
+ assert_true(reset_focus.matches(":focus"), "Clicking focusable div should match :focus");
+ assert_false(reset_focus.matches(":focus-visible"), "Clicking focusable div shouldn't match :focus-visible");
+
+ element.focus(options);
+
+ assert_equals(document.activeElement, shouldBeFocused ? element : reset_focus, "activeElement");
+ assert_equals(element.matches(":focus"), shouldBeFocused, ":focus");
+ assert_equals(element.matches(":focus-visible"), shouldBeVisible, ":focus-visible");
+}
+
+for (let selector of ["button", "input", "#contenteditable", "#tabindex", "#not_focusable"]) {
+ promise_test(async function() {
+ const takesKeyboardInput = selector == "#contenteditable" || selector == "input";
+ const shouldBeFocused = selector != "#not_focusable";
+
+ const element = document.querySelector(selector);
+
+ await check_focus_visible(element, {}, {
+ shouldBeVisible: takesKeyboardInput,
+ shouldBeFocused,
+ });
+
+ await check_focus_visible(element, { focusVisible: true }, {
+ shouldBeVisible: shouldBeFocused,
+ shouldBeFocused,
+ });
+ await check_focus_visible(element, { focusVisible: false }, {
+ shouldBeVisible: false,
+ shouldBeFocused,
+ });
+ }, "FocusOptions.focusVisible: " + selector);
+}
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html b/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html
new file mode 100644
index 0000000000..c9209d3cf6
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/legend-focusable.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>legend focusable</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ const t = async_test();
+</script>
+<fieldset>
+ <legend tabindex=0 onfocus="t.step(() => { t.done(); })">
+ legend
+ <input onfocus="t.unreached_func('input in legend was focused')();">
+ </legend>
+ <input onfocus="t.unreached_func('input after legend was focused')();">
+</fieldset>
+<script>
+ document.querySelector('legend').focus();
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html b/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html
new file mode 100644
index 0000000000..b53839374d
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/legend.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>legend</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ const t = async_test();
+</script>
+<fieldset>
+ <legend onfocus="t.unreached_func('legend was focused')()">
+ legend
+ <input onfocus="t.unreached_func('input in legend was focused')();">
+ </legend>
+ <input onfocus="t.unreached_func('input after legend was focused')();">
+</fieldset>
+<script>
+ document.querySelector('legend').focus();
+ t.step_timeout(() => {
+ t.done();
+ }, 500);
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html
new file mode 100644
index 0000000000..a7ae76f733
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-nested-scroll-elements.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>focus(options) - preventScroll on nested scroll elements</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .scrollBox { width: 400px; height: 300px; border: 1px solid }
+ .bigbox { width: 380px; height: 300px; border: 1px solid }
+ .box { width: 380px; height: 150px; border: 1px solid }
+</style>
+<div class="scrollBox" style="overflow-y: scroll;">
+ <div class="bigbox" id="1" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="1_1" tabindex=1>1_1</div>
+ <div class="box" id="1_2" tabindex=1>1_2</div>
+ <div class="box" id="1_3" tabindex=1>1_3</div>
+ </div>
+ <div class="bigbox" id="2" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="2_1" tabindex=1>2_1</div>
+ <div class="box" id="2_2" tabindex=1>2_2</div>
+ <div class="box" id="2_3" tabindex=1>2_3</div>
+ </div>
+ <div class="bigbox" id="3" style="overflow-y: scroll;" tabindex=1>
+ <div class="box" id="3_1" tabindex=1>3_1</div>
+ <div class="box" id="3_2" tabindex=1>3_2</div>
+ <div class="box" id="3_3" tabindex=1>3_3</div>
+ </div>
+</div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let div2_2 = document.getElementById("2_2");
+ div2_2.focus({ preventScroll: true });
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_equals(document.activeElement, div2_2, `box 2_2: should have been focused`);
+ assert_equals(div2_2.scrollTop, 0, "box 2_2: should not have scrolled");
+ assert_equals(div2_2.parentNode.scrollTop, 0, "box 2_2: should not have scrolled ancestor");
+
+ // Reset focus
+ let div1_1 = document.getElementById("1_1");
+ div1_1.focus();
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+ assert_equals(document.activeElement, div1_1, `box 1_1: should have been focused`);
+
+ let div2 = document.getElementById("2");
+ div2.focus({ preventScroll: true });
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_equals(document.activeElement, div2, `box 2: should have been focused`);
+ assert_equals(div2.scrollTop, 0, "box 2: should not have scrolled");
+ assert_equals(div2_2.scrollTop, 0, "box 2_2: should not have scrolled");
+ assert_equals(div2.parentNode.scrollTop, 0, "box 2: should not have scrolled ancestor");
+});
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html
new file mode 100644
index 0000000000..446284b186
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll-textarea.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>focus(options) - preventScroll on textarea element</title>
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1634153">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div style="height: 200vh"></div>
+<textarea>ABCD</textarea>
+<input value="EFGH">
+<button></button>
+<div style="height: 200vh"></div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let elements = document.querySelectorAll("textarea, input, button");
+ assert_equals(elements.length, 3, `Precondition`);
+ for (let element of elements) {
+ let name = element.nodeName;
+ assert_equals(window.scrollY, 0, `${name}: Precondition`);
+ element.focus({ preventScroll: true });
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled`);
+ assert_equals(document.activeElement, element, `${name}: Should have been focused`);
+
+ // Wait a couple event loop turns because the original bug was triggered off
+ // an async event.
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ await new Promise(resolve => t.step_timeout(resolve, 0));
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled after a couple event loop ticks`);
+ assert_equals(document.activeElement, element, `${name}: Should remain focused`);
+
+ // Also wait for rendering, just out of paranoia.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ assert_equals(window.scrollY, 0, `${name}: Should not have scrolled after rendering`);
+ assert_equals(document.activeElement, element, `${name}: Should remain focused after rendering`);
+ }
+}, "preventScroll: true on a textarea element");
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html
new file mode 100644
index 0000000000..97d341b30e
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/preventScroll.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<title>focus(options) - preventScroll</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#iframe { width: 500px; height: 500px; border: none }
+</style>
+<iframe id=iframe src="support/preventScroll-helper.html"></iframe>
+<script>
+function isEntirelyInView(elm, win) {
+ const inViewHorizontal = (elm.offsetLeft >= win.scrollX) &&
+ ((elm.offsetLeft + elm.clientWidth) <= (win.scrollX + win.innerWidth));
+ const inViewVertical = (elm.offsetTop >= win.scrollY) &&
+ ((elm.offsetTop + elm.clientHeight) <= (win.scrollY + win.innerHeight));
+ return inViewHorizontal && inViewVertical;
+}
+
+setup({explicit_done: true});
+
+function resetState(win) {
+ win.scrollTo(0, 0);
+ win.document.activeElement.blur();
+}
+
+onload = () => {
+ const win = document.getElementById('iframe').contentWindow;
+ const elm = win.document.getElementById('button');
+
+ test(() => {
+ assert_false(isEntirelyInView(elm, win), 'initial state');
+ elm.scrollIntoView();
+ assert_true(isEntirelyInView(elm, win), 'after elm.scrollIntoView()');
+ resetState(win);
+ assert_false(isEntirelyInView(elm, win), 'after resetScrollPosition(win)');
+ }, 'Sanity test');
+
+ test(() => {
+ resetState(win);
+ elm.focus();
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus() without arguments');
+
+ test(() => {
+ resetState(win);
+ elm.focus(undefined);
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus(undefined)');
+
+ test(() => {
+ resetState(win);
+ elm.focus(null);
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus(null)');
+
+ test(() => {
+ resetState(win);
+ elm.focus({});
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus({})');
+
+ test(() => {
+ resetState(win);
+ elm.focus({preventScroll: false});
+ assert_true(isEntirelyInView(elm, win));
+ }, 'elm.focus({preventScroll: false})');
+
+ test(() => {
+ resetState(win);
+ elm.focus({preventScroll: true});
+ assert_equals(win.scrollX, 0);
+ assert_equals(win.scrollY, 0);
+ }, 'elm.focus({preventScroll: true})');
+
+ done();
+}
+</script>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html b/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html
new file mode 100644
index 0000000000..43c6d86a57
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/support/preventScroll-helper.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>Helper document for preventScroll test</title>
+<style>
+body { padding: 2000px }
+</style>
+<button id=button>X</button>
diff --git a/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html b/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html
new file mode 100644
index 0000000000..c8e252f7da
--- /dev/null
+++ b/testing/web-platform/tests/html/interaction/focus/processing-model/textarea-scroll-selection.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<title>programatic focus() scrolls selection into view including ancestors</title>
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1644366">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div style="overflow: auto; height: 100px">
+ <textarea style="overflow: hidden; height: 200px">
+ Some text
+ That is surely more
+ Than 100px tall
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ For sure.
+ </textarea>
+</div>
+<script>
+promise_test(async function(t) {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ let textarea = document.querySelector("textarea");
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length);
+ textarea.focus();
+
+ await new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+
+ assert_not_equals(textarea.parentNode.scrollTop, 0, "Should've scrolled ancestor to show the selection");
+});
+</script>