Title: [206102] trunk
Revision
206102
Author
n_w...@apple.com
Date
2016-09-19 11:32:21 -0700 (Mon, 19 Sep 2016)

Log 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.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (206101 => 206102)


--- trunk/LayoutTests/ChangeLog	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/LayoutTests/ChangeLog	2016-09-19 18:32:21 UTC (rev 206102)
@@ -1,3 +1,13 @@
+2016-09-19  Nan Wang  <n_w...@apple.com>
+
+        AX: Add accessibility support for details element on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=162041
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/ios-simulator/detail-summary-ios-expected.txt: Added.
+        * accessibility/ios-simulator/detail-summary-ios.html: Added.
+
 2016-09-19  Zalan Bujtas  <za...@apple.com>
 
         ASSERTION FAILED: clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType] while loading guardian.co.uk

Added: trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios-expected.txt (0 => 206102)


--- trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios-expected.txt	2016-09-19 18:32:21 UTC (rev 206102)
@@ -0,0 +1,22 @@
+Summary1
+
+Detail1
+
+link
+Summary2
+
+This tests the details element on iOS.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS summary1.isExpanded is true
+PASS detail1.isExpanded is false
+PASS summary2.isExpanded is false
+PASS document.getElementById('summary1') === document.activeElement is false
+PASS document.getElementById('details1') === document.activeElement is false
+PASS document.getElementById('link') === document.activeElement is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios.html (0 => 206102)


--- trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios.html	                        (rev 0)
+++ trunk/LayoutTests/accessibility/ios-simulator/detail-summary-ios.html	2016-09-19 18:32:21 UTC (rev 206102)
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body id="body">
+
+<details open id="details1">
+  <summary id="summary1"><p id="s1">Summary1</p></summary>
+  <p id="d1">Detail1</p>
+  <a href="" id="link">link</a>
+</details>
+
+<details id="details2">
+  <summary><p id="s2">Summary2</p></summary>
+  <p>Detail2</p>
+</details>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests the details element on iOS.");
+
+    var callbackCount = 0;
+    if (window.accessibilityController) {
+    
+        // Test that we are getting the expanded status for the summary text, but not for the details children.
+        var summary1 = accessibilityController.accessibleElementById("s1");
+        var detail1 = accessibilityController.accessibleElementById("d1");
+        var summary2 = accessibilityController.accessibleElementById("s2");
+        var link = accessibilityController.accessibleElementById("link");
+        
+        shouldBeTrue("summary1.isExpanded");
+        shouldBeFalse("detail1.isExpanded");
+        shouldBeFalse("summary2.isExpanded");
+        
+        // Test that VO focus onto the details children won't set focus to the summary/details element or its children.
+        summary1.assistiveTechnologySimulatedFocus();
+        shouldBeFalse("document.getElementById('summary1') === document.activeElement");
+        
+        detail1.assistiveTechnologySimulatedFocus();
+        shouldBeFalse("document.getElementById('details1') === document.activeElement");
+        
+        link.assistiveTechnologySimulatedFocus();
+        shouldBeFalse("document.getElementById('link') === document.activeElement");
+    }
+    
+</script>
+
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (206101 => 206102)


--- trunk/Source/WebCore/ChangeLog	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/Source/WebCore/ChangeLog	2016-09-19 18:32:21 UTC (rev 206102)
@@ -1,3 +1,35 @@
+2016-09-19  Nan Wang  <n_w...@apple.com>
+
+        AX: Add accessibility support for details element on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=162041
+
+        Reviewed by Chris Fleizach.
+
+        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]):
+
 2016-09-19  Zalan Bujtas  <za...@apple.com>
 
         ASSERTION FAILED: clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType] while loading guardian.co.uk

Modified: trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm (206101 => 206102)


--- trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm	2016-09-19 18:32:21 UTC (rev 206102)
@@ -131,6 +131,22 @@
     return wrapper;
 }
 
+template<typename MatchFunction>
+static AccessibilityObject* matchedParent(AccessibilityObject* object, bool includeSelf, const MatchFunction& matches)
+{
+    if (!object)
+        return nullptr;
+
+    AccessibilityObject* parent = includeSelf ? object : object->parentObject();
+    for (; parent; parent = parent->parentObject()) {
+        if (matches(parent))
+            return parent;
+    }
+    
+    return nullptr;
+}
+
+
 #pragma mark Accessibility Text Marker
 
 @interface WebAccessibilityTextMarker : NSObject
