WAI-ARIA: aria-activedescendant doesn't work as intended
authorcfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2009 07:50:48 +0000 (07:50 +0000)
committercfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2009 07:50:48 +0000 (07:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=32100

Reviewed by Beth Dakin.

WebCore:

Fixes a number of issues regarding the "tree" role and aria-activedescendant.
1. The indexes were being reported incorrectly by treeitems.
2. aria-activedescendant changes were not being sent to the containing item.
3. The tree's selected rows need to consult aria-activedescendant.
4. Since a tree changes what it returns as its children (it returns its rows)
   the mac-specific array indexing methods need to correctly handle the tree case.

Tests: platform/mac/accessibility/aria-tree-activedescendant.html
       platform/mac/accessibility/aria-tree-index-of-items.html

* accessibility/AXObjectCache.h:
(WebCore::AXObjectCache::):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::shouldFocusActiveDescendant):
(WebCore::AccessibilityRenderObject::activeDescendant):
(WebCore::AccessibilityRenderObject::handleActiveDescendantChanged):
(WebCore::AccessibilityRenderObject::ariaTreeSelectedRows):
* accessibility/mac/AXObjectCacheMac.mm:
(WebCore::AXObjectCache::postPlatformNotification):
* accessibility/mac/AccessibilityObjectWrapper.mm:
(-[AccessibilityObjectWrapper accessibilityAttributeValue:]):
(-[AccessibilityObjectWrapper accessibilityIndexOfChild:]):
(-[AccessibilityObjectWrapper accessibilityArrayAttributeValues:index:maxCount:]):
* accessibility/win/AXObjectCacheWin.cpp:
(WebCore::AXObjectCache::postPlatformNotification):

LayoutTests:

