Changeset 206102 in webkit


Ignore:
Timestamp:
Sep 19, 2016 11:32:21 AM (8 years ago)
Author:
n_wang@apple.com
Message:

AX: Add accessibility support for details element on iOS
https://bugs.webkit.org/show_bug.cgi?id=162041

Reviewed by Chris Fleizach.

Source/WebCore:

The details and summary elements are poorly supported on iOS.
Two major issues:

  1. Assistive technologies taking focus onto details/summary elements will cause unexpected behavior.
  2. VoiceOver is not speaking the expanded status of the details element.

Fixed them by not setting focus onto elements inside details and exposing the details element's expanded
status to its summary's accessible children.

Test: accessibility/ios-simulator/detail-summary-ios.html

  • accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:

(matchedParent):
(-[WebAccessibilityObjectWrapper _accessibilityListAncestor]):
(-[WebAccessibilityObjectWrapper _accessibilityLandmarkAncestor]):
(-[WebAccessibilityObjectWrapper _accessibilityTableAncestor]):
(-[WebAccessibilityObjectWrapper _accessibilityFieldsetAncestor]):
(-[WebAccessibilityObjectWrapper tableCellParent]):
(-[WebAccessibilityObjectWrapper tableParent]):
(-[WebAccessibilityObjectWrapper convertPointToScreenSpace:]):
(-[WebAccessibilityObjectWrapper convertRectToScreenSpace:]):
(-[WebAccessibilityObjectWrapper detailParentForSummaryObject:]):
(-[WebAccessibilityObjectWrapper detailParentForObject:]):
(-[WebAccessibilityObjectWrapper accessibilityElementDidBecomeFocused]):
(-[WebAccessibilityObjectWrapper accessibilitySupportsARIAExpanded]):
(-[WebAccessibilityObjectWrapper accessibilityIsExpanded]):

Tools:

  • DumpRenderTree/ios/AccessibilityUIElementIOS.mm:

(AccessibilityUIElement::isExpanded):

  • WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:

(WTR::AccessibilityUIElement::isExpanded):

