Some document.write() WPTs made the assumption that a setTimeout() in the parent window will run after the load event in the child frame, and there's no such guarantee per spec. The focus fixup rule test similarly relied on the rendering not being suppressed by the time the test run. Do that by awaiting an animation frame, not just a timeout. Differential Revision: https://phabricator.services.mozilla.com/D301259
118 lines
3.8 KiB
HTML
118 lines
3.8 KiB
HTML
<!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>
|
|
<button id="button6">Button 6</button>
|
|
</div>
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
function test_focus_fixup(selector, change, expectSync = false) {
|
|
promise_test(async function(t) {
|
|
// Make sure we're not running from a ResizeObserver / etc notification, and
|
|
// that the document is rendering already.
|
|
await new Promise(r => {
|
|
requestAnimationFrame(() => {
|
|
t.step_timeout(r, 0);
|
|
});
|
|
});
|
|
|
|
const el = document.querySelector(selector);
|
|
el.focus();
|
|
|
|
assert_equals(document.activeElement, el, `Sanity check: ${selector} must start focused`);
|
|
|
|
change(el);
|
|
|
|
{
|
|
const fn = expectSync ? assert_not_equals : assert_equals;
|
|
fn(document.activeElement, el, `active element ${expectSync ? "is" : "isn't"} fixed-up sync`);
|
|
}
|
|
|
|
// We don't expect blur events in the sync case per spec yet, at least.
|
|
if (!expectSync) {
|
|
// Fixup should run after animation frame callbacks, right before the end of
|
|
// "update the rendering", so after resize observations.
|
|
let ranFirstRaf = false;
|
|
let ranRO = false;
|
|
|
|
let resolveDone;
|
|
let done = new Promise(r => { resolveDone = r; });
|
|
|
|
requestAnimationFrame(t.step_func(() => {
|
|
ranFirstRaf = true;
|
|
assert_equals(document.activeElement, el, "activeElement shouldn't have changed yet (rAF)");
|
|
requestAnimationFrame(t.step_func(() => {
|
|
assert_true(ranRO, "ResizeObserver should've ran on the previous frame");
|
|
resolveDone();
|
|
}));
|
|
}));
|
|
|
|
let ro = new ResizeObserver(t.step_func(() => {
|
|
assert_true(ranFirstRaf, "requestAnimationFrame should run before ResizeObserver");
|
|
ranRO = true;
|
|
assert_equals(document.activeElement, el, "activeElement shouldn't have changed yet (ResizeObserver)");
|
|
}));
|
|
|
|
// TODO: Test IntersectionObserver timing. It's a bit trickier because it
|
|
// uses its own task source and so on.
|
|
ro.observe(document.documentElement);
|
|
|
|
await done;
|
|
|
|
ro.disconnect();
|
|
}
|
|
|
|
assert_not_equals(document.activeElement, el, "focus is fixed up");
|
|
assert_equals(document.activeElement, document.body, "Body is focused");
|
|
}, selector);
|
|
}
|
|
|
|
test_focus_fixup("#button1", function(button) {
|
|
button.disabled = true;
|
|
});
|
|
|
|
test_focus_fixup("#button2", function(button) {
|
|
button.hidden = true;
|
|
});
|
|
|
|
test_focus_fixup("#button3", function(button) {
|
|
button.remove();
|
|
}, /* expectSync = */ true);
|
|
|
|
test_focus_fixup("#button4", function(button) {
|
|
document.querySelector("#fieldset1").disabled = true;
|
|
});
|
|
|
|
test_focus_fixup("#button5", function(button) {
|
|
const fieldset = document.querySelector("#fieldset2");
|
|
fieldset.insertBefore(document.createElement("legend"), fieldset.firstChild);
|
|
});
|
|
|
|
test_focus_fixup("#div", function(div) {
|
|
div.removeAttribute("tabindex");
|
|
});
|
|
|
|
test_focus_fixup("#editable", function(div) {
|
|
div.contentEditable = false;
|
|
});
|
|
|
|
test_focus_fixup("#button6", function(button) {
|
|
button.style.visibility = "hidden";
|
|
});
|
|
</script>
|