diff options
Diffstat (limited to 'testing/web-platform/tests/html/interaction/focus/processing-model')
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 <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> |