* platform/mac/accessibility/aria-tree-activedescendant-expected.txt: Added.
* platform/mac/accessibility/aria-tree-activedescendant.html: Added.
* platform/mac/accessibility/aria-tree-index-of-items-expected.txt: Added.
* platform/mac/accessibility/aria-tree-index-of-items.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@51684 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/platform/mac/accessibility/aria-tree-activedescendant-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/aria-tree-activedescendant.html [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/aria-tree-index-of-items-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/aria-tree-index-of-items.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/accessibility/AXObjectCache.h
WebCore/accessibility/AccessibilityRenderObject.cpp
WebCore/accessibility/mac/AXObjectCacheMac.mm
WebCore/accessibility/mac/AccessibilityObjectWrapper.mm
WebCore/accessibility/win/AXObjectCacheWin.cpp

index d7feb144363adc8b02cca9721a6ec6ae6e394590..baf76612a31e09e35983c70ac66dab92ab779bcf 100644 (file)
@@ -1,3 +1,15 @@
+2009-12-03  Chris Fleizach  <cfleizach@apple.com>
+
+        Reviewed by Beth Dakin.
+
+        WAI-ARIA: aria-activedescendant doesn't work as intended
+        https://bugs.webkit.org/show_bug.cgi?id=32100
+
+        * platform/mac/accessibility/aria-tree-activedescendant-expected.txt: Added.
+        * platform/mac/accessibility/aria-tree-activedescendant.html: Added.
+        * platform/mac/accessibility/aria-tree-index-of-items-expected.txt: Added.
+        * platform/mac/accessibility/aria-tree-index-of-items.html: Added.
+
 2009-12-03  Chris Fleizach  <cfleizach@apple.com>
 
         Fixing layout test bustage on platforms.
diff --git a/LayoutTests/platform/mac/accessibility/aria-tree-activedescendant-expected.txt b/LayoutTests/platform/mac/accessibility/aria-tree-activedescendant-expected.txt
new file mode 100644 (file)
index 0000000..9578b88
--- /dev/null
@@ -0,0 +1,16 @@
+Animals
+Birds
+Cats
+Siamese
+Tabby
+This tests that the ARIA drag and drop attributes work as intended.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS axtree.selectedRowAtIndex(0).isEqual(activeDescendant) is true
+PASS axtree.selectedRowAtIndex(0).isEqual(newActiveDescendant) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac/accessibility/aria-tree-activedescendant.html b/LayoutTests/platform/mac/accessibility/aria-tree-activedescendant.html
new file mode 100644 (file)
index 0000000..d972a1e
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+<script>
+var successfullyParsed = false;
+</script>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body id="body">
+
+<ul id="tree0" role="tree" aria-labelledby="treelabel" aria-activedescendant="tree0_item0" tabindex="0">
+<li id="tree0_item0" role="treeitem" aria-level="1" aria-expanded="true">
+<span><span class="expander"></span>Animals</span>
+<ul role="group">
+<li id="tree0_item0_0" role="treeitem" aria-level="2"><span>Birds</span></li>
+<li id="tree0_item0_1" role="treeitem" aria-level="2" aria-expanded="false">
+<span><span class="expander"></span>Cats</span>
+<ul role="group">
+<li id="tree0_item0_1_0" role="treeitem"aria-level="3"><span>Siamese</span></li>
+<li id="tree0_item0_1_1" role="treeitem" aria-level="3"><span>Tabby</span></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests that the ARIA drag and drop attributes work as intended.");
+
+    if (window.accessibilityController) {
+
+          var axtree = accessibilityController.rootElement.childAtIndex(0).childAtIndex(0);
+
+          var activeDescendant = axtree.childAtIndex(0);
+          shouldBe("axtree.selectedRowAtIndex(0).isEqual(activeDescendant)", "true");
+
+          var newActiveDescendant = axtree.childAtIndex(2);
+          document.getElementById("tree0").setAttribute("aria-activedescendant", "tree0_item0_1");
+
+          shouldBe("axtree.selectedRowAtIndex(0).isEqual(newActiveDescendant)", "true");
+    }
+
+    successfullyParsed = true;
+</script>
+
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/platform/mac/accessibility/aria-tree-index-of-items-expected.txt b/LayoutTests/platform/mac/accessibility/aria-tree-index-of-items-expected.txt
new file mode 100644 (file)
index 0000000..59203f2
--- /dev/null
@@ -0,0 +1,19 @@
+Animals
+Birds
+Cats
+Siamese
+Tabby
+This tests that the index attribute is correctly returned for all tree items, regardless of their hierarchical level.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS axtree.selectedRowAtIndex(0).indexInTable() is 0
+PASS axtree.selectedRowAtIndex(1).indexInTable() is 1
+PASS axtree.selectedRowAtIndex(2).indexInTable() is 2
+PASS axtree.selectedRowAtIndex(3).indexInTable() is 3
+PASS axtree.selectedRowAtIndex(4).indexInTable() is 4
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac/accessibility/aria-tree-index-of-items.html b/LayoutTests/platform/mac/accessibility/aria-tree-index-of-items.html
new file mode 100644 (file)
index 0000000..511c5d3
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+<script>
+var successfullyParsed = false;
+</script>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body id="body">
+
+<ul id="tree0" role="tree" aria-labelledby="treelabel" aria-multiselectable="true" tabindex="0">
+    <li id="tree0_item0" role="treeitem" aria-level="1" aria-selected="true" aria-expanded="true"><span>
+    <span class="expander"></span>Animals</span>
+    <ul role="group">
+        <li id="tree0_item0_0" aria-selected="true" role="treeitem" aria-level="2"><span>Birds</span></li>
+        <li id="tree0_item0_1" aria-selected="true" role="treeitem" aria-level="2" aria-expanded="false">
+        <span><span class="expander"></span>Cats</span>
+        <ul role="group">
+            <li id="tree0_item0_1_0" aria-selected="true" role="treeitem"aria-level="3"><span>Siamese</span></li>
+            <li id="tree0_item0_1_1" aria-selected="true" role="treeitem" aria-level="3"><span>Tabby</span></li>
+         </ul>
+         </li>
+      </ul>
+      </li>
+</ul>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests that the index attribute is correctly returned for all tree items, regardless of their hierarchical level.");
+
+    if (window.accessibilityController) {
+
+          var tree = document.getElementById("tree0");
+          tree.focus();
+          var axtree = accessibilityController.focusedElement;
+          tree.setAttribute("activedescendant", "tree0_item0");
+          shouldBe("axtree.selectedRowAtIndex(0).indexInTable()", "0");
+
+          tree.setAttribute("activedescendant", "tree0_item0_0");
+          shouldBe("axtree.selectedRowAtIndex(1).indexInTable()", "1");
+
+          tree.setAttribute("activedescendant", "tree0_item0_1");
+          shouldBe("axtree.selectedRowAtIndex(2).indexInTable()", "2");
+
+          tree.setAttribute("activedescendant", "tree0_item0_1_0");
+          shouldBe("axtree.selectedRowAtIndex(3).indexInTable()", "3");
+
+          tree.setAttribute("activedescendant", "tree0_item0_1_1");
+          shouldBe("axtree.selectedRowAtIndex(4).indexInTable()", "4");
+    }
+
+    successfullyParsed = true;
+</script>
+
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 002eb369daad8ef0a02d778ba00bca4d87c3ece7..2611e55ccbacffb74999ef3cf6f845badcbda55b 100644 (file)
@@ -1,3 +1,36 @@
+2009-12-03  Chris Fleizach  <cfleizach@apple.com>
+
+        Reviewed by Beth Dakin.
+
+        WAI-ARIA: aria-activedescendant doesn't work as intended
+        https://bugs.webkit.org/show_bug.cgi?id=32100
+
+        Fixes a number of issues regarding the "tree" role and aria-activedescendant.
+        1. The indexes were being reported incorrectly by treeitems.
+        2. aria-activedescendant changes were not being sent to the containing item.
+        3. The tree's selected rows need to consult aria-activedescendant.
+        4. Since a tree changes what it returns as its children (it returns its rows)
+           the mac-specific array indexing methods need to correctly handle the tree case.
+
+        Tests: platform/mac/accessibility/aria-tree-activedescendant.html
+               platform/mac/accessibility/aria-tree-index-of-items.html
+
+        * accessibility/AXObjectCache.h:
+        (WebCore::AXObjectCache::):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::shouldFocusActiveDescendant):
+        (WebCore::AccessibilityRenderObject::activeDescendant):
+        (WebCore::AccessibilityRenderObject::handleActiveDescendantChanged):
+        (WebCore::AccessibilityRenderObject::ariaTreeSelectedRows):
+        * accessibility/mac/AXObjectCacheMac.mm:
+        (WebCore::AXObjectCache::postPlatformNotification):
+        * accessibility/mac/AccessibilityObjectWrapper.mm:
+        (-[AccessibilityObjectWrapper accessibilityAttributeValue:]):
+        (-[AccessibilityObjectWrapper accessibilityIndexOfChild:]):
+        (-[AccessibilityObjectWrapper accessibilityArrayAttributeValues:index:maxCount:]):
+        * accessibility/win/AXObjectCacheWin.cpp:
+        (WebCore::AXObjectCache::postPlatformNotification):
+
 2009-12-03  Zoltan Horvath  <zoltan@webkit.org>
 
         Reviewed by Eric Seidel.
