Diff
Modified: trunk/LayoutTests/ChangeLog (227143 => 227144)
--- trunk/LayoutTests/ChangeLog 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/LayoutTests/ChangeLog 2018-01-18 17:26:23 UTC (rev 227144)
@@ -1,3 +1,14 @@
+2018-01-18 Chris Fleizach <[email protected]>
+
+ AX: Aria-activedescendant not supported
+ https://bugs.webkit.org/show_bug.cgi?id=161734
+ <rdar://problem/28202679>
+
+ Reviewed by Joanmarie Diggs.
+
+ * accessibility/aria-combobox-control-owns-elements-expected.txt: Added.
+ * accessibility/aria-combobox-control-owns-elements.html: Added.
+
2018-01-18 Per Arne Vollan <[email protected]>
Mark fast/forms/auto-fill-button/input-strong-password-auto-fill-button.html as failing on Windows.
Added: trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements-expected.txt (0 => 227144)
--- trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements-expected.txt (rev 0)
+++ trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements-expected.txt 2018-01-18 17:26:23 UTC (rev 227144)
@@ -0,0 +1,36 @@
+
+item1
+item2
+
+item1
+item2
+
+cell1
+
+treeitem1
+treeitem2
+This tests variations of the comboboxes and elements it can control and own. Then verifies the active-descendant is reflected correctly.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS list.selectedChildrenCount is 0
+PASS list.selectedChildrenCount is 1
+PASS list.selectedChildAtIndex(0).isEqual(listitem1) is true
+PASS listbox.selectedChildrenCount is 0
+PASS listbox.selectedChildrenCount is 1
+PASS listbox.selectedChildAtIndex(0).isEqual(option2_1) is true
+PASS grid.selectedChildrenCount is 0
+PASS grid.selectedChildrenCount is 1
+PASS grid.selectedChildAtIndex(0).isEqual(row3_1) is true
+PASS tree.selectedChildrenCount is 0
+PASS tree.selectedChildrenCount is 1
+PASS tree.selectedChildAtIndex(0).isEqual(treeitem4_1) is true
+Received AXSelectedChildrenChanged for List1
+Received AXSelectedChildrenChanged for Listbox2
+Received AXSelectedRowsChanged for Grid3
+Received AXSelectedRowsChanged for Tree4
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements.html (0 => 227144)
--- trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements.html (rev 0)
+++ trunk/LayoutTests/accessibility/aria-combobox-control-owns-elements.html 2018-01-18 17:26:23 UTC (rev 227144)
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body id="body">
+
+<!-- Example 1: controls a list -->
+<input type="text" role="combobox" id="combobox1" aria-controls="list1" aria-label="Combobox1">
+<div role="list" id="list1" aria-label="List1">
+<div role="listitem" id="item1_1">item1</div>
+<div role="listitem" id="item1_2">item2</div>
+</div>
+
+<!-- Example 2: owns a listbox -->
+<input type="text" role="combobox" id="combobox2" aria-owns="listbox2" aria-label="Combobox2">
+<div role="listbox" id="listbox2" aria-label="Listbox2">
+<div role="option" id="option2_1">item1</div>
+<div role="option" id="option2_2">item2</div>
+</div>
+
+<!-- Example 3: owns a grid -->
+<input type="text" role="combobox" id="combobox3" aria-owns="grid3" aria-label="Combobox3">
+<div role="grid" id="grid3" aria-label="Grid3">
+<div role="row" id="row3_1">
+<div role="gridcell" id="gridcell3_1">cell1</div>
+</div>
+</div>
+
+<!-- Example 3: owns a tree -->
+<input type="text" role="combobox" id="combobox4" aria-owns="tree4" aria-label="Combobox4">
+<div role="tree" id="tree4" aria-label="Tree4">
+<div role="treeitem" id="treeitem4_1">treeitem1</div>
+<div role="treeitem" id="treeitem4_2">treeitem2</div>
+</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+ description("This tests variations of the comboboxes and elements it can control and own. Then verifies the active-descendant is reflected correctly.");
+
+ if (window.accessibilityController) {
+ window.jsTestIsAsync = true;
+ var selectedChildrenChangeCount = 0;
+
+ window.accessibilityController.addNotificationListener(function (target, notification) {
+ if (notification == "AXSelectedChildrenChanged" || notification == "AXSelectedRowsChanged") {
+ selectedChildrenChangeCount++;
+ var targetString = platformValueForW3CName(target);
+ debug("Received " + notification + " for " + targetString);
+ if (selectedChildrenChangeCount == 4) {
+ accessibilityController.removeNotificationListener();
+ finishJSTest();
+ }
+ }
+ });
+
+ // Example 1: aria-controls a list.
+ document.getElementById("combobox1").focus();
+ var list = accessibilityController.accessibleElementById("list1");
+ shouldBe("list.selectedChildrenCount", "0");
+ // Set active-descendant, verify notification is sent and that list has correct selected children.
+ document.getElementById("combobox1").setAttribute("aria-activedescendant", "item1_1");
+ var listitem1 = accessibilityController.accessibleElementById("item1_1");
+ shouldBe("list.selectedChildrenCount", "1");
+ shouldBeTrue("list.selectedChildAtIndex(0).isEqual(listitem1)");
+
+ // Example 2: aria-owns a listbox.
+ document.getElementById("combobox2").focus();
+ var listbox = accessibilityController.accessibleElementById("listbox2");
+ shouldBe("listbox.selectedChildrenCount", "0");
+ // Set active-descendant, verify notification is sent and that list has correct selected children.
+ document.getElementById("combobox2").setAttribute("aria-activedescendant", "option2_1");
+ var option2_1 = accessibilityController.accessibleElementById("option2_1");
+ shouldBe("listbox.selectedChildrenCount", "1");
+ shouldBeTrue("listbox.selectedChildAtIndex(0).isEqual(option2_1)");
+
+ // Example 3: aria-owns a grid.
+ document.getElementById("combobox3").focus();
+ var grid = accessibilityController.accessibleElementById("grid3");
+ shouldBe("grid.selectedChildrenCount", "0");
+ // Set active-descendant, verify notification is sent and that list has correct selected children.
+ document.getElementById("combobox3").setAttribute("aria-activedescendant", "row3_1");
+ var row3_1 = accessibilityController.accessibleElementById("row3_1");
+ shouldBe("grid.selectedChildrenCount", "1");
+ shouldBeTrue("grid.selectedChildAtIndex(0).isEqual(row3_1)");
+
+ // Example 4: aria-owns a tree.
+ document.getElementById("combobox4").focus();
+ var tree = accessibilityController.accessibleElementById("tree4");
+ shouldBe("tree.selectedChildrenCount", "0");
+ // Set active-descendant, verify notification is sent and that list has correct selected children.
+ document.getElementById("combobox4").setAttribute("aria-activedescendant", "treeitem4_1");
+ var treeitem4_1 = accessibilityController.accessibleElementById("treeitem4_1");
+ shouldBe("tree.selectedChildrenCount", "1");
+ shouldBeTrue("tree.selectedChildAtIndex(0).isEqual(treeitem4_1)");
+ }
+
+</script>
+
+<script src=""
+</body>
+</html>
+
Modified: trunk/LayoutTests/accessibility/lists-expected.txt (227143 => 227144)
--- trunk/LayoutTests/accessibility/lists-expected.txt 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/LayoutTests/accessibility/lists-expected.txt 2018-01-18 17:26:23 UTC (rev 227144)
@@ -38,7 +38,7 @@
AXFocusableAncestor: <AXList>
AXEditableAncestor: (null)
AXHighestEditableAncestor: (null)
-AXSelectedChildren: (null)
+AXSelectedChildren: <array of size 0>
AXVisibleChildren: <array of size 2>
AXOrientation: AXVerticalOrientation
AXTitleUIElement: (null)
@@ -72,7 +72,7 @@
AXFocusableAncestor: <AXList>
AXEditableAncestor: (null)
AXHighestEditableAncestor: (null)
-AXSelectedChildren: (null)
+AXSelectedChildren: <array of size 0>
AXVisibleChildren: <array of size 2>
AXOrientation: AXVerticalOrientation
AXTitleUIElement: (null)
Modified: trunk/Source/WebCore/ChangeLog (227143 => 227144)
--- trunk/Source/WebCore/ChangeLog 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/ChangeLog 2018-01-18 17:26:23 UTC (rev 227144)
@@ -1,3 +1,29 @@
+2018-01-18 Chris Fleizach <[email protected]>
+
+ AX: Aria-activedescendant not supported
+ https://bugs.webkit.org/show_bug.cgi?id=161734
+ <rdar://problem/28202679>
+
+ Reviewed by Joanmarie Diggs.
+
+ When a combo-box owns/controls a list/listbox/grid/tree, the owned element needs to check the active-descendant of the combobox when
+ checking if it has selected children.
+ The target of the selection change notification should also be the owned element in these cases.
+
+ Test: accessibility/aria-combobox-controlling-list.html
+
+ * accessibility/AccessibilityObject.cpp:
+ (WebCore::AccessibilityObject::selectedListItem):
+ * accessibility/AccessibilityObject.h:
+ * accessibility/AccessibilityRenderObject.cpp:
+ (WebCore::AccessibilityRenderObject::targetElementForActiveDescendant const):
+ (WebCore::AccessibilityRenderObject::handleActiveDescendantChanged):
+ (WebCore::AccessibilityRenderObject::canHaveSelectedChildren const):
+ (WebCore::AccessibilityRenderObject::selectedChildren):
+ * accessibility/AccessibilityRenderObject.h:
+ * accessibility/mac/AXObjectCacheMac.mm:
+ (WebCore::AXObjectCache::postPlatformNotification):
+
2018-01-17 Per Arne Vollan <[email protected]>
REGRESSION (r224780): Text stroke not applied to video captions.
Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.cpp (227143 => 227144)
--- trunk/Source/WebCore/accessibility/AccessibilityObject.cpp 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.cpp 2018-01-18 17:26:23 UTC (rev 227144)
@@ -3410,6 +3410,16 @@
Element* element = node->shadowHost();
return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(*element).isPasswordField();
}
+
+AccessibilityObject* AccessibilityObject::selectedListItem()
+{
+ for (const auto& child : children()) {
+ if (child->isListItem() && (child->isSelected() || child->isActiveDescendantOfFocusedContainer()))
+ return child.get();
+ }
+
+ return nullptr;
+}
void AccessibilityObject::ariaElementsFromAttribute(AccessibilityChildrenVector& children, const QualifiedName& attributeName) const
{
Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (227143 => 227144)
--- trunk/Source/WebCore/accessibility/AccessibilityObject.h 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h 2018-01-18 17:26:23 UTC (rev 227144)
@@ -660,6 +660,7 @@
virtual float stepValueForRange() const { return 0.0f; }
virtual AccessibilityObject* selectedRadioButton() { return nullptr; }
virtual AccessibilityObject* selectedTabItem() { return nullptr; }
+ AccessibilityObject* selectedListItem();
virtual int layoutCount() const { return 0; }
virtual double estimatedLoadingProgress() const { return 0; }
static bool isARIAControl(AccessibilityRole);
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (227143 => 227144)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2018-01-18 17:26:23 UTC (rev 227144)
@@ -2495,7 +2495,19 @@
else
cache->postNotification(this, document(), AXObjectCache::AXExpandedChanged);
}
+
+RenderObject* AccessibilityRenderObject::targetElementForActiveDescendant(const QualifiedName& attributeName, AccessibilityObject* activeDescendant) const
+{
+ AccessibilityObject::AccessibilityChildrenVector elements;
+ ariaElementsFromAttribute(elements, attributeName);
+ for (auto element : elements) {
+ if (activeDescendant->isDescendantOfObject(element.get()))
+ return element->renderer();
+ }
+ return nullptr;
+}
+
void AccessibilityRenderObject::handleActiveDescendantChanged()
{
Element* element = downcast<Element>(renderer()->node());
@@ -2504,8 +2516,22 @@
if (!renderer()->frame().selection().isFocusedAndActive() || renderer()->document().focusedElement() != element)
return;
- if (activeDescendant() && shouldNotifyActiveDescendant())
- renderer()->document().axObjectCache()->postNotification(renderer(), AXObjectCache::AXActiveDescendantChanged);
+ auto* activeDescendant = this->activeDescendant();
+ if (activeDescendant && shouldNotifyActiveDescendant()) {
+ auto* targetRenderer = renderer();
+
+#if PLATFORM(COCOA)
+ // If the combobox's activeDescendant is inside another object, the target element should be that parent.
+ if (isComboBox()) {
+ if (auto* ariaOwner = targetElementForActiveDescendant(aria_ownsAttr, activeDescendant))
+ targetRenderer = ariaOwner;
+ else if (auto* ariaController = targetElementForActiveDescendant(aria_controlsAttr, activeDescendant))
+ targetRenderer = ariaController;
+ }
+#endif
+
+ renderer()->document().axObjectCache()->postNotification(targetRenderer, AXObjectCache::AXActiveDescendantChanged);
+ }
}
AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
@@ -3299,6 +3325,7 @@
case AccessibilityRole::TabList:
case AccessibilityRole::Tree:
case AccessibilityRole::TreeGrid:
+ case AccessibilityRole::List:
// These roles are containers whose children are treated as selected by assistive
// technologies. We can get the "selected" item via aria-activedescendant or the
// focused element.
@@ -3326,7 +3353,7 @@
// Get all the rows.
auto rowsIteration = [&](auto& rows) {
for (auto& row : rows) {
- if (row->isSelected()) {
+ if (row->isSelected() || row->isActiveDescendantOfFocusedContainer()) {
result.append(row);
if (!isMulti)
break;
@@ -3350,7 +3377,7 @@
for (const auto& child : children()) {
// Every child should have aria-role option, and if so, check for selected attribute/state.
- if (child->isSelected() && child->ariaRoleAttribute() == AccessibilityRole::ListBoxOption) {
+ if (child->ariaRoleAttribute() == AccessibilityRole::ListBoxOption && (child->isSelected() || child->isActiveDescendantOfFocusedContainer())) {
result.append(child);
if (!isMulti)
return;
@@ -3379,6 +3406,10 @@
if (AccessibilityObject* selectedTab = selectedTabItem())
result.append(selectedTab);
return;
+ case AccessibilityRole::List:
+ if (auto* selectedListItemChild = selectedListItem())
+ result.append(selectedListItemChild);
+ return;
case AccessibilityRole::Menu:
case AccessibilityRole::MenuBar:
if (AccessibilityObject* descendant = activeDescendant()) {
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h (227143 => 227144)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2018-01-18 17:26:23 UTC (rev 227144)
@@ -285,6 +285,7 @@
bool shouldGetTextFromNode(AccessibilityTextUnderElementMode) const;
+ RenderObject* targetElementForActiveDescendant(const QualifiedName&, AccessibilityObject*) const;
bool canHavePlainText() const;
};
Modified: trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm (227143 => 227144)
--- trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm 2018-01-18 16:52:35 UTC (rev 227143)
+++ trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm 2018-01-18 17:26:23 UTC (rev 227144)
@@ -271,13 +271,13 @@
switch (notification) {
case AXActiveDescendantChanged:
// An active descendant change for trees means a selected rows change.
- if (obj->isTree())
+ if (obj->isTree() || obj->isTable())
macNotification = NSAccessibilitySelectedRowsChangedNotification;
// When a combobox uses active descendant, it means the selected item in its associated
// list has changed. In these cases we should use selected children changed, because
// we don't want the focus to change away from the combobox where the user is typing.
- else if (obj->isComboBox())
+ else if (obj->isComboBox() || obj->isList() || obj->isListBox())
macNotification = NSAccessibilitySelectedChildrenChangedNotification;
else
macNotification = NSAccessibilityFocusedUIElementChangedNotification;