Title: [210267] trunk
Revision
210267
Author
[email protected]
Date
2017-01-03 19:39:39 -0800 (Tue, 03 Jan 2017)

Log Message

label element with tabindex >= 0 is not focusable
https://bugs.webkit.org/show_bug.cgi?id=102780
<rdar://problem/29796608>

Reviewed by Darin Adler.

Source/WebCore:

Fixed the bug by removing the override for HTMLLabelElement::isFocusable which always returned false.

This is a behavior from r5532 but it doesn't match the latest HTML specification or that of Chrome
and Firefox.

Also fixed an existing bug in HTMLLabelElement::focus and HTMLLegendElement::focus which focused
the associated form control when there is one even if the element itself is focusable. Without this fix,
traversing from control with shift+tab would break since focusing the label would move the focus back
to the input element inside the label element.

Finally, fixed a bug in HTMLLegendElement::focus that we can call inFocus without updating layout first.

The fix was inspired by https://chromium.googlesource.com/chromium/src/+/085ad8697b1be50c4f93e296797a25a43a79bcfb

Test: fast/events/focus-label-legend-elements-with-tabindex.html

* html/HTMLLabelElement.cpp:
(WebCore::HTMLLabelElement::focus):
(WebCore::HTMLLabelElement::isFocusable): Deleted.
* html/HTMLLabelElement.h:
* html/HTMLLegendElement.cpp:
(WebCore::HTMLLegendElement::focus):

LayoutTests:

Added a regression test for traversing label and legend elements by tabbing.
A native merge of the blink fix would have regressed this for the label element
while the bug in the legend element had always existed.

Also added a regression test for focusing label and legend elements with tabindex.
We should be able to focus either element. New behavior matches that of Chrome.
Firefox moves the focus to the label element like we used to before this patch.

Also merge the test fix from https://chromium.googlesource.com/chromium/src/+/085ad8697b1be50c4f93e296797a25a43a79bcfb

* fast/events/focus-label-legend-elements-expected.txt: Added.
* fast/events/focus-label-legend-elements-with-tab-expected.txt: Added.
* fast/events/focus-label-legend-elements-with-tab.html: Added.
* fast/events/focus-label-legend-elements.html: Added.
* fast/events/resources/tabindex-focus-blur-all-frame1.html:
* fast/events/resources/tabindex-focus-blur-all-frame2.html:
* fast/events/resources/tabindex-focus-blur-all.js:
* fast/events/tabindex-focus-blur-all-expected.txt:
* platform/ios-simulator-wk2/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (210266 => 210267)


--- trunk/LayoutTests/ChangeLog	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/ChangeLog	2017-01-04 03:39:39 UTC (rev 210267)
@@ -1,3 +1,31 @@
+2017-01-03  Ryosuke Niwa  <[email protected]>
+
+        label element with tabindex >= 0 is not focusable
+        https://bugs.webkit.org/show_bug.cgi?id=102780
+        <rdar://problem/29796608>
+
+        Reviewed by Darin Adler.
+
+        Added a regression test for traversing label and legend elements by tabbing.
+        A native merge of the blink fix would have regressed this for the label element
+        while the bug in the legend element had always existed.
+
+        Also added a regression test for focusing label and legend elements with tabindex.
+        We should be able to focus either element. New behavior matches that of Chrome.
+        Firefox moves the focus to the label element like we used to before this patch.
+
+        Also merge the test fix from https://chromium.googlesource.com/chromium/src/+/085ad8697b1be50c4f93e296797a25a43a79bcfb
+
+        * fast/events/focus-label-legend-elements-expected.txt: Added.
+        * fast/events/focus-label-legend-elements-with-tab-expected.txt: Added.
+        * fast/events/focus-label-legend-elements-with-tab.html: Added.
+        * fast/events/focus-label-legend-elements.html: Added.
+        * fast/events/resources/tabindex-focus-blur-all-frame1.html:
+        * fast/events/resources/tabindex-focus-blur-all-frame2.html:
+        * fast/events/resources/tabindex-focus-blur-all.js:
+        * fast/events/tabindex-focus-blur-all-expected.txt:
+        * platform/ios-simulator-wk2/TestExpectations:
+
 2017-01-03  Tim Horton  <[email protected]>
 
         NSSpellChecker's recordResponse isn't called for unseen automatic corrections

Added: trunk/LayoutTests/fast/events/focus-label-legend-elements-expected.txt (0 => 210267)


--- trunk/LayoutTests/fast/events/focus-label-legend-elements-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/focus-label-legend-elements-expected.txt	2017-01-04 03:39:39 UTC (rev 210267)
@@ -0,0 +1,12 @@
+Tests for calling focus() on a label element and a legend element. They should not move the focus to the associated element.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS testContent.querySelector("label").focus(); document.activeElement is testContent.querySelector("label")
+PASS testContent.querySelector("legend").focus(); document.activeElement is testContent.querySelector("legend")
+PASS successfullyParsed is true
+
+TEST COMPLETE
+ foo
+

