Title: [197439] trunk
Revision
197439
Author
[email protected]
Date
2016-03-01 21:31:28 -0800 (Tue, 01 Mar 2016)

Log Message

Contents inside a shadow host with a negative tabindex should not be tab focusable
https://bugs.webkit.org/show_bug.cgi?id=154769

Reviewed by Antti Koivisto.

Source/WebCore:

Contents inside a shadow host with a negative tabindex content attribute should not be included in
the sequential focus navigation order as discussed on https://github.com/w3c/webcomponents/issues/399.

Test: fast/shadow-dom/negative-tabindex-on-shadow-host.html

* dom/Element.cpp:
(WebCore::Element::tabIndexSetExplicitly): Added.
* dom/Element.h:
* page/FocusController.cpp:
(WebCore::shadowAdjustedTabIndex): Renamed from adjustedTabIndex. Return 0 when tabindex content attribute
is not explicitly set since element.tabIndex() would return -1 for HTML elements in such case.
(WebCore::isFocusableOrHasShadowTreeWithoutCustomFocusLogic): Renamed from shouldVisit.
(WebCore::FocusController::findElementWithExactTabIndex):
(WebCore::nextElementWithGreaterTabIndex):
(WebCore::previousElementWithLowerTabIndex):
(WebCore::FocusController::nextFocusableElement):
(WebCore::FocusController::previousFocusableElement):

LayoutTests:

Added a test for navigating across shadow boundaries.

* fast/shadow-dom/negative-tabindex-on-shadow-host-expected.txt: Added.
* fast/shadow-dom/negative-tabindex-on-shadow-host.html: Added.
* platform/ios-simulator/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (197438 => 197439)


--- trunk/LayoutTests/ChangeLog	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/LayoutTests/ChangeLog	2016-03-02 05:31:28 UTC (rev 197439)
@@ -1,3 +1,16 @@
+2016-02-26  Ryosuke Niwa  <[email protected]>
+
+        Contents inside a shadow host with a negative tabindex should not be tab focusable
+        https://bugs.webkit.org/show_bug.cgi?id=154769
+
+        Reviewed by Antti Koivisto.
+
+        Added a test for navigating across shadow boundaries.
+
+        * fast/shadow-dom/negative-tabindex-on-shadow-host-expected.txt: Added.
+        * fast/shadow-dom/negative-tabindex-on-shadow-host.html: Added.
+        * platform/ios-simulator/TestExpectations:
+
 2016-03-01  Myles C. Maxfield  <[email protected]>
 
         Small-caps non-BMP characters are garbled in the complex text codepath

Added: trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host-expected.txt (0 => 197439)


--- trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host-expected.txt	2016-03-02 05:31:28 UTC (rev 197439)
@@ -0,0 +1,11 @@
+Tests for setting a negative tabindex on shadow host. Elements inside such a shadow tree should not be in the sequential navigation order.
+To manually test, press tab key four times. It should traverse focusable elements in the increasing numerical order.
+
+1. First sequentially focusable element outside shadow trees
+2. / 3.2. Shadow host with a positive tabindex
+3.1. Focusable element inside a shadow host with a positive tabindex
+2. / 3.2. Shadow host with a positive tabindex
+4.1. Focusable element inside a shadow host with no tabindex
+4.2. Shadow host with no tabindex
+5. Last sequentially focusable element outside shadow trees
+

Added: trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host.html (0 => 197439)


--- trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/negative-tabindex-on-shadow-host.html	2016-03-02 05:31:28 UTC (rev 197439)
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>Tests for setting a negative tabindex on shadow host. Elements inside such a shadow tree should not be in the sequential navigation order.<br>
+To manually test, press tab key four times. It should traverse focusable elements in the increasing numerical order.</p>
+<div id="test-content">
+<div id="first" tabindex="1" _onfocus_="log(this)">1. First sequentially focusable element outside shadow trees</div>
+<div id="host-with-negative-tabindex" tabindex="-1" _onfocus_="log(this)">Shadow host with a negative tabindex</div>
+<div id="host-with-no-tabindex" _onfocus_="log(this)">4.2. Shadow host with no tabindex</div>
+<div id="host-with-positive-tabindex" tabindex="2" _onfocus_="log(this)">2. / 3.2. Shadow host with a positive tabindex</div>
+<div tabindex="0" _onfocus_="log(this)">5. Last sequentially focusable element outside shadow trees</div>
+</div>
+<pre></pre>
+<script>
+
+document.getElementById('host-with-negative-tabindex').attachShadow({mode: 'closed'}).innerHTML = `
+    <div tabindex="0" _onfocus_="log(this)">Should not be focused as it is inside a shadow host with a negative tabindex</div>
+`;
+
+document.getElementById('host-with-no-tabindex').attachShadow({mode: 'closed'}).innerHTML = `
+    <div tabindex="0" _onfocus_="log(this)">4.1. Focusable element inside a shadow host with no tabindex</div>
+`;
+
+document.getElementById('host-with-positive-tabindex').attachShadow({mode: 'closed'}).innerHTML = `
+    <slot></slot>
+    <div tabindex="0" _onfocus_="log(this)">3.1. Focusable element inside a shadow host with a positive tabindex</div>
+`;
+
+function log(element) {
+    document.querySelector('pre').textContent += element.textContent + '\n';
+}
+
+if (window.testRunner)
+    testRunner.dumpAsText();
+
+document.getElementById('first').focus();
+
+if (window.eventSender) {
+    eventSender.keyDown('\t');
+    eventSender.keyDown('\t');
+    eventSender.keyDown('\t');
+    eventSender.keyDown('\t');
+    document.getElementById('test-content').style.display = 'none';
+}
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (197438 => 197439)


--- trunk/LayoutTests/platform/ios-simulator/TestExpectations	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations	2016-03-02 05:31:28 UTC (rev 197439)
@@ -235,6 +235,9 @@
 webkit.org/b/149441 fast/shadow-dom/css-scoping-shadow-slotted-rule.html [ ImageOnlyFailure ]
 webkit.org/b/149441 fast/shadow-dom/css-scoping-shadow-slot-display-override.html [ ImageOnlyFailure ]
 
+# No tab navigation support on iOS
+fast/shadow-dom/negative-tabindex-on-shadow-host.html [ Failure ]
+
 webkit.org/b/150225 fast/custom-elements [ Pass ]
 
 # This test needs to be rewritten to use runUIScript to work on iOS

Modified: trunk/Source/WebCore/ChangeLog (197438 => 197439)


--- trunk/Source/WebCore/ChangeLog	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/Source/WebCore/ChangeLog	2016-03-02 05:31:28 UTC (rev 197439)
@@ -1,3 +1,28 @@
+2016-02-26  Ryosuke Niwa  <[email protected]>
+
+        Contents inside a shadow host with a negative tabindex should not be tab focusable
+        https://bugs.webkit.org/show_bug.cgi?id=154769
+
+        Reviewed by Antti Koivisto.
+
+        Contents inside a shadow host with a negative tabindex content attribute should not be included in
+        the sequential focus navigation order as discussed on https://github.com/w3c/webcomponents/issues/399.
+
+        Test: fast/shadow-dom/negative-tabindex-on-shadow-host.html
+
+        * dom/Element.cpp:
+        (WebCore::Element::tabIndexSetExplicitly): Added.
+        * dom/Element.h:
+        * page/FocusController.cpp:
+        (WebCore::shadowAdjustedTabIndex): Renamed from adjustedTabIndex. Return 0 when tabindex content attribute
+        is not explicitly set since element.tabIndex() would return -1 for HTML elements in such case.
+        (WebCore::isFocusableOrHasShadowTreeWithoutCustomFocusLogic): Renamed from shouldVisit.
+        (WebCore::FocusController::findElementWithExactTabIndex):
+        (WebCore::nextElementWithGreaterTabIndex):
+        (WebCore::previousElementWithLowerTabIndex):
+        (WebCore::FocusController::nextFocusableElement):
+        (WebCore::FocusController::previousFocusableElement):
+
 2016-03-01  Michael Saboff  <[email protected]>
 
         REGRESSION (r197426): Missed adding unicode parameter to call to Yarr::parse() in URLFilterParser::addPattern()

