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
- trunk/LayoutTests/ChangeLog
- trunk/LayoutTests/platform/ios-simulator/TestExpectations
- trunk/Source/WebCore/ChangeLog
- trunk/Source/WebCore/dom/Element.cpp
- trunk/Source/WebCore/dom/Element.h
- trunk/Source/WebCore/page/FocusController.cpp
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
