Changeset 131915 in webkit


Ignore:
Timestamp:
Oct 19, 2012 10:16:19 AM (12 years ago)
Author:
Chris Fleizach
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.
Location:
trunk
Files:
2 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r131910 r131915  
     12012-10-19  Chris Fleizach  <cfleizach@apple.com>
     2
     3        AX: aria-hidden=false does not work as expected
     4        https://bugs.webkit.org/show_bug.cgi?id=98787
     5
     6        Reviewed by Beth Dakin.
     7
     8        * accessibility/aria-hidden-negates-no-visibility.html: Added.
     9        * platform/mac/accessibility/aria-hidden-negates-no-visibility-expected.txt: Added.
     10
    1112012-10-19  Shinya Kawanaka  <shinyak@chromium.org>
    212
  • trunk/LayoutTests/platform/chromium/TestExpectations

    r131908 r131915  
    14251425
    14261426webkit.org/b/73912 accessibility/aria-checkbox-sends-notification.html [ Failure Pass ]
     1427webkit.org/b/98787 accessibility/aria-hidden-negates-no-visibility.html [ Skip ]
    14271428
    14281429# -----------------------------------------------------------------
  • trunk/Source/WebCore/ChangeLog

    r131914 r131915  
     12012-10-19  Chris Fleizach  <cfleizach@apple.com>
     2
     3        AX: aria-hidden=false does not work as expected
     4        https://bugs.webkit.org/show_bug.cgi?id=98787
     5
     6        Reviewed by Beth Dakin.
     7
     8        ARIA requires that aria-hidden=false override an element's native visibility and include that
     9        node in the AX hierarchy.
     10 
     11        To accomplish this we have to allow invisible items to be included, as well as items that
     12        have no renderers associated with them.
     13
     14        Test: accessibility/aria-hidden-negates-no-visibility.html
     15
     16        * accessibility/AXObjectCache.cpp:
     17        (WebCore::AXObjectCache::getOrCreate):
     18        * accessibility/AccessibilityARIAGrid.cpp:
     19        (WebCore::AccessibilityARIAGrid::addTableCellChild):
     20        (WebCore::AccessibilityARIAGrid::addChildren):
     21        * accessibility/AccessibilityARIAGrid.h:
     22        (AccessibilityARIAGrid):
     23        * accessibility/AccessibilityNodeObject.cpp:
     24        (WebCore):
     25        (WebCore::AccessibilityNodeObject::boundingBoxRect):
     26        (WebCore::AccessibilityNodeObject::insertChild):
     27        (WebCore::AccessibilityNodeObject::addChild):
     28        (WebCore::AccessibilityNodeObject::addChildren):
     29        (WebCore::AccessibilityNodeObject::textUnderElement):
     30        * accessibility/AccessibilityNodeObject.h:
     31        (AccessibilityNodeObject):
     32        * accessibility/AccessibilityObject.cpp:
     33        (WebCore::AccessibilityObject::textIteratorBehaviorForTextRange):
     34        (WebCore):
     35        * accessibility/AccessibilityObject.h:
     36        (AccessibilityObject):
     37        (WebCore::AccessibilityObject::addChild):
     38        (WebCore::AccessibilityObject::insertChild):
     39        * accessibility/AccessibilityRenderObject.cpp:
     40        (WebCore):
     41        (WebCore::AccessibilityRenderObject::accessibilityIsIgnoredBase):
     42        (WebCore::AccessibilityRenderObject::addHiddenChildren):
     43        (WebCore::AccessibilityRenderObject::addChildren):
     44        * accessibility/AccessibilityRenderObject.h:
     45        (AccessibilityRenderObject):
     46
    1472012-10-19  Tommy Widenflycht  <tommyw@google.com>
    248
  • trunk/Source/WebCore/accessibility/AXObjectCache.cpp

    r130612 r131915  
    317317        return getOrCreate(node->renderer());
    318318
     319    if (!node->parentElement())
     320        return 0;
     321   
    319322    // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
    320     if (!node->parentElement() || !node->parentElement()->isInCanvasSubtree())
     323    // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
     324    bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
     325    bool isHidden = !node->renderer() && isNodeAriaVisible(node);
     326    if (!inCanvasSubtree && !isHidden)
    321327        return 0;
    322328
     
    784790    return axObject && axObject->isTextControl();
    785791}
     792   
     793bool isNodeAriaVisible(Node* node)
     794{
     795    if (!node)
     796        return false;
     797   
     798    if (!node->isElementNode())
     799        return false;
     800   
     801    return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
     802}
    786803
    787804} // namespace WebCore
  • trunk/Source/WebCore/accessibility/AXObjectCache.h

    r128570 r131915  
    209209
    210210bool nodeHasRole(Node*, const String& role);
    211 
     211// This will let you know if aria-hidden was explicitly set to false.
     212bool isNodeAriaVisible(Node*);
     213   
    212214#if !HAVE(ACCESSIBILITY)
    213215inline AXObjectCache::AXObjectCache(const Document* doc) : m_document(const_cast<Document*>(doc)), m_notificationPostTimer(this, 0) { }
  • trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp

    r123428 r131915  
    6767}
    6868
    69 bool AccessibilityARIAGrid::addChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
     69bool AccessibilityARIAGrid::addTableCellChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
    7070{
    7171    if (!child || !child->isTableRow() || child->ariaRoleAttribute() != RowRole)
     
    115115    for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) {
    116116
    117         if (!addChild(child.get(), appendedRows, columnCount)) {
     117        if (!addTableCellChild(child.get(), appendedRows, columnCount)) {
    118118           
    119119            // in case the render tree doesn't match the expected ARIA hierarchy, look at the children
     
    126126            size_t length = children.size();
    127127            for (size_t i = 0; i < length; ++i)
    128                 addChild(children[i].get(), appendedRows, columnCount);
     128                addTableCellChild(children[i].get(), appendedRows, columnCount);
    129129        }
    130130    }
  • trunk/Source/WebCore/accessibility/AccessibilityARIAGrid.h

    r124582 r131915  
    5959    virtual bool isMultiSelectable() const { return true; }
    6060   
    61     bool addChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
     61    bool addTableCellChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
    6262};
    6363
  • trunk/Source/WebCore/accessibility/AccessibilityNodeObject.cpp

    r131905 r131915  
    223223    return boundingBoxRect();
    224224}
     225   
     226LayoutRect AccessibilityNodeObject::boundingBoxRect() const
     227{
     228    // AccessibilityNodeObjects have no mechanism yet to return a size or position.
     229    // For now, let's return the position of the ancestor that does have a position,
     230    // 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.
     231   
     232    LayoutRect boundingBox;
     233   
     234    for (AccessibilityObject* positionProvider = parentObject(); positionProvider; positionProvider = positionProvider->parentObject()) {
     235        if (positionProvider->isAccessibilityRenderObject()) {
     236            LayoutRect parentRect = positionProvider->elementRect();
     237            boundingBox.setSize(LayoutSize(parentRect.width(), FractionalLayoutUnit(std::min(10.0f, parentRect.height().toFloat()))));
     238            boundingBox.setLocation(parentRect.location());
     239            break;
     240        }
     241    }
     242   
     243    return boundingBox;
     244}
    225245
    226246void AccessibilityNodeObject::setNode(Node* node)
     
    285305}
    286306
     307void AccessibilityNodeObject::insertChild(AccessibilityObject* child, unsigned index)
     308{
     309    if (!child)
     310        return;
     311   
     312    // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
     313    // or its visibility has changed. In the latter case, this child may have a stale child cached.
     314    // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
     315    child->clearChildren();
     316   
     317    if (child->accessibilityIsIgnored()) {
     318        AccessibilityChildrenVector children = child->children();
     319        size_t length = children.size();
     320        for (size_t i = 0; i < length; ++i)
     321            m_children.insert(index + i, children[i]);
     322    } else {
     323        ASSERT(child->parentObject() == this);
     324        m_children.insert(index, child);
     325    }
     326}
     327
     328void AccessibilityNodeObject::addChild(AccessibilityObject* child)
     329{
     330    insertChild(child, m_children.size());
     331}
     332
    287333void AccessibilityNodeObject::addChildren()
    288334{
     
    300346        return;
    301347   
    302     for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) {
    303         RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child);
    304         obj->clearChildren();
    305         if (obj->accessibilityIsIgnored()) {
    306             AccessibilityChildrenVector children = obj->children();
    307             size_t length = children.size();
    308             for (size_t i = 0; i < length; ++i)
    309                 m_children.append(children[i]);
    310         } else {
    311             ASSERT(obj->parentObject() == this);
    312             m_children.append(obj);
    313         }
    314     }
     348    for (Node* child = m_node->firstChild(); child; child = child->nextSibling())
     349        addChild(axObjectCache()->getOrCreate(child));
    315350}
    316351
     
    14011436    if (node->isElementNode())
    14021437        return toElement(node)->innerText();
    1403 
     1438    else if (node->isTextNode())
     1439        return toText(node)->wholeText();
     1440   
    14041441    return String();
    14051442}
  • trunk/Source/WebCore/accessibility/AccessibilityNodeObject.h

    r131905 r131915  
    159159    virtual AccessibilityRole determineAccessibilityRole();
    160160    virtual void addChildren();
     161    virtual void addChild(AccessibilityObject*);
     162    virtual void insertChild(AccessibilityObject*, unsigned index);
     163
    161164    virtual bool canHaveChildren() const;
    162165    virtual bool accessibilityIsIgnored() const;
     
    175178    String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
    176179    void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName&) const;
     180    virtual LayoutRect boundingBoxRect() const;
    177181    String ariaDescribedByAttribute() const;
    178182   
  • trunk/Source/WebCore/accessibility/AccessibilityObject.cpp

    r128748 r131915  
    17771777}
    17781778
     1779TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() const
     1780{
     1781    TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
     1782   
     1783#if PLATFORM(GTK)
     1784    // We need to emit replaced elements for GTK, and present
     1785    // them with the 'object replacement character' (0xFFFC).
     1786    behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
     1787#endif
     1788   
     1789    return behavior;
     1790}
     1791   
    17791792AccessibilityRole AccessibilityObject::buttonRoleType() const
    17801793{
  • trunk/Source/WebCore/accessibility/AccessibilityObject.h

    r131905 r131915  
    3434#include "FractionalLayoutRect.h"
    3535#include "LayoutTypes.h"
     36#include "TextIterator.h"
    3637#include "VisiblePosition.h"
    3738#include "VisibleSelection.h"
     
    574575    static IntRect boundingBoxForQuads(RenderObject*, const Vector<FloatQuad>&);
    575576   
     577    TextIteratorBehavior textIteratorBehaviorForTextRange() const;
    576578    virtual PlainTextRange selectedTextRange() const { return PlainTextRange(); }
    577579    unsigned selectionStart() const { return selectedTextRange().start; }
     
    613615    const AccessibilityChildrenVector& children();
    614616    virtual void addChildren() { }
     617    virtual void addChild(AccessibilityObject*) { }
     618    virtual void insertChild(AccessibilityObject*, unsigned) { }
     619   
    615620    virtual bool canHaveChildren() const { return true; }
    616621    virtual bool hasChildren() const { return m_haveChildren; }
  • trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp

    r131908 r131915  
    8080#include "Text.h"
    8181#include "TextControlInnerElements.h"
    82 #include "TextIterator.h"
    8382#include "htmlediting.h"
    8483#include "visible_units.h"
     
    598597   
    599598    return String();
    600 }
    601 
    602 static TextIteratorBehavior textIteratorBehaviorForTextRange()
    603 {
    604     TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
    605 
    606 #if PLATFORM(GTK)
    607     // We need to emit replaced elements for GTK, and present
    608     // them with the 'object replacement character' (0xFFFC).
    609     behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
    610 #endif
    611 
    612     return behavior;
    613599}
    614600
     
    10361022    // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
    10371023   
    1038     // Ignore invisible elements.
    1039     if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
     1024    if (!m_renderer)
    10401025        return IgnoreObject;
    10411026
     1027    if (m_renderer->style()->visibility() != VISIBLE) {
     1028        // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
     1029        if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "false"))
     1030            return DefaultBehavior;
     1031       
     1032        return IgnoreObject;
     1033    }
     1034   
    10421035    // Anything marked as aria-hidden or a child of something aria-hidden must be hidden.
    10431036    if (ariaIsHidden())
     
    26522645#endif
    26532646
     2647// Hidden children are those that are not rendered or visible, but are specifically marked as aria-hidden=false,
     2648// meaning that they should be exposed to the AX hierarchy.
     2649void AccessibilityRenderObject::addHiddenChildren()
     2650{
     2651    Node* node = this->node();
     2652    if (!node)
     2653        return;
     2654   
     2655    // First do a quick run through to determine if we have any hidden nodes (most often we will not).
     2656    // If we do have hidden nodes, we need to determine where to insert them so they match DOM order as close as possible.
     2657    bool shouldInsertHiddenNodes = false;
     2658    for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
     2659        if (!child->renderer() && isNodeAriaVisible(child)) {
     2660            shouldInsertHiddenNodes = true;
     2661            break;
     2662        }
     2663    }
     2664   
     2665    if (!shouldInsertHiddenNodes)
     2666        return;
     2667   
     2668    // Iterate through all of the children, including those that may have already been added, and
     2669    // try to insert hidden nodes in the correct place in the DOM order.
     2670    unsigned insertionIndex = 0;
     2671    for (Node* child = node->firstChild(); child; child = child->nextSibling()) {
     2672        if (child->renderer()) {
     2673            // Find out where the last render sibling is located within m_children.
     2674            AccessibilityObject* childObject = axObjectCache()->get(child->renderer());
     2675            if (childObject && childObject->accessibilityIsIgnored()) {
     2676                AccessibilityChildrenVector children = childObject->children();
     2677                if (children.size())
     2678                    childObject = children.last().get();
     2679                else
     2680                    childObject = 0;
     2681            }
     2682
     2683            if (childObject)
     2684                insertionIndex = m_children.find(childObject) + 1;
     2685            continue;
     2686        }
     2687
     2688        if (!isNodeAriaVisible(child))
     2689            continue;
     2690       
     2691        unsigned previousSize = m_children.size();
     2692        if (insertionIndex > previousSize)
     2693            insertionIndex = previousSize;
     2694       
     2695        insertChild(axObjectCache()->getOrCreate(child), insertionIndex);
     2696        insertionIndex += (m_children.size() - previousSize);
     2697    }
     2698}
     2699   
    26542700void AccessibilityRenderObject::addChildren()
    26552701{
     
    26632709        return;
    26642710   
    2665     // add all unignored acc children
    2666     for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
    2667         // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op),
    2668         // or its visibility has changed. In the latter case, this child may have a stale child cached.
    2669         // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
    2670         obj->clearChildren();
    2671 
    2672         if (obj->accessibilityIsIgnored()) {
    2673             AccessibilityChildrenVector children = obj->children();
    2674             unsigned length = children.size();
    2675             for (unsigned i = 0; i < length; ++i)
    2676                 m_children.append(children[i]);
    2677         } else {
    2678             ASSERT(obj->parentObject() == this);
    2679             m_children.append(obj);
    2680         }
    2681     }
    2682    
     2711    for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling())
     2712        addChild(obj.get());
     2713   
     2714    addHiddenChildren();
    26832715    addAttachmentChildren();
    26842716    addImageMapChildren();
  • trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h

    r131905 r131915  
    247247    // This returns true if it's focusable but it's not content editable and it's not a control or ARIA control.
    248248
     249    void addHiddenChildren();
    249250    void addTextFieldChildren();
    250251    void addImageMapChildren();
Note: See TracChangeset for help on using the changeset viewer.