@@ -533,42 +549,46 @@
 
 - (AccessibilityObjectWrapper*)_accessibilityListAncestor
 {
-    for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
-        AccessibilityRole role = parent->roleValue();
-        if (role == ListRole || role == ListBoxRole)
-            return parent->wrapper();
-    }
+    auto matchFunc = [] (AccessibilityObject* object) {
+        AccessibilityRole role = object->roleValue();
+        return role == ListRole || role == ListBoxRole;
+    };
     
+    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
+        return parent->wrapper();
     return nil;
 }
 
 - (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
 {
-    for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
-        if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
-            return parent->wrapper();
-    }
-
+    auto matchFunc = [self] (AccessibilityObject* object) {
+        return [self _accessibilityIsLandmarkRole:object->roleValue()];
+    };
+    
+    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
+        return parent->wrapper();
     return nil;
 }
 
 - (AccessibilityObjectWrapper*)_accessibilityTableAncestor
 {
-    for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
-        if (parent->roleValue() == TableRole || parent->roleValue() == GridRole)
-            return parent->wrapper();   
-    }
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return object->roleValue() == TableRole || object->roleValue() == GridRole;
+    };
     
+    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
+        return parent->wrapper();
     return nil;
 }
 
 - (AccessibilityObjectWrapper*)_accessibilityFieldsetAncestor
 {
-    for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
-        if (parent->isFieldset())
-            return parent->wrapper();
-    }
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return object->isFieldset();
+    };
     
+    if (AccessibilityObject* parent = matchedParent(m_object, false, matchFunc))
+        return parent->wrapper();
     return nil;
 }
 
@@ -1021,27 +1041,25 @@
 - (AccessibilityTableCell*)tableCellParent
 {
     // Find if this element is in a table cell.
-    AccessibilityObject* cell = nullptr;
-    for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject()) 
-    { }
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return object->isTableCell();
+    };
     
-    if (!cell)
-        return nil;
-
-    return static_cast<AccessibilityTableCell*>(cell);
+    if (AccessibilityObject* parent = matchedParent(m_object, true, matchFunc))
+        return static_cast<AccessibilityTableCell*>(parent);
+    return nil;
 }
 
 - (AccessibilityTable*)tableParent
 {
     // Find if the parent table for the table cell.
-    AccessibilityObject* parentTable = nullptr;
-    for (parentTable = m_object; parentTable && !(is<AccessibilityTable>(*parentTable) && downcast<AccessibilityTable>(*parentTable).isExposableThroughAccessibility()); parentTable = parentTable->parentObject())
-    { }
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return is<AccessibilityTable>(*object) && downcast<AccessibilityTable>(*object).isExposableThroughAccessibility();
+    };
     
-    if (!parentTable)
-        return nil;
-    
-    return static_cast<AccessibilityTable*>(parentTable);
+    if (AccessibilityObject* parent = matchedParent(m_object, true, matchFunc))
+        return static_cast<AccessibilityTable*>(parent);
+    return nil;
 }
 
 - (id)accessibilityTitleElement
@@ -1408,14 +1426,14 @@
     }
     else {
         // Find the appropriate scroll view to use to convert the contents to the window.
+        auto matchFunc = [] (AccessibilityObject* object) {
+            return is<AccessibilityScrollView>(*object);
+        };
+        
         ScrollView* scrollView = nullptr;
-        AccessibilityObject* parent = nullptr;
-        for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
-            if (is<AccessibilityScrollView>(*parent)) {
-                scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
-                break;
-            }
-        }
+        AccessibilityObject* parent = matchedParent(m_object, false, matchFunc);
+        if (parent)
+            scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
         
         IntPoint intPoint = flooredIntPoint(point);
         if (scrollView)
@@ -1462,14 +1480,14 @@
         
     } else {
         // Find the appropriate scroll view to use to convert the contents to the window.
+        auto matchFunc = [] (AccessibilityObject* object) {
+            return is<AccessibilityScrollView>(*object);
+        };
+
         ScrollView* scrollView = nullptr;
-        AccessibilityObject* parent = nullptr;
-        for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
-            if (is<AccessibilityScrollView>(*parent)) {
-                scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
-                break;
-            }
-        }
+        AccessibilityObject* parent = matchedParent(m_object, false, matchFunc);
+        if (parent)
+            scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
         
         if (scrollView)
             rect = scrollView->contentsToRootView(rect);
@@ -1833,6 +1851,31 @@
     return m_object->scrollVisibleContentRect();
 }
 
+- (AccessibilityObject*)detailParentForSummaryObject:(AccessibilityObject*)object
+{
+    // Use this to check if an object is the child of a summary object.
+    // And return the summary's parent, which is the expandable details object.
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return object->hasTagName(summaryTag);
+    };
+    
+    if (AccessibilityObject* summary = matchedParent(object, true, matchFunc))
+        return summary->parentObject();
+    return nil;
+}
+
+- (AccessibilityObject*)detailParentForObject:(AccessibilityObject*)object
+{
+    // Use this to check if an object is inside a details object.
+    auto matchFunc = [] (AccessibilityObject* object) {
+        return object->hasTagName(detailsTag);
+    };
+    
+    if (AccessibilityObject* details = matchedParent(object, true, matchFunc))
+        return details;
+    return nil;
+}
+
 - (void)accessibilityElementDidBecomeFocused
 {
     if (![self _prepareAccessibilityCall])
@@ -1845,6 +1888,9 @@
             break;
 
         if (object->canSetFocusAttribute()) {
+            // webkit.org/b/162041 Taking focus onto elements inside a details node will cause VO focusing onto random items.
+            if ([self detailParentForObject:object])
+                break;
             object->setFocused(true);
             break;
         }
@@ -2681,6 +2727,11 @@
     if (![self _prepareAccessibilityCall])
         return NO;
     
+    // Since details element is ignored on iOS, we should expose the expanded status on its
+    // summary's accessible children.
+    if (AccessibilityObject* detailParent = [self detailParentForSummaryObject:m_object])
+        return detailParent->supportsExpanded();
+    
     return m_object->supportsExpanded();
 }
 
@@ -2689,6 +2740,11 @@
     if (![self _prepareAccessibilityCall])
         return NO;
 
+    // Since details element is ignored on iOS, we should expose the expanded status on its
+    // summary's accessible children.
+    if (AccessibilityObject* detailParent = [self detailParentForSummaryObject:m_object])
+        return detailParent->isExpanded();
+    
     return m_object->isExpanded();
 }
 

Modified: trunk/Tools/ChangeLog (206101 => 206102)


--- trunk/Tools/ChangeLog	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/Tools/ChangeLog	2016-09-19 18:32:21 UTC (rev 206102)
@@ -1,3 +1,15 @@
+2016-09-19  Nan Wang  <n_w...@apple.com>
+
+        AX: Add accessibility support for details element on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=162041
+
+        Reviewed by Chris Fleizach.
+
+        * DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
+        (AccessibilityUIElement::isExpanded):
+        * WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
+        (WTR::AccessibilityUIElement::isExpanded):
+
 2016-09-18  Gyuyoung Kim  <gyuyoung....@navercorp.com>
 
         [EFL] Bump efl library to 1.18.1

Modified: trunk/Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm (206101 => 206102)


--- trunk/Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm	2016-09-19 18:32:21 UTC (rev 206102)
@@ -97,6 +97,7 @@
 - (id)_accessibilityFieldsetAncestor;
 - (BOOL)_accessibilityHasTouchEventListener;
 - (NSString *)accessibilityExpandedTextValue;
+- (BOOL)accessibilityIsExpanded;
 
 // TextMarker related
 - (NSArray *)textMarkerRange;
@@ -824,7 +825,7 @@
 
 bool AccessibilityUIElement::isExpanded() const
 {
-    return false;
+    return [m_element accessibilityIsExpanded];
 }
 
 bool AccessibilityUIElement::isChecked() const

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm (206101 => 206102)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm	2016-09-19 17:53:37 UTC (rev 206101)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm	2016-09-19 18:32:21 UTC (rev 206102)
@@ -74,6 +74,7 @@
 - (id)_accessibilityFieldsetAncestor;
 - (BOOL)_accessibilityHasTouchEventListener;
 - (NSString *)accessibilityExpandedTextValue;
+- (BOOL)accessibilityIsExpanded;
 
 // TextMarker related
 - (NSArray *)textMarkerRange;
@@ -568,7 +569,7 @@
 
 bool AccessibilityUIElement::isExpanded() const
 {
-    return false;
+    return [m_element accessibilityIsExpanded];
 }
 
 bool AccessibilityUIElement::isChecked() const
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to