summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-01 22:58:52 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-01 22:58:52 +0000
commit111e7e6c5f51a35d39ccf1cff75e676b1b0d8f6b (patch)
tree0f62236b143d97c2c22696cb238cfc230e5ad545
parent3b00c2d3dc17dfe7927b4884d029a3aa80afb6d3 (diff)
downloadchromium_src-111e7e6c5f51a35d39ccf1cff75e676b1b0d8f6b.zip
chromium_src-111e7e6c5f51a35d39ccf1cff75e676b1b0d8f6b.tar.gz
chromium_src-111e7e6c5f51a35d39ccf1cff75e676b1b0d8f6b.tar.bz2
Support list / listbox accessibility attributes on Mac.
This change adds support for AXSelectedChildren, AXVisibleChildren, and AXOrientation for lists and list boxes. Those are sufficient for VoiceOver to read, e.g. (3 of 5) when navigating a list of 5 items. BUG=381971 Review URL: https://codereview.chromium.org/348183007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280906 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/accessibility/accessibility_tree_formatter_mac.mm38
-rw-r--r--content/browser/accessibility/browser_accessibility_cocoa.h2
-rw-r--r--content/browser/accessibility/browser_accessibility_cocoa.mm59
-rw-r--r--content/browser/accessibility/dump_accessibility_tree_browsertest.cc15
-rw-r--r--content/test/data/accessibility/aria-labelledby-heading-expected-mac.txt2
-rw-r--r--content/test/data/accessibility/aria-list-expected-mac.txt2
-rw-r--r--content/test/data/accessibility/aria-list.html9
-rw-r--r--content/test/data/accessibility/aria-listbox-activedescendant-expected-android.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-activedescendant-expected-mac.txt5
-rw-r--r--content/test/data/accessibility/aria-listbox-activedescendant-expected-win.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-activedescendant.html16
-rw-r--r--content/test/data/accessibility/aria-listbox-aria-selected-expected-android.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-aria-selected-expected-mac.txt7
-rw-r--r--content/test/data/accessibility/aria-listbox-aria-selected-expected-win.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-aria-selected.html17
-rw-r--r--content/test/data/accessibility/aria-listbox-childfocus-expected-android.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-childfocus-expected-mac.txt5
-rw-r--r--content/test/data/accessibility/aria-listbox-childfocus-expected-win.txt1
-rw-r--r--content/test/data/accessibility/aria-listbox-childfocus.html16
-rw-r--r--content/test/data/accessibility/aria-menu-expected-mac.txt2
20 files changed, 185 insertions, 16 deletions
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index e037d4d..69e5c76 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -94,19 +94,35 @@ scoped_ptr<base::ListValue> PopulateArray(NSArray* array) {
scoped_ptr<base::StringValue> StringForBrowserAccessibility(
BrowserAccessibilityCocoa* obj) {
- NSString* description = [obj role];
- id value = [obj value];
+ NSMutableArray* tokens = [[NSMutableArray alloc] init];
+
+ // Always include the role
+ id role = [obj role];
+ [tokens addObject:role];
+
+ // If the role is "group", include the role description as well.
id roleDescription = [obj roleDescription];
- if (value && ![value isEqual:@""]) {
- description = [NSString stringWithFormat:@"%@ %@", description, value];
- } else if ([description isEqualToString:NSAccessibilityGroupRole] &&
- roleDescription != nil &&
- ![roleDescription isEqualToString:@""]) {
- description = [NSString stringWithFormat:@"%@ %@",
- description, roleDescription];
+ if ([role isEqualToString:NSAccessibilityGroupRole] &&
+ roleDescription != nil &&
+ ![roleDescription isEqualToString:@""]) {
+ [tokens addObject:roleDescription];
}
+
+ // Include the description, title, or value - the first one not empty.
+ id title = [obj title];
+ id description = [obj description];
+ id value = [obj value];
+ if (description && ![description isEqual:@""]) {
+ [tokens addObject:description];
+ } else if (title && ![title isEqual:@""]) {
+ [tokens addObject:title];
+ } else if (value && ![value isEqual:@""]) {
+ [tokens addObject:value];
+ }
+
+ NSString* result = [tokens componentsJoinedByString:@" "];
return scoped_ptr<base::StringValue>(
- new base::StringValue(SysNSStringToUTF16(description))).Pass();
+ new base::StringValue(SysNSStringToUTF16(result))).Pass();
}
scoped_ptr<base::Value> PopulateObject(id value) {
@@ -154,9 +170,11 @@ NSArray* BuildAllAttributesArray() {
NSAccessibilityOrientationAttribute,
@"AXRequired",
NSAccessibilityRowIndexRangeAttribute,
+ NSAccessibilitySelectedChildrenAttribute,
NSAccessibilityTitleUIElementAttribute,
NSAccessibilityURLAttribute,
NSAccessibilityVisibleCharacterRangeAttribute,
+ NSAccessibilityVisibleChildrenAttribute,
@"AXVisited",
@"AXLinkedUIElements",
nil];
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h
index 5fc708f..0f01a7a 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.h
+++ b/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -95,6 +95,7 @@
@property(nonatomic, readonly) NSArray* rowHeaders;
@property(nonatomic, readonly) NSValue* rowIndexRange;
@property(nonatomic, readonly) NSArray* rows;
+@property(nonatomic, readonly) NSArray* selectedChildren;
// The size of this object.
@property(nonatomic, readonly) NSValue* size;
// A string indicating the subrole of this object as far as accessibility
@@ -109,6 +110,7 @@
@property(nonatomic, readonly) NSString* valueDescription;
@property(nonatomic, readonly) NSValue* visibleCharacterRange;
@property(nonatomic, readonly) NSArray* visibleCells;
+@property(nonatomic, readonly) NSArray* visibleChildren;
@property(nonatomic, readonly) NSArray* visibleColumns;
@property(nonatomic, readonly) NSArray* visibleRows;
@property(nonatomic, readonly) NSNumber* visited;
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index 232e482..2ed21f4 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -270,6 +270,7 @@ NSDictionary* attributeToMethodNameMap = nil;
{ NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
{ NSAccessibilityRowsAttribute, @"rows" },
// TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
+ { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
{ NSAccessibilitySizeAttribute, @"size" },
{ NSAccessibilitySubroleAttribute, @"subrole" },
{ NSAccessibilityTabsAttribute, @"tabs" },
@@ -281,6 +282,7 @@ NSDictionary* attributeToMethodNameMap = nil;
{ NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
{ NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
{ NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
+ { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
{ NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
{ NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
{ NSAccessibilityWindowAttribute, @"window" },
@@ -628,6 +630,11 @@ NSDictionary* attributeToMethodNameMap = nil;
if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
return NSAccessibilityVerticalOrientationValue;
+ if ([self internalRole] == ui::AX_ROLE_LIST ||
+ [self internalRole] == ui::AX_ROLE_LIST_BOX) {
+ return NSAccessibilityVerticalOrientationValue;
+ }
+
if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
return NSAccessibilityVerticalOrientationValue;
else
@@ -829,6 +836,40 @@ NSDictionary* attributeToMethodNameMap = nil;
return ret;
}
+- (NSArray*)selectedChildren {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+
+ BrowserAccessibilityManager* manager = browserAccessibility_->manager();
+ BrowserAccessibility* focusedChild =
+ manager->GetFocus(browserAccessibility_);
+ if (focusedChild && focusedChild != browserAccessibility_) {
+ // First try the focused child.
+ [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
+ } else {
+ // Next try the active descendant.
+ int activeDescendantId;
+ if (browserAccessibility_->GetIntAttribute(
+ ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
+ BrowserAccessibility* activeDescendant =
+ manager->GetFromID(activeDescendantId);
+ if (activeDescendant)
+ [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
+ } else {
+ // Otherwise return any children with the "selected" state, which
+ // may come from aria-selected.
+ uint32 childCount = browserAccessibility_->PlatformChildCount();
+ for (uint32 index = 0; index < childCount; ++index) {
+ BrowserAccessibility* child =
+ browserAccessibility_->PlatformGetChild(index);
+ if (child->HasState(ui::AX_STATE_SELECTED))
+ [ret addObject:child->ToBrowserAccessibilityCocoa()];
+ }
+ }
+ }
+
+ return ret;
+}
+
// Returns the size of this object.
- (NSValue*)size {
gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
@@ -1002,6 +1043,18 @@ NSDictionary* attributeToMethodNameMap = nil;
return ret;
}
+- (NSArray*)visibleChildren {
+ NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
+ uint32 childCount = browserAccessibility_->PlatformChildCount();
+ for (uint32 index = 0; index < childCount; ++index) {
+ BrowserAccessibilityCocoa* child =
+ browserAccessibility_->PlatformGetChild(index)->
+ ToBrowserAccessibilityCocoa();
+ [ret addObject:child];
+ }
+ return ret;
+}
+
- (NSArray*)visibleColumns {
return [self columns];
}
@@ -1396,6 +1449,12 @@ NSDictionary* attributeToMethodNameMap = nil;
nil]];
}
}
+ } else if ([role isEqualToString:NSAccessibilityListRole]) {
+ [ret addObjectsFromArray:[NSArray arrayWithObjects:
+ NSAccessibilityOrientationAttribute,
+ NSAccessibilitySelectedChildrenAttribute,
+ NSAccessibilityVisibleChildrenAttribute,
+ nil]];
}
// Add the url attribute only if it has a valid url.
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index d373f7b..543b06c 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -301,6 +301,21 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaList) {
RunTest(FILE_PATH_LITERAL("aria-list.html"));
}
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaListBoxActiveDescendant) {
+ RunTest(FILE_PATH_LITERAL("aria-listbox-activedescendant.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaListBoxAriaSelected) {
+ RunTest(FILE_PATH_LITERAL("aria-listbox-aria-selected.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+ AccessibilityAriaListBoxChildFocus) {
+ RunTest(FILE_PATH_LITERAL("aria-listbox-childfocus.html"));
+}
+
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaMenu) {
RunTest(FILE_PATH_LITERAL("aria-menu.html"));
}
diff --git a/content/test/data/accessibility/aria-labelledby-heading-expected-mac.txt b/content/test/data/accessibility/aria-labelledby-heading-expected-mac.txt
index da6c21d..b09d6d47 100644
--- a/content/test/data/accessibility/aria-labelledby-heading-expected-mac.txt
+++ b/content/test/data/accessibility/aria-labelledby-heading-expected-mac.txt
@@ -1,5 +1,5 @@
AXWebArea
AXGroup
- AXTextField AXTitleUIElement='AXHeading 2'
+ AXTextField AXTitleUIElement='AXHeading h2'
AXHeading AXTitle='h2' AXValue='2'
AXStaticText AXValue='h2'
diff --git a/content/test/data/accessibility/aria-list-expected-mac.txt b/content/test/data/accessibility/aria-list-expected-mac.txt
index 8ae9e03..7beb5ea 100644
--- a/content/test/data/accessibility/aria-list-expected-mac.txt
+++ b/content/test/data/accessibility/aria-list-expected-mac.txt
@@ -1,5 +1,5 @@
AXWebArea
- AXList AXSubrole=AXContentList
+ AXList AXSubrole=AXContentList AXOrientation='AXVerticalOrientation' AXSelectedChildren=[] AXVisibleChildren=["AXGroup group 1","AXGroup group 2","AXGroup group 3"]
AXGroup
AXStaticText AXValue='Item 1'
AXGroup
diff --git a/content/test/data/accessibility/aria-list.html b/content/test/data/accessibility/aria-list.html
index 2e69bee..1aa4cd1 100644
--- a/content/test/data/accessibility/aria-list.html
+++ b/content/test/data/accessibility/aria-list.html
@@ -1,12 +1,15 @@
<!--
@MAC-ALLOW:AXSubrole*
+@MAC-ALLOW:AXOrientation*
+@MAC-ALLOW:AXSelectedChildren*
+@MAC-ALLOW:AXVisibleChildren*
-->
<html>
<body>
<div role="list">
- <div role="listitem">Item 1</div>
- <div role="listitem">Item 2</div>
- <div role="listitem">Item 3</div>
+ <div aria-label="1" role="listitem">Item 1</div>
+ <div aria-label="2" role="listitem">Item 2</div>
+ <div aria-label="3" role="listitem">Item 3</div>
</div>
</body>
</html>
diff --git a/content/test/data/accessibility/aria-listbox-activedescendant-expected-android.txt b/content/test/data/accessibility/aria-listbox-activedescendant-expected-android.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-activedescendant-expected-android.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-activedescendant-expected-mac.txt b/content/test/data/accessibility/aria-listbox-activedescendant-expected-mac.txt
new file mode 100644
index 0000000..21a73f8
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-activedescendant-expected-mac.txt
@@ -0,0 +1,5 @@
+AXWebArea
+ AXList AXOrientation='AXVerticalOrientation' AXSelectedChildren=["AXStaticText 3"] AXVisibleChildren=["AXStaticText 1","AXStaticText 2","AXStaticText 3"]
+ AXStaticText AXTitle='Item 1'
+ AXStaticText AXTitle='Item 2'
+ AXStaticText AXTitle='Item 3'
diff --git a/content/test/data/accessibility/aria-listbox-activedescendant-expected-win.txt b/content/test/data/accessibility/aria-listbox-activedescendant-expected-win.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-activedescendant-expected-win.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-activedescendant.html b/content/test/data/accessibility/aria-listbox-activedescendant.html
new file mode 100644
index 0000000..7a5c230
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-activedescendant.html
@@ -0,0 +1,16 @@
+<!--
+@MAC-ALLOW:AXSubrole*
+@MAC-ALLOW:AXOrientation*
+@MAC-ALLOW:AXSelectedChildren*
+@MAC-ALLOW:AXVisibleChildren*
+-->
+<html>
+<body>
+<div role="listbox" tabIndex="0" aria-activedescendant="3">
+ <div id="1" aria-label="1" role="option">Item 1</div>
+ <div id="2" aria-label="2" role="option">Item 2</div>
+ <div id="3" aria-label="3" role="option">Item 3</div>
+</div>
+<script>document.querySelector('*[role="listbox"]').focus();</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria-listbox-aria-selected-expected-android.txt b/content/test/data/accessibility/aria-listbox-aria-selected-expected-android.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-aria-selected-expected-android.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-aria-selected-expected-mac.txt b/content/test/data/accessibility/aria-listbox-aria-selected-expected-mac.txt
new file mode 100644
index 0000000..c521df9a
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-aria-selected-expected-mac.txt
@@ -0,0 +1,7 @@
+AXWebArea
+ AXList AXOrientation='AXVerticalOrientation' AXSelectedChildren=["AXStaticText 4","AXStaticText 5"] AXVisibleChildren=["AXStaticText 1","AXStaticText 2","AXStaticText 3","AXStaticText 4","AXStaticText 5"]
+ AXStaticText AXTitle='Item 1'
+ AXStaticText AXTitle='Item 2'
+ AXStaticText AXTitle='Item 3'
+ AXStaticText AXTitle='Item 4'
+ AXStaticText AXTitle='Item 5'
diff --git a/content/test/data/accessibility/aria-listbox-aria-selected-expected-win.txt b/content/test/data/accessibility/aria-listbox-aria-selected-expected-win.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-aria-selected-expected-win.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-aria-selected.html b/content/test/data/accessibility/aria-listbox-aria-selected.html
new file mode 100644
index 0000000..50c8037
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-aria-selected.html
@@ -0,0 +1,17 @@
+<!--
+@MAC-ALLOW:AXSubrole*
+@MAC-ALLOW:AXOrientation*
+@MAC-ALLOW:AXSelectedChildren*
+@MAC-ALLOW:AXVisibleChildren*
+-->
+<html>
+<body>
+<div role="listbox">
+ <div tabIndex="-1" aria-label="1" role="option">Item 1</div>
+ <div tabIndex="-1" aria-label="2" role="option">Item 2</div>
+ <div tabIndex="-1" aria-label="3" role="option">Item 3</div>
+ <div tabIndex="-1" aria-selected="true" aria-label="4" role="option">Item 4</div>
+ <div tabIndex="-1" aria-selected="true" aria-label="5" role="option">Item 5</div>
+</div>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria-listbox-childfocus-expected-android.txt b/content/test/data/accessibility/aria-listbox-childfocus-expected-android.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-childfocus-expected-android.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-childfocus-expected-mac.txt b/content/test/data/accessibility/aria-listbox-childfocus-expected-mac.txt
new file mode 100644
index 0000000..81da39e
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-childfocus-expected-mac.txt
@@ -0,0 +1,5 @@
+AXWebArea
+ AXList AXOrientation='AXVerticalOrientation' AXSelectedChildren=["AXStaticText 2"] AXVisibleChildren=["AXStaticText 1","AXStaticText 2","AXStaticText 3"]
+ AXStaticText AXTitle='Item 1'
+ AXStaticText AXTitle='Item 2'
+ AXStaticText AXTitle='Item 3'
diff --git a/content/test/data/accessibility/aria-listbox-childfocus-expected-win.txt b/content/test/data/accessibility/aria-listbox-childfocus-expected-win.txt
new file mode 100644
index 0000000..8620d59
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-childfocus-expected-win.txt
@@ -0,0 +1 @@
+#<skip - need to generate expectation>
diff --git a/content/test/data/accessibility/aria-listbox-childfocus.html b/content/test/data/accessibility/aria-listbox-childfocus.html
new file mode 100644
index 0000000..c05497e
--- /dev/null
+++ b/content/test/data/accessibility/aria-listbox-childfocus.html
@@ -0,0 +1,16 @@
+<!--
+@MAC-ALLOW:AXSubrole*
+@MAC-ALLOW:AXOrientation*
+@MAC-ALLOW:AXSelectedChildren*
+@MAC-ALLOW:AXVisibleChildren*
+-->
+<html>
+<body>
+<div role="listbox">
+ <div tabIndex="-1" aria-label="1" role="option">Item 1</div>
+ <div tabIndex="0" aria-label="2" role="option">Item 2</div>
+ <div tabIndex="-1" aria-label="3" role="option">Item 3</div>
+</div>
+<script>document.querySelector('*[aria-label="2"]').focus();</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria-menu-expected-mac.txt b/content/test/data/accessibility/aria-menu-expected-mac.txt
index 7c1715a..eab78cf 100644
--- a/content/test/data/accessibility/aria-menu-expected-mac.txt
+++ b/content/test/data/accessibility/aria-menu-expected-mac.txt
@@ -1,6 +1,6 @@
AXWebArea
AXMenuBar
- AXMenuItem AXTitle='File' AXLinkedUIElements=["AXMenu"]
+ AXMenuItem AXTitle='File' AXLinkedUIElements=["AXMenu File"]
AXMenuItem AXTitle='Edit'
AXMenuItem AXTitle='View'
AXMenu AXDescription='File'