Commit ffc34741 authored by Dominic Mazzoni's avatar Dominic Mazzoni Committed by Commit Bot

Support aria-activedescendant in comboboxes on Mac with VoiceOver.

Mac's accessibility API doesn't have a way for us to expose the
active descendant of a control. For something like a list box or grid,
we just pretend that the active descendant is actually focused and that
basically works.

For a combobox, we need VoiceOver to announce the selected option
while keeping focus on the text box. Fix this by exposing the
AXOwns relationship, which VoiceOver uses to find a list box owned
by a combo box.

Rather than plumbing through aria-owns, instead we just expose the
parent of the activeDescendant target. That way things just work if
the author uses aria-activedescendant correctly even if they forget
aria-owns.

Also implements AXHasPopup to match Safari.

Bug: 829945
Change-Id: I7f670ecb8af1056e14e889d31aa52a9917719964
Reviewed-on: https://chromium-review.googlesource.com/999958
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarNektarios Paisios <nektar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549286}
parent 21b59335
...@@ -69,12 +69,14 @@ NSString* const NSAccessibilityDropEffectsAttribute = @"AXDropEffects"; ...@@ -69,12 +69,14 @@ NSString* const NSAccessibilityDropEffectsAttribute = @"AXDropEffects";
NSString* const NSAccessibilityEditableAncestorAttribute = NSString* const NSAccessibilityEditableAncestorAttribute =
@"AXEditableAncestor"; @"AXEditableAncestor";
NSString* const NSAccessibilityGrabbedAttribute = @"AXGrabbed"; NSString* const NSAccessibilityGrabbedAttribute = @"AXGrabbed";
NSString* const NSAccessibilityHasPopupAttribute = @"AXHasPopup";
NSString* const NSAccessibilityHighestEditableAncestorAttribute = NSString* const NSAccessibilityHighestEditableAncestorAttribute =
@"AXHighestEditableAncestor"; @"AXHighestEditableAncestor";
NSString* const NSAccessibilityInvalidAttribute = @"AXInvalid"; NSString* const NSAccessibilityInvalidAttribute = @"AXInvalid";
NSString* const NSAccessibilityIsMultiSelectableAttribute = NSString* const NSAccessibilityIsMultiSelectableAttribute =
@"AXIsMultiSelectable"; @"AXIsMultiSelectable";
NSString* const NSAccessibilityLoadingProgressAttribute = @"AXLoadingProgress"; NSString* const NSAccessibilityLoadingProgressAttribute = @"AXLoadingProgress";
NSString* const NSAccessibilityOwnsAttribute = @"AXOwns";
NSString* const NSString* const
NSAccessibilityUIElementCountForSearchPredicateParameterizedAttribute = NSAccessibilityUIElementCountForSearchPredicateParameterizedAttribute =
@"AXUIElementCountForSearchPredicate"; @"AXUIElementCountForSearchPredicate";
...@@ -583,6 +585,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -583,6 +585,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
{NSAccessibilityFocusedAttribute, @"focused"}, {NSAccessibilityFocusedAttribute, @"focused"},
{NSAccessibilityGrabbedAttribute, @"grabbed"}, {NSAccessibilityGrabbedAttribute, @"grabbed"},
{NSAccessibilityHeaderAttribute, @"header"}, {NSAccessibilityHeaderAttribute, @"header"},
{NSAccessibilityHasPopupAttribute, @"hasPopup"},
{NSAccessibilityHelpAttribute, @"help"}, {NSAccessibilityHelpAttribute, @"help"},
{NSAccessibilityHighestEditableAncestorAttribute, {NSAccessibilityHighestEditableAncestorAttribute,
@"highestEditableAncestor"}, @"highestEditableAncestor"},
...@@ -598,6 +601,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -598,6 +601,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
{NSAccessibilityMinValueAttribute, @"minValue"}, {NSAccessibilityMinValueAttribute, @"minValue"},
{NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters"}, {NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters"},
{NSAccessibilityOrientationAttribute, @"orientation"}, {NSAccessibilityOrientationAttribute, @"orientation"},
{NSAccessibilityOwnsAttribute, @"owns"},
{NSAccessibilityParentAttribute, @"parent"}, {NSAccessibilityParentAttribute, @"parent"},
{NSAccessibilityPlaceholderValueAttribute, @"placeholderValue"}, {NSAccessibilityPlaceholderValueAttribute, @"placeholderValue"},
{NSAccessibilityPositionAttribute, @"position"}, {NSAccessibilityPositionAttribute, @"position"},
...@@ -1108,6 +1112,13 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -1108,6 +1112,13 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
return [NSNumber numberWithBool:NO]; return [NSNumber numberWithBool:NO];
} }
- (NSNumber*)hasPopup {
if (![self instanceActive])
return nil;
return browserAccessibility_->HasState(ax::mojom::State::kHaspopup) ? @YES
: @NO;
}
- (id)header { - (id)header {
if (![self instanceActive]) if (![self instanceActive])
return nil; return nil;
...@@ -1360,6 +1371,40 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -1360,6 +1371,40 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
return @""; return @"";
} }
- (id)owns {
if (![self instanceActive])
return nil;
//
// If the active descendant points to an element in a container with
// selectable children, add the "owns" relationship to point to that
// container. That's the only way activeDescendant is actually
// supported with VoiceOver.
//
int activeDescendantId;
if (!browserAccessibility_->GetIntAttribute(
ax::mojom::IntAttribute::kActivedescendantId, &activeDescendantId))
return nil;
BrowserAccessibilityManager* manager = browserAccessibility_->manager();
BrowserAccessibility* activeDescendant =
manager->GetFromID(activeDescendantId);
if (!activeDescendant)
return nil;
BrowserAccessibility* container = activeDescendant->PlatformGetParent();
while (container &&
!ui::IsContainerWithSelectableChildrenRole(container->GetRole()))
container = container->PlatformGetParent();
if (!container)
return nil;
NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
[ret addObject:ToBrowserAccessibilityCocoa(container)];
return ret;
}
- (NSNumber*)numberOfCharacters { - (NSNumber*)numberOfCharacters {
if (![self instanceActive]) if (![self instanceActive])
return nil; return nil;
...@@ -2984,6 +3029,10 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -2984,6 +3029,10 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
[ret addObjectsFromArray:@[ NSAccessibilityGrabbedAttribute ]]; [ret addObjectsFromArray:@[ NSAccessibilityGrabbedAttribute ]];
} }
if (browserAccessibility_->HasState(ax::mojom::State::kHaspopup)) {
[ret addObjectsFromArray:@[ NSAccessibilityHasPopupAttribute ]];
}
// Add expanded attribute only if it has expanded or collapsed state. // Add expanded attribute only if it has expanded or collapsed state.
if (GetState(browserAccessibility_, ax::mojom::State::kExpanded) || if (GetState(browserAccessibility_, ax::mojom::State::kExpanded) ||
GetState(browserAccessibility_, ax::mojom::State::kCollapsed)) { GetState(browserAccessibility_, ax::mojom::State::kCollapsed)) {
...@@ -3009,6 +3058,12 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ...@@ -3009,6 +3058,12 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired";
[ret addObjectsFromArray:@[ NSAccessibilityLanguageAttribute ]]; [ret addObjectsFromArray:@[ NSAccessibilityLanguageAttribute ]];
} }
if ([self internalRole] == ax::mojom::Role::kTextFieldWithComboBox) {
[ret addObjectsFromArray:@[
NSAccessibilityOwnsAttribute,
]];
}
// Title UI Element. // Title UI Element.
if (browserAccessibility_->HasIntListAttribute( if (browserAccessibility_->HasIntListAttribute(
ax::mojom::IntListAttribute::kLabelledbyIds) && ax::mojom::IntListAttribute::kLabelledbyIds) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment