Skip to content
Permalink
Browse files
Implement new autofocus behavior
https://bugs.webkit.org/show_bug.cgi?id=203139
<rdar://problem/56397019>

Reviewed by Wenson Hsieh.

Make WebKit match the new autofocus spec:
- https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute
- https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps

The new autofocus behavior queues up all autofocusable elements (visible or not), then
fires autofocus asynchronously during the "update rendering" steps, unlike the old behavior
which runs focus synchronously whenever it finds a visible autofocusable element.

Original patch by Ryosuke Niwa.

LayoutTests/imported/w3c:

* web-platform-tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-empty-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-nonexistent-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/document-with-fragment-top-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/first-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/first-when-later-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/queue-non-focusable-expected.txt:
* web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering-expected.txt:
* web-platform-tests/selection/selection-select-all-move-input-crash-expected.txt: Expected change since input autofocus now places caret at start

Source/WebCore:

* dom/Document.cpp:
(WebCore::Document::appendAutofocusCandidate):
(WebCore::Document::flushAutofocusCandidates):
* dom/Document.h:
(WebCore::Document::isAutofocusProcessed const):
(WebCore::Document::setAutofocusProcessed):
* dom/Element.cpp:
(WebCore::Element::runFocusingStepsForAutofocus):
* dom/Element.h:
* html/HTMLFormControlElement.cpp:
(WebCore::shouldAutofocus):
(WebCore::HTMLFormControlElement::didAttachRenderers):
(WebCore::HTMLFormControlElement::insertedIntoAncestor):
(WebCore::HTMLFormControlElement::runFocusingStepsForAutofocus):
* html/HTMLFormControlElement.h:
(WebCore::HTMLFormControlElement::hasAutofocused const): Deleted.
(WebCore::HTMLFormControlElement::setAutofocused): Deleted.
* page/Page.cpp:
(WebCore::Page::updateRendering):
* page/Page.h:

LayoutTests:

* TestExpectations:
* fast/dom/Window/window-scroll-ignore-null-frame.html:
* fast/dom/adopt-node-crash-2-expected.txt:
* fast/dom/adopt-node-crash-2.html:
* fast/dom/window-inner-width-crash.html:
* fast/forms/autofocus-in-sandbox-with-allow-scripts-expected.txt:
* fast/forms/autofocus-in-sandbox-with-allow-scripts.html:
* fast/forms/autofocus-keygen.html:
* fast/forms/autofocus-opera-001.html:
* fast/forms/autofocus-opera-002.html:
* fast/forms/autofocus-opera-003.html:
* fast/forms/autofocus-opera-006.html:
* fast/forms/autofocus-opera-007.html:
* fast/forms/change-input-type-in-focus-handler.html:
* fast/frames/crash-when-iframe-is-remove-in-eventhandler.html:
* fast/history/page-cache-execute-script-during-restore.html:
* platform/ios-wk2/TestExpectations:
* platform/ios-wk2/imported/w3c/web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering-expected.txt:
* platform/ios/imported/w3c/web-platform-tests/selection/selection-select-all-move-input-crash-expected.txt:
* platform/mac-wk1/imported/w3c/web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering-expected.txt:
* platform/win/TestExpectations:


Canonical link: https://commits.webkit.org/242794@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@283935 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
nt1m committed Oct 11, 2021
1 parent 2f8b2bf commit 04121307d367f18294594257e49265758e196716
Showing 40 changed files with 330 additions and 112 deletions.
@@ -1,3 +1,43 @@
2021-10-11 Tim Nguyen <ntim@apple.com>

Implement new autofocus behavior
https://bugs.webkit.org/show_bug.cgi?id=203139
<rdar://problem/56397019>

Reviewed by Wenson Hsieh.

Make WebKit match the new autofocus spec:
- https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute
- https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps

The new autofocus behavior queues up all autofocusable elements (visible or not), then
fires autofocus asynchronously during the "update rendering" steps, unlike the old behavior
which runs focus synchronously whenever it finds a visible autofocusable element.

Original patch by Ryosuke Niwa.

