Title: [131915] trunk
Revision
131915
Author
[email protected]
Date
2012-10-19 10:16:19 -0700 (Fri, 19 Oct 2012)

Log Message

AX: aria-hidden=false does not work as expected
https://bugs.webkit.org/show_bug.cgi?id=98787

Reviewed by Beth Dakin.

Source/WebCore: 

ARIA requires that aria-hidden=false override an element's native visibility and include that
node in the AX hierarchy.
 
To accomplish this we have to allow invisible items to be included, as well as items that
have no renderers associated with them.

Test: accessibility/aria-hidden-negates-no-visibility.html

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::getOrCreate):
* accessibility/AccessibilityARIAGrid.cpp:
(WebCore::AccessibilityARIAGrid::addTableCellChild):
(WebCore::AccessibilityARIAGrid::addChildren):
* accessibility/AccessibilityARIAGrid.h:
(AccessibilityARIAGrid):
* accessibility/AccessibilityNodeObject.cpp:
(WebCore):
(WebCore::AccessibilityNodeObject::boundingBoxRect):
(WebCore::AccessibilityNodeObject::insertChild):
(WebCore::AccessibilityNodeObject::addChild):
(WebCore::AccessibilityNodeObject::addChildren):
(WebCore::AccessibilityNodeObject::textUnderElement):
* accessibility/AccessibilityNodeObject.h:
(AccessibilityNodeObject):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::textIteratorBehaviorForTextRange):
(WebCore):
* accessibility/AccessibilityObject.h:
(AccessibilityObject):
(WebCore::AccessibilityObject::addChild):
(WebCore::AccessibilityObject::insertChild):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore):
(WebCore::AccessibilityRenderObject::accessibilityIsIgnoredBase):
(WebCore::AccessibilityRenderObject::addHiddenChildren):
(WebCore::AccessibilityRenderObject::addChildren):
* accessibility/AccessibilityRenderObject.h:
(AccessibilityRenderObject):

LayoutTests: 

* accessibility/aria-hidden-negates-no-visibility.html: Added.
* platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (131914 => 131915)


--- trunk/LayoutTests/ChangeLog	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/LayoutTests/ChangeLog	2012-10-19 17:16:19 UTC (rev 131915)
@@ -1,3 +1,13 @@
+2012-10-19  Chris Fleizach  <[email protected]>
+
+        AX: aria-hidden=false does not work as expected
+        https://bugs.webkit.org/show_bug.cgi?id=98787
+
+        Reviewed by Beth Dakin.
+
+        * accessibility/aria-hidden-negates-no-visibility.html: Added.
+        * platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt: Added.
+
 2012-10-19  Shinya Kawanaka  <[email protected]>
 
         Elements assigned to <shadow> should not be reprojected.

Added: trunk/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html (0 => 131915)


--- trunk/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html	                        (rev 0)
+++ trunk/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html	2012-10-19 17:16:19 UTC (rev 131915)
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body id="body">
+
+<h1 hidden id="heading1_1">heading1.1</h1>
+<h1 aria-hidden="true" id="heading1_2">heading1.2</h1>
+<h1 hidden aria-hidden="true" id="heading1_3">heading1.3</h1>
+
+<h1 hidden aria-hidden="false" id="heading2">heading2</h1>
+<h1 style="display:none;" aria-hidden="false" id="heading3">heading3</h1>
+<h1 style="visibility:hidden;" aria-hidden="false" id="heading4">heading4</h1>
+
+<div hidden aria-hidden="false" id="hiddenDiv">HiddenText1</div>
+<input type="text" aria-labelledby="hiddenDiv" id="textFieldWithHiddenLabeller">
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests ensures that aria-hidden=false will allow a node to be in the AX hierarchy even if it's not rendered or visible");
+
+    if (window.accessibilityController) {
+
+        // First groups of headings are actually hidde.n
+        var heading1_1 = accessibilityController.accessibleElementById("heading1_1");
+        shouldBeTrue("!heading1_1 || !heading1_1.isValid");
+
+        var heading1_2 = accessibilityController.accessibleElementById("heading1_2");
+        shouldBeTrue("!heading1_2 || !heading1_3.isValid");
+
+        var heading1_3 = accessibilityController.accessibleElementById("heading1_3");
+        shouldBeTrue("!heading1_3 || !heading1_3.isValid");
+
+        // These headings should be in AX tree despite being hidden.
+        var heading2 = accessibilityController.accessibleElementById("heading2");
+        shouldBe("heading2.role", "'AXRole: AXHeading'");
+
+        // Check that the order of these elements is correct based on DOM order.
+        var parent = heading2.parentElement();
+        shouldBeTrue("parent.childAtIndex(0).isEqual(heading2)");
+
+        var heading3 = accessibilityController.accessibleElementById("heading3");
+        shouldBe("heading3.role", "'AXRole: AXHeading'");
+        shouldBeTrue("parent.childAtIndex(1).isEqual(heading3)");
+
+        var heading4 = accessibilityController.accessibleElementById("heading4");
+        shouldBe("heading4.role", "'AXRole: AXHeading'");
+        shouldBeTrue("parent.childAtIndex(2).isEqual(heading4)");
+
+        // The aria-labelledby attribute should work even though hidden.
+        var textField = accessibilityController.accessibleElementById("textFieldWithHiddenLabeller");
+        debug("Textfield Title: " + textField.title);
+    }
+
+</script>
+
+<script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/platform/chromium/TestExpectations (131914 => 131915)


--- trunk/LayoutTests/platform/chromium/TestExpectations	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/LayoutTests/platform/chromium/TestExpectations	2012-10-19 17:16:19 UTC (rev 131915)
@@ -1424,6 +1424,7 @@
 webkit.org/b/99665 accessibility/loading-iframe-sends-notification.html [ Skip ]
 
 webkit.org/b/73912 accessibility/aria-checkbox-sends-notification.html [ Failure Pass ]
+webkit.org/b/98787 accessibility/aria-hidden-negates-no-visibility.html [ Skip ]
 
 # -----------------------------------------------------------------
 # Test failures with Skia GPU rendering of canvas 2D

Added: trunk/LayoutTests/platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt (0 => 131915)


--- trunk/LayoutTests/platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt	2012-10-19 17:16:19 UTC (rev 131915)
@@ -0,0 +1,23 @@
+heading1.2
+
+
+
+This tests ensures that aria-hidden=false will allow a node to be in the AX hierarchy even if it's not rendered or visible
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS !heading1_1 || !heading1_1.isValid is true
+PASS !heading1_2 || !heading1_3.isValid is true
+PASS !heading1_3 || !heading1_3.isValid is true
+PASS heading2.role is 'AXRole: AXHeading'
+PASS parent.childAtIndex(0).isEqual(heading2) is true
+PASS heading3.role is 'AXRole: AXHeading'
+PASS parent.childAtIndex(1).isEqual(heading3) is true
+PASS heading4.role is 'AXRole: AXHeading'
+PASS parent.childAtIndex(2).isEqual(heading4) is true
+Textfield Title: AXTitle: 
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Modified: trunk/Source/WebCore/ChangeLog (131914 => 131915)


--- trunk/Source/WebCore/ChangeLog	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/ChangeLog	2012-10-19 17:16:19 UTC (rev 131915)
@@ -1,3 +1,49 @@
+2012-10-19  Chris Fleizach  <[email protected]>
+
+        AX: aria-hidden=false does not work as expected
+        https://bugs.webkit.org/show_bug.cgi?id=98787
+
+        Reviewed by Beth Dakin.
+
+        ARIA requires that aria-hidden=false override an element's native visibility and include that
+        node in the AX hierarchy.
+ 
+        To accomplish this we have to allow invisible items to be included, as well as items that
+        have no renderers associated with them.
+
+        Test: accessibility/aria-hidden-negates-no-visibility.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::getOrCreate):
+        * accessibility/AccessibilityARIAGrid.cpp:
+        (WebCore::AccessibilityARIAGrid::addTableCellChild):
+        (WebCore::AccessibilityARIAGrid::addChildren):
+        * accessibility/AccessibilityARIAGrid.h:
+        (AccessibilityARIAGrid):
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore):
+        (WebCore::AccessibilityNodeObject::boundingBoxRect):
+        (WebCore::AccessibilityNodeObject::insertChild):
+        (WebCore::AccessibilityNodeObject::addChild):
+        (WebCore::AccessibilityNodeObject::addChildren):
+        (WebCore::AccessibilityNodeObject::textUnderElement):
+        * accessibility/AccessibilityNodeObject.h:
+        (AccessibilityNodeObject):
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::textIteratorBehaviorForTextRange):
+        (WebCore):
+        * accessibility/AccessibilityObject.h:
+        (AccessibilityObject):
+        (WebCore::AccessibilityObject::addChild):
+        (WebCore::AccessibilityObject::insertChild):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore):
+        (WebCore::AccessibilityRenderObject::accessibilityIsIgnoredBase):
+        (WebCore::AccessibilityRenderObject::addHiddenChildren):
+        (WebCore::AccessibilityRenderObject::addChildren):
+        * accessibility/AccessibilityRenderObject.h:
+        (AccessibilityRenderObject):
+
 2012-10-19  Tommy Widenflycht  <[email protected]>
 
         MediaStream API: Rename owner to client in MediaStreamDescriptor

Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp	2012-10-19 17:16:19 UTC (rev 131915)
@@ -316,8 +316,14 @@
     if (node->renderer())
         return getOrCreate(node->renderer());
 
+    if (!node->parentElement())
+        return 0;
+    
     // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
-    if (!node->parentElement() || !node->parentElement()->isInCanvasSubtree())
+    // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
+    bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
+    bool isHidden = !node->renderer() && isNodeAriaVisible(node);
+    if (!inCanvasSubtree && !isHidden)
         return 0;
 
     RefPtr<AccessibilityObject> newObj = createFromNode(node);
@@ -783,6 +789,17 @@
     const AccessibilityObject* axObject = getOrCreate(const_cast<Node*>(node));
     return axObject && axObject->isTextControl();
 }
+    
+bool isNodeAriaVisible(Node* node)
+{
+    if (!node)
+        return false;
+    
+    if (!node->isElementNode())
+        return false;
+    
+    return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
+}
 
 } // namespace WebCore
 

Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AXObjectCache.h	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h	2012-10-19 17:16:19 UTC (rev 131915)
@@ -208,7 +208,9 @@
 };
 
 bool nodeHasRole(Node*, const String& role);
-
+// This will let you know if aria-hidden was explicitly set to false.
+bool isNodeAriaVisible(Node*);
+    
 #if !HAVE(ACCESSIBILITY)
 inline AXObjectCache::AXObjectCache(const Document* doc) : m_document(const_cast<Document*>(doc)), m_notificationPostTimer(this, 0) { }
 inline AXObjectCache::~AXObjectCache() { }

Modified: trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp	2012-10-19 17:16:19 UTC (rev 131915)
@@ -66,7 +66,7 @@
     return adoptRef(obj);
 }
 
-bool AccessibilityARIAGrid::addChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
+bool AccessibilityARIAGrid::addTableCellChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
 {
     if (!child || !child->isTableRow() || child->ariaRoleAttribute() != RowRole)
         return false;
@@ -114,7 +114,7 @@
     unsigned columnCount = 0;
     for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) {
 
-        if (!addChild(child.get(), appendedRows, columnCount)) {
+        if (!addTableCellChild(child.get(), appendedRows, columnCount)) {
             
             // in case the render tree doesn't match the expected ARIA hierarchy, look at the children
             if (!child->hasChildren())
@@ -125,7 +125,7 @@
             AccessibilityChildrenVector children = child->children();
             size_t length = children.size();
             for (size_t i = 0; i < length; ++i)
-                addChild(children[i].get(), appendedRows, columnCount);
+                addTableCellChild(children[i].get(), appendedRows, columnCount);
         }
     }
     

Modified: trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.h (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.h	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.h	2012-10-19 17:16:19 UTC (rev 131915)
@@ -58,7 +58,7 @@
     virtual bool supportsSelectedRows() { return true; }    
     virtual bool isMultiSelectable() const { return true; }
     
-    bool addChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
+    bool addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
 };
 
 } // namespace WebCore 

Modified: trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp	2012-10-19 17:16:19 UTC (rev 131915)
@@ -222,6 +222,26 @@
 {
     return boundingBoxRect();
 }
