Bug 1625192 - Support select element and its dropdown menu. r=morgan

Differential Revision: https://phabricator.services.mozilla.com/D75135
This commit is contained in:
Eitan Isaacson 2020-05-15 21:31:08 +00:00
Родитель b0b8bbe697
Коммит 96db37abd6
5 изменённых файлов: 196 добавлений и 5 удалений

Просмотреть файл

@ -172,6 +172,8 @@ Class a11y::GetTypeFromRole(roles::Role aRole) {
switch (aRole) {
case roles::COMBOBOX:
return [mozPopupButtonAccessible class];
case roles::PUSHBUTTON:
return [mozButtonAccessible class];
@ -214,6 +216,18 @@ Class a11y::GetTypeFromRole(roles::Role aRole) {
return [mozOptionAccessible class];
}
case roles::COMBOBOX_LIST:
case roles::MENUBAR:
case roles::MENUPOPUP: {
return [mozMenuAccessible class];
}
case roles::COMBOBOX_OPTION:
case roles::PARENT_MENUITEM:
case roles::MENUITEM: {
return [mozMenuItemAccessible class];
}
default:
return [mozAccessible class];
}

Просмотреть файл

@ -13,6 +13,9 @@
- (BOOL)hasPopup;
@end
@interface mozPopupButtonAccessible : mozButtonAccessible
@end
@interface mozCheckboxAccessible : mozButtonAccessible
// returns one of the constants defined in CheckboxValue
- (int)isChecked;

Просмотреть файл

@ -58,11 +58,6 @@ enum CheckboxValue {
- (id)accessibilityAttributeValue:(NSString*)attribute {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
if ([self hasPopup]) return [self children];
return nil;
}
if ([attribute isEqualToString:NSAccessibilityHasPopupAttribute]) {
return [NSNumber numberWithBool:[self hasPopup]];
}
@ -86,6 +81,80 @@ enum CheckboxValue {
@end
@implementation mozPopupButtonAccessible
- (NSString*)title {
// Popup buttons don't have titles.
return @"";
}
- (NSArray*)accessibilityAttributeNames {
static NSMutableArray* supportedAttributes = nil;
if (!supportedAttributes) {
supportedAttributes = [[super accessibilityAttributeNames] mutableCopy];
// We need to remove AXHasPopup from a AXPopupButton for it to be reported
// as a popup button. Otherwise VO reports it as a button that has a popup
// which is not consistent with Safari.
[supportedAttributes removeObject:NSAccessibilityHasPopupAttribute];
[supportedAttributes addObject:NSAccessibilityValueAttribute];
}
return supportedAttributes;
}
- (id)accessibilityAttributeValue:(NSString*)attribute {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if ([attribute isEqualToString:NSAccessibilityHasPopupAttribute]) {
// We need to null on AXHasPopup for it to be reported as a popup button.
// Otherwise VO reports it as a button that has a popup which is not
// consistent with Safari.
return nil;
}
return [super accessibilityAttributeValue:attribute];
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (NSArray*)children {
if ([self stateWithMask:states::EXPANDED] == 0) {
// If the popup button is collapsed don't return its children.
return @[];
}
return [super children];
}
- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
[super stateChanged:state isEnabled:enabled];
if (state == states::EXPANDED) {
// If the EXPANDED state is updated, fire AXMenu events on the
// popups child which is the actual menu.
if (mozAccessible* popup = (mozAccessible*)[self childAt:0]) {
[popup postNotification:(enabled ? @"AXMenuOpened" : @"AXMenuClosed")];
}
}
}
- (BOOL)ignoreWithParent:(mozAccessible*)parent {
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
if (accWrap->IsContent() && accWrap->GetContent()->IsXULElement(nsGkAtoms::menulist)) {
// The way select elements work is that content has a COMBOBOX accessible, when it is clicked
// it expands a COMBOBOX in our top-level main XUL window. The latter COMBOBOX is a stand-in
// for the content one while it is expanded.
// XXX: VO focus behaves weirdly if we expose the dummy XUL COMBOBOX in the tree.
// We are only interested in its menu child.
return YES;
}
}
return [super ignoreWithParent:parent];
}
@end
@implementation mozCheckboxAccessible
- (NSString*)accessibilityActionDescription:(NSString*)action {

Просмотреть файл

@ -26,3 +26,11 @@
@interface mozOptionAccessible : mozSelectableChildAccessible
@end
@interface mozMenuAccessible : mozSelectableAccessible {
BOOL mIsOpened;
}
@end
@interface mozMenuItemAccessible : mozSelectableChildAccessible
@end

Просмотреть файл

@ -250,3 +250,100 @@
}
@end
@implementation mozMenuAccessible
- (NSString*)title {
return @"";
}
- (NSString*)accessibilityLabel {
return @"";
}
- (void)postNotification:(NSString*)notification {
[super postNotification:notification];
if ([notification isEqualToString:@"AXMenuOpened"]) {
mIsOpened = YES;
} else if ([notification isEqualToString:@"AXMenuClosed"]) {
mIsOpened = NO;
}
}
- (void)expire {
if (mIsOpened) {
// VO needs to receive a menu closed event when the menu goes away.
// If the menu is being destroyed, send a menu closed event first.
[self postNotification:@"AXMenuClosed"];
}
[super expire];
}
@end
@implementation mozMenuItemAccessible
- (NSString*)accessibilityLabel {
return @"";
}
- (NSArray*)accessibilityAttributeNames {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
static NSMutableArray* attributes = nil;
if (!attributes) {
attributes = [[super accessibilityAttributeNames] mutableCopy];
[attributes addObject:@"AXMenuItemMarkChar"];
}
return attributes;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (id)accessibilityAttributeValue:(NSString*)attribute {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if ([attribute isEqualToString:@"AXMenuItemMarkChar"]) {
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (accWrap && accWrap->IsContent() && accWrap->GetContent()->IsXULElement(nsGkAtoms::menuitem)) {
// We need to provide a marker character. This is the visible "√" you see
// on dropdown menus. In our a11y tree this is a single child text node
// of the menu item.
// We do this only with XUL menuitems that conform to the native theme, and not
// with aria menu items that might have a pseudo element or something.
if (accWrap->ChildCount() == 1 && accWrap->FirstChild()->Role() == roles::STATICTEXT) {
nsAutoString marker;
accWrap->FirstChild()->Name(marker);
if (marker.Length() == 1) {
return nsCocoaUtils::ToNSString(marker);
}
}
}
return nil;
}
return [super accessibilityAttributeValue:attribute];
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (BOOL)isSelected {
// Our focused state is equivelent to native selected states for menus.
return [self stateWithMask:states::FOCUSED] != 0;
}
- (void)handleAccessibleEvent:(uint32_t)eventType {
switch (eventType) {
case nsIAccessibleEvent::EVENT_FOCUS:
// Our focused state is equivelent to native selected states for menus.
mozAccessible* parent = (mozAccessible*)[self parent];
[parent postNotification:NSAccessibilitySelectedChildrenChangedNotification];
break;
}
[super handleAccessibleEvent:eventType];
}
@end