LibWeb: Implement autofocus candidate processing

This change implements the algorithms necessary to focus elements with
the autofocus attribute on page load.
This commit is contained in:
Tim Ledbetter
2026-04-21 18:17:38 +01:00
committed by Shannon Booth
parent baefb51902
commit e5d615cb11
Notes: github-actions[bot] 2026-04-21 21:48:16 +00:00
19 changed files with 345 additions and 4 deletions

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<input id="first" autofocus>
<input id="second" autofocus>
<script>
document.getElementById("first").remove();
asyncTest(done => {
requestAnimationFrame(() => {
println(`activeElement.id=${document.activeElement.id}`);
done();
});
});
</script>

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<meta charset="utf-8">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<input autofocus id="i1">
<input autofocus id="i2">
<script>
"use strict";
promise_test(async () => {
const input1 = document.querySelector("#i1");
const input2 = document.querySelector("#i2");
input1.remove();
input2.parentNode.insertBefore(input1, input2);
await waitUntilStableAutofocusState();
assert_equals(document.activeElement, input2);
}, 'The second autofocus element wins if the first autofocus element was ' +
'disconnected and reconnected before flushing the autofocus candidates.');
</script>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>The temporally first autofocus in the document wins, even if an element is inserted later that is previous in the document tree</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
<link rel="author" title="Domenic Denicola" href="d@domenic.me">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<input autofocus>
<script>
"use strict";
promise_test(async () => {
const input1 = document.querySelector("input");
const input2 = document.createElement("input");
input2.autofocus = true;
document.body.prepend(input2);
await waitUntilStableAutofocusState();
assert_equals(document.activeElement, input1);
assert_not_equals(document.activeElement, input2);
}, 'The temporally first autofocus in the document wins, even if an element is inserted later that is previous in the document tree.');
</script>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>The first autofocus in the document wins, even if elements are inserted later</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
<link rel="author" title="Domenic Denicola" href="d@domenic.me">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<input autofocus>
<script>
"use strict";
promise_test(async () => {
const input1 = document.querySelector("input");
const input2 = document.createElement("input");
input2.autofocus = true;
document.body.appendChild(input2);
await waitUntilStableAutofocusState();
assert_equals(document.activeElement, input1);
assert_not_equals(document.activeElement, input2);
}, 'The first autofocus in the document wins, even if elements are inserted later.');
</script>

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>The first autofocus in the document wins</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/#autofocusing-a-form-control:-the-autofocus-attribute">
<link rel="author" title="Domenic Denicola" href="d@domenic.me">
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<input autofocus>
<input autofocus>
<script>
"use strict";
promise_test(async () => {
const [input1, input2] = document.querySelectorAll("input");
await waitUntilStableAutofocusState();
assert_equals(document.activeElement, input1);
assert_not_equals(document.activeElement, input2);
}, 'The first autofocus element in the document should win.');
</script>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<textarea autofocus disabled></textarea>
<select autofocus></select>
<script>
'use strict';
promise_test(async () => {
const [textarea, select] = document.querySelectorAll('[autofocus]');
textarea.disabled = false;
await waitUntilStableAutofocusState();
assert_equals(document.activeElement, textarea);
assert_not_equals(document.activeElement, select);
}, 'If the first autofocus element is not focusable, but becomes focusable before a frame, it should be focused.');
</script>

View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
<textarea autofocus disabled></textarea>
<select autofocus></select>
<script>
'use strict';
promise_test(async () => {
const [textarea, select] = document.querySelectorAll('[autofocus]');
await waitUntilStableAutofocusState();
assert_not_equals(document.activeElement, textarea);
assert_equals(document.activeElement, select);
}, 'Non-focusable autofocus element is skipped.');
</script>