+    
+LayoutRect AccessibilityNodeObject::boundingBoxRect() const
+{
+    // AccessibilityNodeObjects have no mechanism yet to return a size or position.
+    // For now, let's return the position of the ancestor that does have a position,
+    // and make it the width of that parent, and about the height of a line of text, so that it's clear the object is a child of the parent.
+    
+    LayoutRect boundingBox;
+    
+    for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
+        if (positionProvider->isAccessibilityRenderObject()) {
+            LayoutRect parentRect = positionProvider->elementRect();
+            boundingBox.setSize(LayoutSize(parentRect.width(), FractionalLayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
+            boundingBox.setLocation(parentRect.location());
+            break;
+        }
+    }
+    
+    return boundingBox;
+}
 
 void AccessibilityNodeObject::setNode(Node* node)
 {
@@ -284,6 +304,32 @@
     return UnknownRole;
 }
 
+void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index)
+{
+    if (!child)
+        return;
+    
+    // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
+    // or its visibility has changed. In the latter case, this child may have a stale child cached.
+    // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
+    child->clearChildren();
+    
+    if (child->accessibilityIsIgnored()) {
+        AccessibilityChildrenVector children = child->children();
+        size_t length = children.size();
+        for (size_t i = 0; i < length; ++i)
+            m_children.insert(index + i, children[i]);
+    } else {
+        ASSERT(child->parentObject() == this);
+        m_children.insert(index, child);
+    }
+}
+
+void AccessibilityNodeObject::addChild(AccessibilityObject* child)
+{
+    insertChild(child, m_children.size());
+}
+
 void AccessibilityNodeObject::addChildren()
 {
     // If the need to add more children in addition to existing children arises, 
@@ -299,19 +345,8 @@
     if (renderer() && !m_node->hasTagName(canvasTag))
         return;
     
-    for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) {
-        RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child);
-        obj->clearChildren();
-        if (obj->accessibilityIsIgnored()) {
-            AccessibilityChildrenVector children = obj->children();
-            size_t length = children.size();
-            for (size_t i = 0; i < length; ++i)
-                m_children.append(children[i]);
-        } else {
-            ASSERT(obj->parentObject() == this);
-            m_children.append(obj);
-        }
-    }
+    for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
+        addChild(axObjectCache()->getOrCreate(child));
 }
 
 bool AccessibilityNodeObject::canHaveChildren() const
@@ -1400,7 +1435,9 @@
     // If this could be fixed, it'd be more accurate use TextIterator here.
     if (node->isElementNode())
         return toElement(node)->innerText();
-
+    else if (node->isTextNode())
+        return toText(node)->wholeText();
+    
     return String();
 }
 

Modified: trunk/Source/WebCore/accessibility/AccessibilityNodeObject.h (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityNodeObject.h	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityNodeObject.h	2012-10-19 17:16:19 UTC (rev 131915)
@@ -158,6 +158,9 @@
 
     virtual AccessibilityRole determineAccessibilityRole();
     virtual void addChildren();
+    virtual void addChild(AccessibilityObject*);
+    virtual void insertChild(AccessibilityObject*, unsigned index);
+
     virtual bool canHaveChildren() const;
     virtual bool accessibilityIsIgnored() const;
     AccessibilityRole ariaRoleAttribute() const;
@@ -174,6 +177,7 @@
     void ariaLabeledByElements(Vector<Element*>& elements) const;
     String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
     void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName&) const;
+    virtual LayoutRect boundingBoxRect() const;
     String ariaDescribedByAttribute() const;
     
     Element* menuElementForMenuButton() const;

Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.cpp (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityObject.cpp	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.cpp	2012-10-19 17:16:19 UTC (rev 131915)
@@ -1776,6 +1776,19 @@
     return !getAttribute(aria_pressedAttr).isEmpty();
 }
 
+TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() const
+{
+    TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
+    
+#if PLATFORM(GTK)
+    // We need to emit replaced elements for GTK, and present
+    // them with the 'object replacement character' (0xFFFC).
+    behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
+#endif
+    
+    return behavior;
+}
+    
 AccessibilityRole AccessibilityObject::buttonRoleType() const
 {
     // If aria-pressed is present, then it should be exposed as a toggle button.

Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityObject.h	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h	2012-10-19 17:16:19 UTC (rev 131915)
@@ -33,6 +33,7 @@
 #include "FloatQuad.h"
 #include "FractionalLayoutRect.h"
 #include "LayoutTypes.h"
+#include "TextIterator.h"
 #include "VisiblePosition.h"
 #include "VisibleSelection.h"
 #include <wtf/Forward.h>
@@ -573,6 +574,7 @@
     virtual IntPoint clickPoint();
     static IntRect boundingBoxForQuads(RenderObject*, const Vector<FloatQuad>&);
     
+    TextIteratorBehavior textIteratorBehaviorForTextRange() const;
     virtual PlainTextRange selectedTextRange() const { return PlainTextRange(); }
     unsigned selectionStart() const { return selectedTextRange().start; }
     unsigned selectionEnd() const { return selectedTextRange().length; }
@@ -612,6 +614,9 @@
     virtual void updateAccessibilityRole() { }
     const AccessibilityChildrenVector& children();
     virtual void addChildren() { }
+    virtual void addChild(AccessibilityObject*) { }
+    virtual void insertChild(AccessibilityObject*, unsigned) { }
+    
     virtual bool canHaveChildren() const { return true; }
     virtual bool hasChildren() const { return m_haveChildren; }
     virtual void updateChildrenIfNecessary();

Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2012-10-19 17:16:19 UTC (rev 131915)
@@ -79,7 +79,6 @@
 #include "RenderedPosition.h"
 #include "Text.h"
 #include "TextControlInnerElements.h"
-#include "TextIterator.h"
 #include "htmlediting.h"
 #include "visible_units.h"
 #include <wtf/StdLibExtras.h>
@@ -599,19 +598,6 @@
     return String();
 }
 