* TestExpectations:
* fast/dom/Window/window-scroll-ignore-null-frame.html:
* fast/dom/adopt-node-crash-2-expected.txt:
* fast/dom/adopt-node-crash-2.html:
* fast/dom/window-inner-width-crash.html:
* fast/forms/autofocus-in-sandbox-with-allow-scripts-expected.txt:
* fast/forms/autofocus-in-sandbox-with-allow-scripts.html:
* fast/forms/autofocus-keygen.html:
* fast/forms/autofocus-opera-001.html:
* fast/forms/autofocus-opera-002.html:
* fast/forms/autofocus-opera-003.html:
* fast/forms/autofocus-opera-006.html:
* fast/forms/autofocus-opera-007.html:
* fast/forms/change-input-type-in-focus-handler.html:
* fast/frames/crash-when-iframe-is-remove-in-eventhandler.html:
* fast/history/page-cache-execute-script-during-restore.html:
* platform/ios-wk2/TestExpectations:
* platform/ios-wk2/imported/w3c/web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering-expected.txt:
* platform/ios/imported/w3c/web-platform-tests/selection/selection-select-all-move-input-crash-expected.txt:
* platform/mac-wk1/imported/w3c/web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering-expected.txt:
* platform/win/TestExpectations:

2021-10-11 Ayumi Kojima <ayumi_kojima@apple.com>

[ iOS BigSur wk1 ] webgl/1.0.3/conformance/uniforms/uniform-default-values.html is flaky timing out.
@@ -806,7 +806,6 @@ imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/002.
imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/combination_history_002.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/combination_history_003.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/interaction/focus/the-autofocus-attribute/update-the-rendering.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/rendering/widgets/baseline-alignment-and-overflow.tentative.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-pointer-remove-source.html [ Failure Pass ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/cross-origin-to-whom.window.html [ Failure Pass ]
@@ -2,14 +2,20 @@
<script>

function runTest() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

select1.appendChild(inputParent);
input1.autofocus = true;
input1.setSelectionRange(1, 0);
document.body.appendChild(input1);
frame1.contentWindow.scrollBy({left: 1, top: 0});
requestAnimationFrame(() => {
frame1.contentWindow.scrollBy({left: 1, top: 0});
if (window.testRunner)
testRunner.notifyDone();
});
}

</script>
@@ -1,2 +1,4 @@
Tests for a crash due to adopting a DOM node during DOMFocusOut event. Test passes if it doesn't crash.
Tests for a crash due to adopting a DOM node when unfocusing. Test passes if it doesn't crash.

PASS: Focused node should be keygen
PASS: Focused node should no longer be keygen
@@ -1,6 +1,6 @@
<!DOCTYPE html><!-- webkit-test-runner [ KeygenElementEnabled=true ] -->
<html>
<div>Tests for a crash due to adopting a DOM node during DOMFocusOut event. Test passes if it doesn't crash.</div>
<div>Tests for a crash due to adopting a DOM node when unfocusing. Test passes if it doesn't crash.</div>
<script>
if (window.testRunner) {
testRunner.dumpAsText();
@@ -14,29 +14,32 @@
</iframe>
</applet>
<header id="header1">
<keygen autofocus>
<keygen id="keygen" autofocus>
</header>
</div>
<div id="logs"></div>
<script>
function doit()
{
div2.addEventListener("DOMFocusOut", function () {
document.implementation.createDocument("", "", null).adoptNode(div2);
setTimeout(() => {
if (window.internals)
internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
if (window.testRunner)
testRunner.notifyDone();
}, 0);
}, false);
div1.outerHTML = header1.outerHTML;
function assert_true(assert, text) {
const div = document.createElement("div");
if (assert)
div.textContent = "PASS: " + text;
else
div.textContent = "FAIL: " + text;
logs.append(div);
}
window.onload = function() {

function doit() {
assert_true(document.activeElement == keygen, "Focused node should be keygen");
div1.outerHTML = header1.outerHTML;
assert_true(document.activeElement != keygen, "Focused node should no longer be keygen");
document.implementation.createDocument("", "", null).adoptNode(div2);
if (window.internals)
internals.updateLayoutIgnorePendingStylesheetsAndRunPostLayoutTasks();
else
div2.getBoundingClientRect();
setTimeout("doit()", 1);
if (window.testRunner)
testRunner.notifyDone();
}
window.onload = function() {
requestAnimationFrame(doit);
};
</script>
</html>
@@ -6,8 +6,8 @@
testRunner.dumpAsText();

function runTest() {
button.autofocus = true;
body.appendChild(paragraph);
button.focus();
var testVal = window[0].innerWidth;
}

@@ -1,2 +1,7 @@
CONSOLE MESSAGE: INPUT
This test passes if the activeElement is the input element rather than the body (which it would be if the sandbox didn't allow autofocus although allow-scripts flag is set).
This tests having an input element with autofocus content attribute inside a sandboxed iframe.
The input element should be autofocused since scripts are allowed.

--------
Frame: '<!--frame1-->'
--------
PASS
@@ -1,8 +1,19 @@
<script>
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.dumpChildFramesAsText();
}
</script>
This test passes if the activeElement is the input element rather than the body
(which it would be if the sandbox didn't allow autofocus although allow-scripts flag is set).
<iframe sandbox="allow-scripts allow-modals"
src="data:text/html,<input autofocus onfocus><script>window.onload = function() { console.log(document.activeElement.tagName) }</script>"></iframe>
This tests having an input element with autofocus content attribute inside a sandboxed iframe.<br>
The input element should be autofocused since scripts are allowed.
<iframe sandbox="allow-scripts"
src="data:text/html,<input id=input autofocus onfocus><script>
if (window.testRunner)
testRunner.waitUntilDone();
window.onload = function () {
requestAnimationFrame(() => {
document.body.append(document.activeElement == input ? 'PASS' : 'FAIL');
if (window.testRunner)
testRunner.notifyDone();
});
}</script>"></iframe>
@@ -6,9 +6,11 @@
window.jsTestIsAsync = true;

function onLoad() {
shouldBe('document.activeElement', 'document.querySelector("keygen")');
shouldBe('document.activeElement.autofocus', 'true');
finishJSTest();
requestAnimationFrame(() => {
shouldBe('document.activeElement', 'document.querySelector("keygen")');
shouldBe('document.activeElement.autofocus', 'true');
finishJSTest();
});
}
</script>
</head>
@@ -11,13 +11,19 @@
}

function test() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

if (document.activeElement == document.getElementsByTagName("input")[0])
log("SUCCESS");
else
log("FAILURE");
requestAnimationFrame(() => {
if (document.activeElement == document.getElementsByTagName("input")[0])
log("SUCCESS");
else
log("FAILURE");
if (window.testRunner)
testRunner.notifyDone();
});
}
</script>
</head>
@@ -11,13 +11,19 @@
}

function test() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

if (document.activeElement == document.getElementsByTagName("input")[0])
log("SUCCESS");
else
log("FAILURE");
requestAnimationFrame(() => {
if (document.activeElement == document.getElementsByTagName("input")[0])
log("SUCCESS");
else
log("FAILURE");
if (window.testRunner)
testRunner.notifyDone();
});
}
</script>
</head>
@@ -12,14 +12,20 @@
}

