diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-01 22:58:52 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-01 22:58:52 +0000 |
commit | 111e7e6c5f51a35d39ccf1cff75e676b1b0d8f6b (patch) | |
tree | 0f62236b143d97c2c22696cb238cfc230e5ad545 /content/browser/accessibility | |
parent | 3b00c2d3dc17dfe7927b4884d029a3aa80afb6d3 (diff) | |
download | chromium_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
Diffstat (limited to 'content/browser/accessibility')
4 files changed, 104 insertions, 10 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")); } |