-static TextIteratorBehavior textIteratorBehaviorForTextRange()
-{
-    TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
-
-#if PLATFORM(GTK)
-    // We need to emit replaced elements for GTK, and present
-    // them with the 'object replacement character' (0xFFFC).
-    behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
-#endif
-
-    return behavior;
-}
-
 String AccessibilityRenderObject::textUnderElement() const
 {
     if (!m_renderer)
@@ -1035,10 +1021,17 @@
 {
     // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
     
-    // Ignore invisible elements.
-    if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
+    if (!m_renderer)
         return IgnoreObject;
 
+    if (m_renderer->style()->visibility() != VISIBLE) {
+        // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
+        if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
+            return DefaultBehavior;
+        
+        return IgnoreObject;
+    }
+    
     // Anything marked as aria-hidden or a child of something aria-hidden must be hidden.
     if (ariaIsHidden())
         return IgnoreObject;
@@ -2651,6 +2644,59 @@
 }
 #endif
 
+// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
+// meaning that they should be exposed to the AX hierarchy.
+void AccessibilityRenderObject::addHiddenChildren()
+{
+    Node* node = this->node();
+    if (!node)
+        return;
+    
+    // First do a quick run through to determine if we have any hidden nodes (most often we will not).
+    // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
+    bool shouldInsertHiddenNodes = false;
+    for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
+        if (!child->renderer() && isNodeAriaVisible(child)) {
+            shouldInsertHiddenNodes = true;
+            break;
+        }
+    }
+    
+    if (!shouldInsertHiddenNodes)
+        return;
+    
+    // Iterate through all of the children, including those that may have already been added, and
+    // try to insert hidden nodes in the correct place in the DOM order.
+    unsigned insertionIndex = 0;
+    for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
+        if (child->renderer()) {
+            // Find out where the last render sibling is located within m_children.
+            AccessibilityObject* childObject = axObjectCache()->get(child->renderer());
+            if (childObject && childObject->accessibilityIsIgnored()) {
+                AccessibilityChildrenVector children = childObject->children();
+                if (children.size())
+                    childObject = children.last().get();
+                else
+                    childObject = 0;
+            }
+
+            if (childObject)
+                insertionIndex = m_children.find(childObject) + 1;
+            continue;
+        }
+
+        if (!isNodeAriaVisible(child))
+            continue;
+        
+        unsigned previousSize = m_children.size();
+        if (insertionIndex > previousSize)
+            insertionIndex = previousSize;
+        
+        insertChild(axObjectCache()->getOrCreate(child), insertionIndex);
+        insertionIndex += (m_children.size() - previousSize);
+    }
+}
+    
 void AccessibilityRenderObject::addChildren()
 {
     // If the need to add more children in addition to existing children arises, 
@@ -2662,24 +2708,10 @@
     if (!canHaveChildren())
         return;
     
-    // add all unignored acc children
-    for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
-        // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op), 
-        // or its visibility has changed. In the latter case, this child may have a stale child cached. 
-        // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
-        obj->clearChildren();
-
-        if (obj->accessibilityIsIgnored()) {
-            AccessibilityChildrenVector children = obj->children();
-            unsigned length = children.size();
-            for (unsigned i = 0; i < length; ++i)
-                m_children.append(children[i]);
-        } else {
-            ASSERT(obj->parentObject() == this);
-            m_children.append(obj);
-        }
-    }
+    for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling())
+        addChild(obj.get());
     
+    addHiddenChildren();
     addAttachmentChildren();
     addImageMapChildren();
     addTextFieldChildren();

Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h (131914 => 131915)


--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h	2012-10-19 17:14:40 UTC (rev 131914)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h	2012-10-19 17:16:19 UTC (rev 131915)
@@ -246,6 +246,7 @@
     bool isDescendantOfElementType(const QualifiedName& tagName) const;
     // This returns true if it's focusable but it's not content editable and it's not a control or ARIA control.
 
+    void addHiddenChildren();
     void addTextFieldChildren();
     void addImageMapChildren();
     void addCanvasChildren();
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to