index 8ea76f54716911d364789194916296d69853a3f1..adfddcd45f86fd14bff09408c1196c59224c082e 100644 (file)
@@ -99,6 +99,7 @@ public:
     static VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
 
     enum AXNotification {
+        AXActiveDescendantChanged,
         AXCheckedStateChanged,
         AXFocusedUIElementChanged,
         AXLayoutComplete,
index 63937b4feafe77c6eddf51b50bf0da85350537f5..c57dd9281fe1689e6ea6685845164f851146d00f 100644 (file)
@@ -2439,14 +2439,14 @@ bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
     case ProgressIndicatorRole:
     case ToolbarRole:
     case OutlineRole:
+    case TreeRole:
+    case GridRole:
     /* FIXME: replace these with actual roles when they are added to AccessibilityRole
     composite
     alert
     alertdialog
-    grid
     status
     timer
-    tree
     */
         return true;
     default:
@@ -2456,19 +2456,22 @@ bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
 
 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
 {
-    if (renderer()->node() && !renderer()->node()->isElementNode())
+    if (!m_renderer)
         return 0;
-    Element* element = static_cast<Element*>(renderer()->node());
+    
+    if (m_renderer->node() && !m_renderer->node()->isElementNode())
+        return 0;
+    Element* element = static_cast<Element*>(m_renderer->node());
         
     String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string();
     if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
         return 0;
     
-    Element* target = renderer()->document()->getElementById(activeDescendantAttrStr);
+    Element* target = document()->getElementById(activeDescendantAttrStr);
     if (!target)
         return 0;
     
-    AccessibilityObject* obj = renderer()->document()->axObjectCache()->getOrCreate(target->renderer());
+    AccessibilityObject* obj = axObjectCache()->getOrCreate(target->renderer());
     if (obj && obj->isAccessibilityRenderObject())
     // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
         return obj;
@@ -2487,7 +2490,7 @@ void AccessibilityRenderObject::handleActiveDescendantChanged()
     AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
     
     if (activedescendant && shouldFocusActiveDescendant())
-        doc->axObjectCache()->postNotification(activedescendant->renderer(), AXObjectCache::AXFocusedUIElementChanged, true);
+        doc->axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
 }
 
 AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
@@ -2840,6 +2843,14 @@ void AccessibilityRenderObject::ariaTreeSelectedRows(AccessibilityChildrenVector
     // Determine which rows are selected.
     bool isMultiselectable = elementAttributeValue(aria_multiselectableAttr);
 
+    // Prefer active descendant over aria-selected.
+    AccessibilityObject* activeDesc = activeDescendant();
+    if (activeDesc && activeDesc->isTreeItem()) {
+        result.append(activeDesc);    
+        if (!isMultiselectable)
+            return;
+    }
+
     unsigned count = allRows.size();
     for (unsigned k = 0; k < count; ++k) {
         if (allRows[k]->isSelected()) {
index 2f18cf346263661024e620a55348a5e1ec3452bf..bf1b22c8d7718fa9c2119cdca5a6bca0909ac6d2 100644 (file)
@@ -59,6 +59,13 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
     // Some notifications are unique to Safari and do not have NSAccessibility equivalents.
     String macNotification;
     switch (notification) {
+        case AXActiveDescendantChanged:
+            // An active descendant change for trees means a selected rows change.
+            if (obj->isTree())
+                macNotification = NSAccessibilitySelectedRowsChangedNotification;
+            else
+                macNotification = NSAccessibilityFocusedUIElementChangedNotification;                
+            break;
         case AXCheckedStateChanged:
             macNotification = "AXCheckedStateChanged";
             break;
index 0a857b016c757ab329323dffbb2a08e371a6b565..7ae470abb1711cbf0aa4c2cce86756a8536c7c87 100644 (file)
@@ -1646,14 +1646,18 @@ static NSString* roleValueToNSString(AccessibilityRole value)
     if (m_object->isTreeItem()) {
         if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) {
             AccessibilityObject* parent = m_object->parentObject();
+            for (; parent && !parent->isTree(); parent = parent->parentObject())
+            { }
+            
             if (!parent)
                 return nil;
             
             // Find the index of this item by iterating the parents.
-            const AccessibilityObject::AccessibilityChildrenVector& children = parent->children();
-            unsigned count = children.size();
-            for (unsigned k = 0; k < count; ++k)
-                if (children[k]->wrapper() == self)
+            AccessibilityObject::AccessibilityChildrenVector rowsCopy;
+            parent->ariaTreeRows(rowsCopy);
+            size_t count = rowsCopy.size();
+            for (size_t k = 0; k < count; ++k)
+                if (rowsCopy[k]->wrapper() == self)
                     return [NSNumber numberWithUnsignedInt:k];
             
             return nil;
@@ -2477,7 +2481,12 @@ static RenderObject* rendererForView(NSView* view)
     m_object->updateBackingStore();
     if (!m_object)
         return NSNotFound;
-
+    
+    // Tree objects return their rows as their children. We can use the original method
+    // here, because we won't gain any speed up.
+    if (m_object->isTree())
+        return [super accessibilityIndexOfChild:child];
+       
     const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
        
     if (children.isEmpty())
@@ -2539,6 +2548,9 @@ static RenderObject* rendererForView(NSView* view)
             
             NSUInteger arrayLength = min(childCount - index, maxCount);
             return [children subarrayWithRange:NSMakeRange(index, arrayLength)];
+        } else if (m_object->isTree()) {
+            // Tree objects return their rows as their children. We can use the original method in this case.
+            return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
         }
         
         const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
index a1bdcc0d4fd9cf2e147c41e66e4c223c69b2a27e..863793cfb74842d98426acf325ae864de87450ed 100644 (file)
@@ -74,6 +74,7 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
     DWORD msaaEvent;
     switch (notification) {
         case AXFocusedUIElementChanged:
+        case AXActiveDescendantChanged:
             msaaEvent = EVENT_OBJECT_FOCUS;
             break;