Added: trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab-expected.txt (0 => 210267)


--- trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab-expected.txt	2017-01-04 03:39:39 UTC (rev 210267)
@@ -0,0 +1,18 @@
+Tests for moving focus by pressing tab key across label and legend elements.
+To manually test, press tab key seven times then shift+tab six times.
+It should traverse focusable elements in the ascending order from 1 to 7 and then in the descending order back to 1.
+
+
+1. An input element inside a non-focusable label element
+2. A label element with an input element
+3. An input elment inside a focusable label element
+4. A label element with just text
+6. A focusable legend element inside a fieldset element
+7. An input element inside a fieldset element
+7. An input element inside a fieldset element
+6. A focusable legend element inside a fieldset element
+4. A label element with just text
+3. An input elment inside a focusable label element
+2. A label element with an input element
+1. An input element inside a non-focusable label element
+

Added: trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab.html (0 => 210267)


--- trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/focus-label-legend-elements-with-tab.html	2017-01-04 03:39:39 UTC (rev 210267)
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Tests for moving focus by pressing tab key across label and legend elements.<br>
+To manually test, press tab key seven times then shift+tab six times.<br>
+It should traverse focusable elements in the ascending order from 1 to 7 and then in the descending order back to 1.</p>
+</p>
+<input id="startingNode">
+<div id="test-content">
+    <label title="FAIL - a non-focusable label with an input should not be focused">
+        <input title="1. An input element inside a non-focusable label element">hello
+    </label>
+    <label tabindex="0" title="2. A label element with an input element">
+        <input title="3. An input elment inside a focusable label element">world</label>
+    <label tabindex="0" title="4. A label element with just text">webkit</label>
+    <label title="FAIL - an label element without tabindex should not be focused">rocks</label>
+    <fieldset tabindex="0" title="5. A fieldset element with tabindex">
+        <legend tabindex="0" title="6. A focusable legend element inside a fieldset element">foo</legend>
+        <input title="7. An input element inside a fieldset element">
+    </fieldset>
+</div>
+<style> :focus { background: red; } </style>
+<pre id="result"></pre>
+<script>
+
+function moveFocusForward(focusCount)
+{
+    setTimeout(function () {
+        if (!focusCount)
+            return moveFocusBackward(7);
+        eventSender.keyDown('\t');
+        setTimeout(function () {
+            moveFocusForward(focusCount - 1);            
+        }, 1);
+    }, 1);
+}
+
+function moveFocusBackward(focusCount)
+{
+    setTimeout(function () {
+        if (!focusCount) {
+            testContent.style.display = 'none';
+            testRunner.notifyDone();
+            return;
+        }
+        eventSender.keyDown('\t', ['shiftKey']);
+        setTimeout(function () {
+            moveFocusBackward(focusCount - 1);            
+        }, 1);
+    }, 1);
+}
+
+let testContent = document.getElementById('test-content');
+
+window._onload_ = () => {
+    document.getElementById('startingNode').focus();
+    if (window.eventSender) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+    moveFocusForward(7);
+}
+
+testContent.querySelectorAll('*').forEach((element) => {
+    element._onfocus_ = function () {
+        document.getElementById('result').textContent += (element.title || 'FAIL - focused an unexpected element') + '\n';
+    }
+});
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/focus-label-legend-elements.html (0 => 210267)


--- trunk/LayoutTests/fast/events/focus-label-legend-elements.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/focus-label-legend-elements.html	2017-01-04 03:39:39 UTC (rev 210267)
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<div id="test-content">
+    <label tabindex="0"><input tabindex="0"></label>
+    <fieldset tabindex="0">
+        <legend tabindex="0">foo</legend>
+        <input tabindex="0">
+    </fieldset>
+</div>
+<script>
+
+description('Tests for calling focus() on a label element and a legend element. They should not move the focus to the associated element.');
+
+let testContent = document.getElementById('test-content');
+shouldBe('testContent.querySelector("label").focus(); document.activeElement', 'testContent.querySelector("label")');
+shouldBe('testContent.querySelector("legend").focus(); document.activeElement', 'testContent.querySelector("legend")');
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame1.html (210266 => 210267)


--- trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame1.html	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame1.html	2017-01-04 03:39:39 UTC (rev 210267)
@@ -154,6 +154,7 @@
 <input type="checkbox" id="checkbox1"><br>
 <input type="radio" id="radioChoice1a" name="radio1" tabindex="0"><br>
 <label id="label1" tabindex="-1"><input type="radio" name="radio1">label for radioChoice B</label><br>
