Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(535)

Unified Diff: ui/accessibility/platform/ax_platform_node_mac.mm

Issue 2944083004: MacViews a11y: Support the "Show menu" action in Textfield and Combobox. (Closed)
Patch Set: more interesting test Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/accessibility/platform/ax_platform_node_mac.mm
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 62936942cb6eb03e7bf5a5d08d545c1d183d1ac8..5179d73a4f0f0a71703da8bd86c2c2469ecac68a 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -20,21 +20,12 @@
namespace {
-struct RoleMapEntry {
- ui::AXRole value;
- NSString* nativeValue;
-};
-
-struct EventMapEntry {
- ui::AXEvent value;
- NSString* nativeValue;
-};
-
-typedef std::map<ui::AXRole, NSString*> RoleMap;
-typedef std::map<ui::AXEvent, NSString*> EventMap;
+using RoleMap = std::map<ui::AXRole, NSString*>;
+using EventMap = std::map<ui::AXEvent, NSString*>;
+using ActionList = std::vector<std::pair<ui::AXAction, NSString*>>;
RoleMap BuildRoleMap() {
- const RoleMapEntry roles[] = {
+ const RoleMap::value_type roles[] = {
{ui::AX_ROLE_ABBR, NSAccessibilityGroupRole},
{ui::AX_ROLE_ALERT, NSAccessibilityGroupRole},
{ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole},
@@ -157,14 +148,11 @@ RoleMap BuildRoleMap() {
// { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
};
- RoleMap role_map;
- for (size_t i = 0; i < arraysize(roles); ++i)
- role_map[roles[i].value] = roles[i].nativeValue;
- return role_map;
+ return RoleMap(begin(roles), end(roles));
}
RoleMap BuildSubroleMap() {
- const RoleMapEntry subroles[] = {
+ const RoleMap::value_type subroles[] = {
{ui::AX_ROLE_ALERT, @"AXApplicationAlert"},
{ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog"},
{ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication"},
@@ -198,14 +186,11 @@ RoleMap BuildSubroleMap() {
{ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole},
};
- RoleMap subrole_map;
- for (size_t i = 0; i < arraysize(subroles); ++i)
- subrole_map[subroles[i].value] = subroles[i].nativeValue;
- return subrole_map;
+ return RoleMap(begin(subroles), end(subroles));
}
EventMap BuildEventMap() {
- const EventMapEntry events[] = {
+ const EventMap::value_type events[] = {
{ui::AX_EVENT_FOCUS, NSAccessibilityFocusedUIElementChangedNotification},
{ui::AX_EVENT_TEXT_CHANGED, NSAccessibilityTitleChangedNotification},
{ui::AX_EVENT_VALUE_CHANGED, NSAccessibilityValueChangedNotification},
@@ -214,10 +199,24 @@ EventMap BuildEventMap() {
// TODO(patricialor): Add more events.
};
- EventMap event_map;
- for (size_t i = 0; i < arraysize(events); ++i)
- event_map[events[i].value] = events[i].nativeValue;
- return event_map;
+ return EventMap(begin(events), end(events));
+}
+
+ActionList BuildActionList() {
+ const ActionList::value_type entries[] = {
+ // NSAccessibilityPressAction must come first in this list.
+ {ui::AX_ACTION_DO_DEFAULT, NSAccessibilityPressAction},
+
+ {ui::AX_ACTION_DECREMENT, NSAccessibilityDecrementAction},
+ {ui::AX_ACTION_INCREMENT, NSAccessibilityIncrementAction},
+ {ui::AX_ACTION_SHOW_CONTEXT_MENU, NSAccessibilityShowMenuAction},
+ };
+ return ActionList(begin(entries), end(entries));
+}
+
+const ActionList& GetActionList() {
+ CR_DEFINE_STATIC_LOCAL(const ActionList, action_map, (BuildActionList()));
+ return action_map;
}
void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) {
@@ -225,6 +224,20 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) {
target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]);
}
+// Returns true if |action| should be added implicitly for |data|.
+bool HasImplicitAction(const ui::AXNodeData& data, ui::AXAction action) {
+ return action == ui::AX_ACTION_DO_DEFAULT && ui::IsRoleClickable(data.role);
+}
+
+// For roles that show a menu for the default action, ensure "show menu" also
+// appears in available actions, but only if that's not already used for a
+// context menu. It will be mapped back to the default action when performed.
+bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) {
+ return HasImplicitAction(data, ui::AX_ACTION_DO_DEFAULT) &&
+ !data.HasAction(ui::AX_ACTION_SHOW_CONTEXT_MENU) &&
+ data.role == ui::AX_ROLE_POP_UP_BUTTON;
+}
+
} // namespace
@interface AXPlatformNodeCocoa ()
@@ -240,24 +253,21 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) {
@synthesize node = node_;
-// A mapping of AX roles to native roles.
+ (NSString*)nativeRoleFromAXRole:(ui::AXRole)role {
- CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap()));
- RoleMap::iterator it = role_map.find(role);
+ CR_DEFINE_STATIC_LOCAL(const RoleMap, role_map, (BuildRoleMap()));
+ RoleMap::const_iterator it = role_map.find(role);
return it != role_map.end() ? it->second : NSAccessibilityUnknownRole;
}
-// A mapping of AX roles to native subroles.
+ (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role {
- CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap()));
- RoleMap::iterator it = subrole_map.find(role);
+ CR_DEFINE_STATIC_LOCAL(const RoleMap, subrole_map, (BuildSubroleMap()));
+ RoleMap::const_iterator it = subrole_map.find(role);
return it != subrole_map.end() ? it->second : nil;
}
-// A mapping of AX events to native notifications.
+ (NSString*)nativeNotificationFromAXEvent:(ui::AXEvent)event {
- CR_DEFINE_STATIC_LOCAL(EventMap, event_map, (BuildEventMap()));
- EventMap::iterator it = event_map.find(event);
+ CR_DEFINE_STATIC_LOCAL(const EventMap, event_map, (BuildEventMap()));
+ EventMap::const_iterator it = event_map.find(event);
return it != event_map.end() ? it->second : nil;
}
@@ -323,9 +333,19 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) {
base::scoped_nsobject<NSMutableArray> axActions(
[[NSMutableArray alloc] init]);
- // VoiceOver expects the "press" action to be first.
- if (ui::IsRoleClickable(node_->GetData().role))
- [axActions addObject:NSAccessibilityPressAction];
+ const ui::AXNodeData& data = node_->GetData();
+ const ActionList& action_list = GetActionList();
+
+ // VoiceOver expects the "press" action to be first. Note that some roles
+ // should be given a press action implicitly.
+ DCHECK([action_list[0].second isEqualToString:NSAccessibilityPressAction]);
+ for (const auto item : action_list) {
+ if (data.HasAction(item.first) || HasImplicitAction(data, item.first))
+ [axActions addObject:item.second];
+ }
+
+ if (AlsoUseShowMenuActionForDefaultAction(data))
+ [axActions addObject:NSAccessibilityShowMenuAction];
return axActions.autorelease();
}
@@ -333,8 +353,17 @@ void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) {
- (void)accessibilityPerformAction:(NSString*)action {
DCHECK([[self accessibilityActionNames] containsObject:action]);
ui::AXActionData data;
- if ([action isEqualToString:NSAccessibilityPressAction])
+ if ([action isEqualToString:NSAccessibilityShowMenuAction] &&
+ AlsoUseShowMenuActionForDefaultAction(node_->GetData())) {
data.action = ui::AX_ACTION_DO_DEFAULT;
+ } else {
+ for (const ActionList::value_type& entry : GetActionList()) {
+ if ([action isEqualToString:entry.second]) {
+ data.action = entry.first;
+ break;
+ }
+ }
+ }
// Note ui::AX_ACTIONs which are just overwriting an accessibility attribute
// are already implemented in -accessibilitySetValue:forAttribute:, so ignore
« no previous file with comments | « ui/accessibility/platform/ax_platform_node_mac.h ('k') | ui/views/accessibility/native_view_accessibility_base.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698