Modified: trunk/Source/WebCore/dom/Element.cpp (197438 => 197439)


--- trunk/Source/WebCore/dom/Element.cpp	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/Source/WebCore/dom/Element.cpp	2016-03-02 05:31:28 UTC (rev 197439)
@@ -212,11 +212,16 @@
     ensureElementRareData().setTabIndexExplicitly(tabIndex);
 }
 
-bool Element::supportsFocus() const
+bool Element::tabIndexSetExplicitly() const
 {
     return hasRareData() && elementRareData()->tabIndexSetExplicitly();
 }
 
+bool Element::supportsFocus() const
+{
+    return tabIndexSetExplicitly();
+}
+
 Element* Element::focusDelegate()
 {
     return this;

Modified: trunk/Source/WebCore/dom/Element.h (197438 => 197439)


--- trunk/Source/WebCore/dom/Element.h	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/Source/WebCore/dom/Element.h	2016-03-02 05:31:28 UTC (rev 197439)
@@ -266,6 +266,7 @@
     virtual void setHovered(bool flag = true);
     virtual void setFocus(bool flag);
 
+    bool tabIndexSetExplicitly() const;
     virtual bool supportsFocus() const;
     virtual bool isFocusable() const;
     virtual bool isKeyboardFocusable(KeyboardEvent*) const;

Modified: trunk/Source/WebCore/page/FocusController.cpp (197438 => 197439)


--- trunk/Source/WebCore/page/FocusController.cpp	2016-03-02 04:30:45 UTC (rev 197438)
+++ trunk/Source/WebCore/page/FocusController.cpp	2016-03-02 05:31:28 UTC (rev 197439)
@@ -214,14 +214,16 @@
     return is<Element>(node) && downcast<Element>(node).isKeyboardFocusable(&event) && downcast<Element>(node).shadowRoot() && !hasCustomFocusLogic(downcast<Element>(node));
 }
 
-static inline int adjustedTabIndex(Node& node, KeyboardEvent& event)
+static inline int shadowAdjustedTabIndex(Element& element, KeyboardEvent& event)
 {
-    if (!is<Element>(node))
-        return 0;
-    return isNonFocusableShadowHost(downcast<Element>(node), event) ? 0 : downcast<Element>(node).tabIndex();
+    if (isNonFocusableShadowHost(element, event)) {
+        if (!element.tabIndexSetExplicitly())
+            return 0; // Treat a shadow host without tabindex if it has tabindex=0 even though HTMLElement::tabIndex returns -1 on such an element.
+    }
+    return element.tabIndex();
 }
 
-static inline bool shouldVisit(Element& element, KeyboardEvent& event)
+static inline bool isFocusableOrHasShadowTreeWithoutCustomFocusLogic(Element& element, KeyboardEvent& event)
 {
     return element.isKeyboardFocusable(&event) || isNonFocusableShadowHost(element, event);
 }
@@ -479,7 +481,7 @@
         if (!is<Element>(*node))
             continue;
         Element& element = downcast<Element>(*node);
-        if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) == tabIndex)
+        if (isFocusableOrHasShadowTreeWithoutCustomFocusLogic(element, *event) && shadowAdjustedTabIndex(element, *event) == tabIndex)
             return &element;
     }
     return nullptr;
@@ -494,7 +496,7 @@
         if (!is<Element>(*node))
             continue;
         Element& element = downcast<Element>(*node);