function test() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

if (document.activeElement == document.getElementsByTagName("input")[0] &&
document.scrollingElement.scrollTop != 0)
log("SUCCESS");
else
log("FAILURE");
requestAnimationFrame(() => {
if (document.activeElement == document.getElementsByTagName("input")[0] &&
document.scrollingElement.scrollTop != 0)
log("SUCCESS");
else
log("FAILURE");
if (window.testRunner)
testRunner.notifyDone();
});
}
</script>
</head>
@@ -11,23 +11,30 @@
}

function test() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

requestAnimationFrame(() => {
if (document.activeElement == document.getElementById("test"))
log("SUCCESS");
else
log("FAILURE");
if (window.testRunner)
testRunner.notifyDone();
});

if (document.activeElement == document.getElementById("test"))
log("SUCCESS");
else
log("FAILURE");
}
</script>
</head>
<body onload="test()">
<p>All form controls below should have a green background:</p>
<p><input autofocus>
<p><input autofocus id="test">
<p><input autofocus>
<p><input autofocus>
<p><input>
<p><input autofocus id="test">
<p><input autofocus>
<p><input>
<hr/>
<ol id="console"></ol>
@@ -17,13 +17,19 @@
}

function test() {
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

if (gotBlur)
log("SUCCESS");
else
log("FAILURE");
requestAnimationFrame(() => {
if (gotBlur)
log("SUCCESS");
else
log("FAILURE");
if (window.testRunner)
testRunner.notifyDone();
});
}
</script>
</head>
@@ -10,16 +10,25 @@
function focused() {
if (input.autofocus) {
input.type = "checkbox";
document.body.innerHTML = "<code style='color: green'>PASS</code>";
document.body.innerHTML = "FAIL - focus event handler called for autofocus";
if (window.testRunner)
testRunner.notifyDone();
return;
}

document.body.appendChild(input);
input.autofocus = true;
requestAnimationFrame(() => {
document.body.textContent = document.activeElement == document.body ? "PASS" : "FAIL";
if (window.testRunner)
testRunner.notifyDone();
});
}

if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

input.focus();
</script>
@@ -6,13 +6,20 @@
<span id=wrapper></span>
<textarea id=textarea onfocus="eventhandler()"></textarea>
<script>
if (window.testRunner)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

document.offsetHeight;
textarea.autofocus = true;
var iframe = document.createElement("iframe");
span.appendChild(iframe);
wrapper.appendChild(textarea);
requestAnimationFrame(() => {
if (window.testRunner)
testRunner.notifyDone();
});
iframe.contentDocument.caretRangeFromPoint();

function eventhandler() {

0 comments on commit 0412130

Please sign in to comment.