LayoutTests:

  • accessibility/ios-simulator/detail-summary-ios-expected.txt: Added.
  • accessibility/ios-simulator/detail-summary-ios.html: Added.
Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r206100 r206102  
     12016-09-19  Nan Wang  <n_wang@apple.com>
     2
     3        AX: Add accessibility support for details element on iOS
     4        https://bugs.webkit.org/show_bug.cgi?id=162041
     5
     6        Reviewed by Chris Fleizach.
     7
     8        * accessibility/ios-simulator/detail-summary-ios-expected.txt: Added.
     9        * accessibility/ios-simulator/detail-summary-ios.html: Added.
     10
    1112016-09-19  Zalan Bujtas  <zalan@apple.com>
    212
  • trunk/Source/WebCore/ChangeLog

    r206100 r206102  
     12016-09-19  Nan Wang  <n_wang@apple.com>
     2
     3        AX: Add accessibility support for details element on iOS
     4        https://bugs.webkit.org/show_bug.cgi?id=162041
     5
     6        Reviewed by Chris Fleizach.
     7
     8        The details and summary elements are poorly supported on iOS.
     9        Two major issues:
     10            1. Assistive technologies taking focus onto details/summary elements will cause unexpected behavior.
     11            2. VoiceOver is not speaking the expanded status of the details element.
     12        Fixed them by not setting focus onto elements inside details and exposing the details element's expanded
     13        status to its summary's accessible children.
     14
     15        Test: accessibility/ios-simulator/detail-summary-ios.html
     16
     17        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
     18        (matchedParent):
     19        (-[WebAccessibilityObjectWrapper _accessibilityListAncestor]):
     20        (-[WebAccessibilityObjectWrapper _accessibilityLandmarkAncestor]):
     21        (-[WebAccessibilityObjectWrapper _accessibilityTableAncestor]):
     22        (-[WebAccessibilityObjectWrapper _accessibilityFieldsetAncestor]):
     23        (-[WebAccessibilityObjectWrapper tableCellParent]):
     24        (-[WebAccessibilityObjectWrapper tableParent]):
     25        (-[WebAccessibilityObjectWrapper convertPointToScreenSpace:]):
     26        (-[WebAccessibilityObjectWrapper convertRectToScreenSpace:]):
     27        (-[WebAccessibilityObjectWrapper detailParentForSummaryObject:]):
     28        (-[WebAccessibilityObjectWrapper detailParentForObject:]):
     29        (-[WebAccessibilityObjectWrapper accessibilityElementDidBecomeFocused]):
     30        (-[WebAccessibilityObjectWrapper accessibilitySupportsARIAExpanded]):
     31        (-[WebAccessibilityObjectWrapper accessibilityIsExpanded]):
     32
    1332016-09-19  Zalan Bujtas  <zalan@apple.com>
    234
  • trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm

    r205985 r206102  
    132132}
    133133
     134template<typename MatchFunction>
     135static AccessibilityObject* matchedParent(AccessibilityObject* object, bool includeSelf, const MatchFunction& matches)
     136{
     137    if (!object)
     138        return nullptr;
     139
     140    AccessibilityObject* parent = includeSelf ? object : object->parentObject();
     141    for (; parent; parent = parent->parentObject()) {
     142        if (matches(parent))
     143            return parent;
     144    }
     145   
     146    return nullptr;
     147}
     148
     149
    134150#pragma mark Accessibility Text Marker
    135151
     
    534550- (AccessibilityObjectWrapper*)_accessibilityListAncestor
    535551{
    536     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
    537         AccessibilityRole role = parent->roleValue();
    538         if (role == ListRole || role == ListBoxRole)
    539             return parent->wrapper();
    540     }
    541    
     552    auto matchFunc = [] (AccessibilityObject* object) {
     553        AccessibilityRole role = object->roleValue();
     554        return role == ListRole || role == ListBoxRole;
     555    };
     556   
     557    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
     558        return parent->wrapper();
    542559    return nil;
    543560}
     
    545562- (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
    546563{
    547     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
    548         if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
    549             return parent->wrapper();
    550     }
    551 
     564    auto matchFunc = [self] (AccessibilityObject* object) {
     565        return [self _accessibilityIsLandmarkRole:object->roleValue()];
     566    };
     567   
     568    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
     569        return parent->wrapper();
    552570    return nil;
    553571}
     
    555573- (AccessibilityObjectWrapper*)_accessibilityTableAncestor
    556574{
    557     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
    558         if (parent->roleValue() == TableRole || parent->roleValue() == GridRole)
    559             return parent->wrapper();   
    560     }
    561    
     575    auto matchFunc = [] (AccessibilityObject* object) {
     576        return object->roleValue() == TableRole || object->roleValue() == GridRole;
     577    };
     578   
     579    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
     580        return parent->wrapper();
    562581    return nil;
    563582}
     
    565584- (AccessibilityObjectWrapper*)_accessibilityFieldsetAncestor
    566585{
    567     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
    568         if (parent->isFieldset())
    569             return parent->wrapper();
    570     }
    571    
     586    auto matchFunc = [] (AccessibilityObject* object) {
     587        return object->isFieldset();
     588    };
     589   
     590    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
     591        return parent->wrapper();
    572592    return nil;
    573593}
     
    10221042{
    10231043    // Find if this element is in a table cell.
    1024     AccessibilityObject* cell = nullptr;
    1025     for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject())
    1026     { }
    1027    
    1028     if (!cell)
    1029         return nil;
    1030 
    1031     return static_cast<AccessibilityTableCell*>(cell);
     1044    auto matchFunc = [] (AccessibilityObject* object) {
     1045        return object->isTableCell();
     1046    };
     1047   
     1048    if (AccessibilityObject* parent = matchedParent(m_object, true, matchFunc))
     1049        return static_cast<AccessibilityTableCell*>(parent);
     1050    return nil;
    10321051}
    10331052
     
    10351054{
    10361055    // Find if the parent table for the table cell.
    1037     AccessibilityObject* parentTable = nullptr;
    1038     for (parentTable = m_object; parentTable && !(is<AccessibilityTable>(*parentTable) && downcast<AccessibilityTable>(*parentTable).isExposableThroughAccessibility()); parentTable = parentTable->parentObject())
    1039     { }
    1040    
    1041     if (!parentTable)
    1042         return nil;
    1043    
    1044     return static_cast<AccessibilityTable*>(parentTable);
     1056    auto matchFunc = [] (AccessibilityObject* object) {
     1057        return is<AccessibilityTable>(*object) && downcast<AccessibilityTable>(*object).isExposableThroughAccessibility();
     1058    };
     1059   
     1060    if (AccessibilityObject* parent = matchedParent(m_object, true, matchFunc))
     1061        return static_cast<AccessibilityTable*>(parent);
     1062    return nil;
    10451063}
    10461064
     
    14091427    else {
    14101428        // Find the appropriate scroll view to use to convert the contents to the window.
     1429        auto matchFunc = [] (AccessibilityObject* object) {
     1430            return is<AccessibilityScrollView>(*object);
     1431        };
     1432       
    14111433        ScrollView* scrollView = nullptr;
    1412         AccessibilityObject* parent = nullptr;
    1413         for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
    1414             if (is<AccessibilityScrollView>(*parent)) {
    1415                 scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
    1416                 break;
    1417             }
    1418         }
     1434        AccessibilityObject* parent = matchedParent(m_object, false, matchFunc);
     1435        if (parent)
     1436            scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
    14191437       
    14201438        IntPoint intPoint = flooredIntPoint(point);
     
    14631481    } else {
    14641482        // Find the appropriate scroll view to use to convert the contents to the window.
     1483        auto matchFunc = [] (AccessibilityObject* object) {
     1484            return is<AccessibilityScrollView>(*object);
     1485        };
     1486
    14651487        ScrollView* scrollView = nullptr;
    1466         AccessibilityObject* parent = nullptr;
    1467         for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
    1468             if (is<AccessibilityScrollView>(*parent)) {
    1469                 scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
    1470                 break;
    1471             }
    1472         }
     1488        AccessibilityObject* parent = matchedParent(m_object, false, matchFunc);
     1489        if (parent)
     1490            scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
    14731491       
    14741492        if (scrollView)
     
    18341852}
    18351853
     1854- (AccessibilityObject*)detailParentForSummaryObject:(AccessibilityObject*)object
     1855{
     1856    // Use this to check if an object is the child of a summary object.
     1857    // And return the summary's parent, which is the expandable details object.
     1858    auto matchFunc = [] (AccessibilityObject* object) {
     1859        return object->hasTagName(summaryTag);
     1860    };
     1861   
     1862    if (AccessibilityObject* summary = matchedParent(object, true, matchFunc))
     1863        return summary->parentObject();
     1864    return nil;
     1865}
     1866
     1867- (AccessibilityObject*)detailParentForObject:(AccessibilityObject*)object
     1868{
     1869    // Use this to check if an object is inside a details object.
     1870    auto matchFunc = [] (AccessibilityObject* object) {
     1871        return object->hasTagName(detailsTag);
     1872    };
     1873   
     1874    if (AccessibilityObject* details = matchedParent(object, true, matchFunc))
     1875        return details;
     1876    return nil;
     1877}
     1878
    18361879- (void)accessibilityElementDidBecomeFocused
    18371880{
     
    18461889
    18471890        if (object->canSetFocusAttribute()) {
     1891            // webkit.org/b/162041 Taking focus onto elements inside a details node will cause VO focusing onto random items.
     1892            if ([self detailParentForObject:object])
     1893                break;
    18481894            object->setFocused(true);
    18491895            break;
     
    26822728        return NO;
    26832729   
     2730    // Since details element is ignored on iOS, we should expose the expanded status on its
     2731    // summary's accessible children.
     2732    if (AccessibilityObject* detailParent = [self detailParentForSummaryObject:m_object])
     2733        return detailParent->supportsExpanded();
     2734   
    26842735    return m_object->supportsExpanded();
    26852736}
     
    26902741        return NO;
    26912742
     2743    // Since details element is ignored on iOS, we should expose the expanded status on its
     2744    // summary's accessible children.
     2745    if (AccessibilityObject* detailParent = [self detailParentForSummaryObject:m_object])
     2746        return detailParent->isExpanded();
     2747   
    26922748    return m_object->isExpanded();
    26932749}
  • trunk/Tools/ChangeLog

    r206078 r206102  
     12016-09-19  Nan Wang  <n_wang@apple.com>
     2
     3        AX: Add accessibility support for details element on iOS
     4        https://bugs.webkit.org/show_bug.cgi?id=162041
     5
     6        Reviewed by Chris Fleizach.
     7
     8        * DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
     9        (AccessibilityUIElement::isExpanded):
     10        * WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
     11        (WTR::AccessibilityUIElement::isExpanded):
     12
    1132016-09-18  Gyuyoung Kim  <gyuyoung.kim@navercorp.com>
    214
  • trunk/Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm

    r204906 r206102  
    9898- (BOOL)_accessibilityHasTouchEventListener;
    9999- (NSString *)accessibilityExpandedTextValue;
     100- (BOOL)accessibilityIsExpanded;
    100101
    101102// TextMarker related
     
    825826bool AccessibilityUIElement::isExpanded() const
    826827{
    827     return false;
     828    return [m_element accessibilityIsExpanded];
    828829}
    829830
  • trunk/Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm

    r204906 r206102  
    7575- (BOOL)_accessibilityHasTouchEventListener;
    7676- (NSString *)accessibilityExpandedTextValue;
     77- (BOOL)accessibilityIsExpanded;
    7778
    7879// TextMarker related
     
    569570bool AccessibilityUIElement::isExpanded() const
    570571{
    571     return false;
     572    return [m_element accessibilityIsExpanded];
    572573}
    573574
Note: See TracChangeset for help on using the changeset viewer.