+<label id="label2"><input type="radio" name="radio1">label for radioChoice C</label><br>
 <input type="file" id="file1" tabindex="2"><br>
 input type="hidden"<input type="hidden" id="hidden1" tabindex="-1"><br>
 input type="image"<input type="image" id="inputImage1" src="" tabindex="3"><br>

Modified: trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame2.html (210266 => 210267)


--- trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame2.html	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all-frame2.html	2017-01-04 03:39:39 UTC (rev 210267)
@@ -154,6 +154,7 @@
 <input type="checkbox" id="checkbox3" tabindex="0"><br>
 <input type="radio" id="radioChoice3a" name="radio3"><br>
 <label id="label3" tabindex="2"><input type="radio" name="radio3" tabindex="0">label for radio button</label><br>
+<label id="label4"><input type="radio" name="radio3" tabindex="0">another label for radio button</label><br>
 <input type="file" id="file3" tabindex="-1"><br>
 input type="hidden"<input type="hidden" id="hidden3" tabindex="3"><br>
 input type="image"<input type="image" id="inputImage3" src="" tabindex="-1"><br>

Modified: trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all.js (210266 => 210267)


--- trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all.js	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/fast/events/resources/tabindex-focus-blur-all.js	2017-01-04 03:39:39 UTC (rev 210267)
@@ -55,7 +55,7 @@
     var homeBase = window.frames[1].document.getElementsByClassName('homebase');
     homeBase[0].focus();
 
-    var resultSummary = focusCount+" focus / "+blurCount+" blur events dispatched, and should be 333 / 333 ";
+    var resultSummary = focusCount+" focus / "+blurCount+" blur events dispatched, and should be 336 / 336 ";
     resultSummary += (focusCount==blurCount) ? "<span style='color:green'>PASSED</span><br>" : "<span style='color:red'>FAILED</span><br>";
     resultSummary += "Total of "+failedTestCount+" focus test(s) failed.";
     if (failedTestCount)
@@ -101,7 +101,7 @@
 function testProgrammaticFocus(elem)
 {
     var elemThatShouldFocus = null;
-    var OKtoFocusOtherElement = false;
+    var shouldFocusOtherElement = false;
     focusedElem = null;
 
     if (elem.tabIndex == elem.getAttribute("tabindex")) // this means tabindex was explicitly set
@@ -117,13 +117,11 @@
     if (elem.tabIndex == -1 && elem.tagName == "AREA")
         elemThatShouldFocus = null;
 
-    if (tagNamesTransferFocused.find(elem.tagName)) {
-        elemThatShouldFocus = null;
-        OKtoFocusOtherElement = true;
-    }
+    if (tagNamesTransferFocused.find(elem.tagName) && elemThatShouldFocus == null)
+        shouldFocusOtherElement = true;
 
     elem.focus();
-    if (elemThatShouldFocus == focusedElem || (!elemThatShouldFocus && OKtoFocusOtherElement))
+    if ((!shouldFocusOtherElement && elemThatShouldFocus == focusedElem) || (shouldFocusOtherElement && elem != focusedElem))
         printToConsole("<"+elem.tagName+"> "+elem.id+" PASSED focus test");
     else {
         failedTestCount++;

Modified: trunk/LayoutTests/fast/events/tabindex-focus-blur-all-expected.txt (210266 => 210267)


--- trunk/LayoutTests/fast/events/tabindex-focus-blur-all-expected.txt	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/fast/events/tabindex-focus-blur-all-expected.txt	2017-01-04 03:39:39 UTC (rev 210267)
@@ -1,2 +1,2 @@
-333 focus / 333 blur events dispatched, and should be 333 / 333 PASSED
+336 focus / 336 blur events dispatched, and should be 336 / 336 PASSED
 Total of 0 focus test(s) failed. PASSED

Modified: trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations (210266 => 210267)


--- trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/LayoutTests/platform/ios-simulator-wk2/TestExpectations	2017-01-04 03:39:39 UTC (rev 210267)
@@ -1766,6 +1766,7 @@
 fast/shadow-dom/fullscreen-in-shadow-webkitCurrentFullScreenElement.html [ Skip ]
 fast/shadow-dom/fullscreen-in-slot-fullscreenElement.html [ Skip ]
 fast/shadow-dom/fullscreen-in-slot-webkitCurrentFullScreenElement.html [ Skip ]
+fast/events/focus-label-legend-elements-with-tab.html [ Skip ]
 
 # No touch events
 fast/visual-viewport/ios/fixed-element-on-bottom-with-keyboard.html [ Skip ]

Modified: trunk/Source/WebCore/ChangeLog (210266 => 210267)


--- trunk/Source/WebCore/ChangeLog	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/Source/WebCore/ChangeLog	2017-01-04 03:39:39 UTC (rev 210267)
@@ -1,3 +1,34 @@
+2017-01-03  Ryosuke Niwa  <[email protected]>
+
+        label element with tabindex >= 0 is not focusable
+        https://bugs.webkit.org/show_bug.cgi?id=102780
+        <rdar://problem/29796608>
+
+        Reviewed by Darin Adler.
+
+        Fixed the bug by removing the override for HTMLLabelElement::isFocusable which always returned false.
+
+        This is a behavior from r5532 but it doesn't match the latest HTML specification or that of Chrome
+        and Firefox.
+
+        Also fixed an existing bug in HTMLLabelElement::focus and HTMLLegendElement::focus which focused
+        the associated form control when there is one even if the element itself is focusable. Without this fix,
+        traversing from control with shift+tab would break since focusing the label would move the focus back
+        to the input element inside the label element.
+
+        Finally, fixed a bug in HTMLLegendElement::focus that we can call inFocus without updating layout first.
+
+        The fix was inspired by https://chromium.googlesource.com/chromium/src/+/085ad8697b1be50c4f93e296797a25a43a79bcfb
+
+        Test: fast/events/focus-label-legend-elements-with-tabindex.html
+
+        * html/HTMLLabelElement.cpp:
+        (WebCore::HTMLLabelElement::focus):
+        (WebCore::HTMLLabelElement::isFocusable): Deleted.
+        * html/HTMLLabelElement.h:
+        * html/HTMLLegendElement.cpp:
+        (WebCore::HTMLLegendElement::focus):
+
 2017-01-03  Tim Horton  <[email protected]>
 
         NSSpellChecker's recordResponse isn't called for unseen automatic corrections

Modified: trunk/Source/WebCore/html/HTMLLabelElement.cpp (210266 => 210267)


--- trunk/Source/WebCore/html/HTMLLabelElement.cpp	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/Source/WebCore/html/HTMLLabelElement.cpp	2017-01-04 03:39:39 UTC (rev 210267)
@@ -58,11 +58,6 @@
     return adoptRef(*new HTMLLabelElement(tagName, document));
 }
 
-bool HTMLLabelElement::isFocusable() const
-{
-    return false;
-}
-
 LabelableElement* HTMLLabelElement::control()
 {
     const AtomicString& controlId = attributeWithoutSynchronization(forAttr);
@@ -150,9 +145,18 @@
     return (element && element->willRespondToMouseClickEvents()) || HTMLElement::willRespondToMouseClickEvents();
 }
 
-void HTMLLabelElement::focus(bool, FocusDirection direction)
+void HTMLLabelElement::focus(bool restorePreviousSelection, FocusDirection direction)
 {
-    // to match other browsers, always restore previous selection
+    if (document().haveStylesheetsLoaded()) {
+        document().updateLayout();
+        if (isFocusable()) {
+            // The value of restorePreviousSelection is not used for label elements as it doesn't override updateFocusAppearance.
+            Element::focus(restorePreviousSelection, direction);
+            return;
+        }
+    }
+
+    // To match other browsers, always restore previous selection.
     if (auto* element = control())
         element->focus(true, direction);
 }

Modified: trunk/Source/WebCore/html/HTMLLabelElement.h (210266 => 210267)


--- trunk/Source/WebCore/html/HTMLLabelElement.h	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/Source/WebCore/html/HTMLLabelElement.h	2017-01-04 03:39:39 UTC (rev 210267)
@@ -39,8 +39,6 @@
 private:
     HTMLLabelElement(const QualifiedName&, Document&);
 
-    bool isFocusable() const final;
-
     void accessKeyAction(bool sendMouseEvents) final;
 
     // Overridden to update the hover/active state of the corresponding control.

Modified: trunk/Source/WebCore/html/HTMLLegendElement.cpp (210266 => 210267)


--- trunk/Source/WebCore/html/HTMLLegendElement.cpp	2017-01-04 03:13:22 UTC (rev 210266)
+++ trunk/Source/WebCore/html/HTMLLegendElement.cpp	2017-01-04 03:39:39 UTC (rev 210267)
@@ -57,13 +57,17 @@
     return descendantsOfType<HTMLFormControlElement>(*enclosingFieldset).first();
 }
 
-void HTMLLegendElement::focus(bool, FocusDirection direction)
+void HTMLLegendElement::focus(bool restorePreviousSelection, FocusDirection direction)
 {
-    if (isFocusable())
-        Element::focus(true, direction);
-        
     // To match other browsers' behavior, never restore previous selection.
-    if (HTMLFormControlElement* control = associatedControl())
+    if (document().haveStylesheetsLoaded()) {
+        document().updateLayoutIgnorePendingStylesheets();
+        if (isFocusable()) {
+            Element::focus(restorePreviousSelection, direction);
+            return;
+        }
+    }
+    if (auto* control = associatedControl())
         control->focus(false, direction);
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to