-        if (shouldVisit(element, event) && element.tabIndex() > tabIndex && element.tabIndex() < winningTabIndex) {
+        if (isFocusableOrHasShadowTreeWithoutCustomFocusLogic(element, event) && element.tabIndex() > tabIndex && element.tabIndex() < winningTabIndex) {
             winner = &element;
             winningTabIndex = element.tabIndex();
         }
@@ -512,8 +514,8 @@
         if (!is<Element>(*node))
             continue;
         Element& element = downcast<Element>(*node);
-        int currentTabIndex = adjustedTabIndex(element, event);
-        if ((shouldVisit(element, event) || isNonFocusableShadowHost(element, event)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
+        int currentTabIndex = shadowAdjustedTabIndex(element, event);
+        if ((isFocusableOrHasShadowTreeWithoutCustomFocusLogic(element, event) || isNonFocusableShadowHost(element, event)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
             winner = &element;
             winningTabIndex = currentTabIndex;
         }
@@ -535,32 +537,34 @@
 
 Element* FocusController::nextFocusableElement(const FocusNavigationScope& scope, Node* start, KeyboardEvent* event)
 {
+    int startTabIndex = 0;
+    if (start && is<Element>(*start))
+        startTabIndex = shadowAdjustedTabIndex(downcast<Element>(*start), *event);
+
     if (start) {
-        int tabIndex = adjustedTabIndex(*start, *event);
         // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
-        if (tabIndex < 0) {
+        if (startTabIndex < 0) {
             for (Node* node = scope.nextInScope(start); node; node = scope.nextInScope(node)) {
                 if (!is<Element>(*node))
                     continue;
                 Element& element = downcast<Element>(*node);
-                if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0)
+                if (isFocusableOrHasShadowTreeWithoutCustomFocusLogic(element, *event) && shadowAdjustedTabIndex(element, *event) >= 0)
                     return &element;
             }
         }
 
         // First try to find a node with the same tabindex as start that comes after start in the scope.
-        if (Element* winner = findElementWithExactTabIndex(scope, scope.nextInScope(start), tabIndex, event, FocusDirectionForward))
+        if (Element* winner = findElementWithExactTabIndex(scope, scope.nextInScope(start), startTabIndex, event, FocusDirectionForward))
             return winner;
 
-        if (!tabIndex)
-            // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
-            return 0;
+        if (!startTabIndex)
+            return nullptr; // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
     }
 
     // Look for the first Element in the scope that:
     // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
     // 2) comes first in the scope, if there's a tie.
-    if (Element* winner = nextElementWithGreaterTabIndex(scope, start ? adjustedTabIndex(*start, *event) : 0, *event))
+    if (Element* winner = nextElementWithGreaterTabIndex(scope, startTabIndex, *event))
         return winner;
 
     // There are no nodes with a tabindex greater than start's tabindex,
@@ -578,14 +582,13 @@
     // First try to find the last node in the scope that comes before start and has the same tabindex as start.
     // If start is null, find the last node in the scope with a tabindex of 0.
     Node* startingNode;
-    int startingTabIndex;
+    int startingTabIndex = 0;
     if (start) {
         startingNode = scope.previousInScope(start);
-        startingTabIndex = adjustedTabIndex(*start, *event);
-    } else {
+        if (is<Element>(*start))
+            startingTabIndex = shadowAdjustedTabIndex(downcast<Element>(*start), *event);
+    } else
         startingNode = last;
-        startingTabIndex = 0;
-    }
 
     // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order
     if (startingTabIndex < 0) {
@@ -593,7 +596,7 @@
             if (!is<Element>(*node))
                 continue;
             Element& element = downcast<Element>(*node);
-            if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0)
+            if (isFocusableOrHasShadowTreeWithoutCustomFocusLogic(element, *event) && shadowAdjustedTabIndex(element, *event) >= 0)
                 return &element;
         }
     }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to