Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ui/accessibility/platform/ax_platform_node_mac.h" | 5 #import "ui/accessibility/platform/ax_platform_node_mac.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include "base/macros.h" | 10 #include "base/macros.h" |
| 11 #include "base/strings/sys_string_conversions.h" | 11 #include "base/strings/sys_string_conversions.h" |
| 12 #include "ui/accessibility/ax_action_data.h" | 12 #include "ui/accessibility/ax_action_data.h" |
| 13 #include "ui/accessibility/ax_node_data.h" | 13 #include "ui/accessibility/ax_node_data.h" |
| 14 #include "ui/accessibility/ax_role_properties.h" | 14 #include "ui/accessibility/ax_role_properties.h" |
| 15 #include "ui/accessibility/platform/ax_platform_node.h" | 15 #include "ui/accessibility/platform/ax_platform_node.h" |
| 16 #include "ui/accessibility/platform/ax_platform_node_delegate.h" | 16 #include "ui/accessibility/platform/ax_platform_node_delegate.h" |
| 17 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
| 18 #import "ui/gfx/mac/coordinate_conversion.h" | 18 #import "ui/gfx/mac/coordinate_conversion.h" |
| 19 #include "ui/strings/grit/ui_strings.h" | 19 #include "ui/strings/grit/ui_strings.h" |
| 20 | 20 |
| 21 namespace { | 21 namespace { |
| 22 | 22 |
| 23 struct RoleMapEntry { | 23 using RoleMap = std::map<ui::AXRole, NSString*>; |
| 24 ui::AXRole value; | 24 using EventMap = std::map<ui::AXEvent, NSString*>; |
| 25 NSString* nativeValue; | 25 using ActionMap = std::map<ui::AXAction, NSString*>; |
| 26 }; | |
| 27 | 26 |
| 28 struct EventMapEntry { | 27 // The AXAction for NSAccessibilityPressAction, which needs special handling. |
| 29 ui::AXEvent value; | 28 constexpr ui::AXAction kActionForPress = ui::AX_ACTION_DO_DEFAULT; |
| 30 NSString* nativeValue; | |
| 31 }; | |
| 32 | |
| 33 typedef std::map<ui::AXRole, NSString*> RoleMap; | |
| 34 typedef std::map<ui::AXEvent, NSString*> EventMap; | |
| 35 | 29 |
| 36 RoleMap BuildRoleMap() { | 30 RoleMap BuildRoleMap() { |
| 37 const RoleMapEntry roles[] = { | 31 const RoleMap::value_type roles[] = { |
| 38 {ui::AX_ROLE_ABBR, NSAccessibilityGroupRole}, | 32 {ui::AX_ROLE_ABBR, NSAccessibilityGroupRole}, |
| 39 {ui::AX_ROLE_ALERT, NSAccessibilityGroupRole}, | 33 {ui::AX_ROLE_ALERT, NSAccessibilityGroupRole}, |
| 40 {ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole}, | 34 {ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole}, |
| 41 {ui::AX_ROLE_ANCHOR, NSAccessibilityGroupRole}, | 35 {ui::AX_ROLE_ANCHOR, NSAccessibilityGroupRole}, |
| 42 {ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole}, | 36 {ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole}, |
| 43 {ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole}, | 37 {ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole}, |
| 44 {ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole}, | 38 {ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole}, |
| 45 {ui::AX_ROLE_AUDIO, NSAccessibilityGroupRole}, | 39 {ui::AX_ROLE_AUDIO, NSAccessibilityGroupRole}, |
| 46 {ui::AX_ROLE_BANNER, NSAccessibilityGroupRole}, | 40 {ui::AX_ROLE_BANNER, NSAccessibilityGroupRole}, |
| 47 {ui::AX_ROLE_BLOCKQUOTE, NSAccessibilityGroupRole}, | 41 {ui::AX_ROLE_BLOCKQUOTE, NSAccessibilityGroupRole}, |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole}, | 144 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole}, |
| 151 {ui::AX_ROLE_VIDEO, NSAccessibilityGroupRole}, | 145 {ui::AX_ROLE_VIDEO, NSAccessibilityGroupRole}, |
| 152 {ui::AX_ROLE_WEB_AREA, @"AXWebArea"}, | 146 {ui::AX_ROLE_WEB_AREA, @"AXWebArea"}, |
| 153 {ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole}, | 147 {ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole}, |
| 154 | 148 |
| 155 // TODO(dtseng): we don't correctly support the attributes for these | 149 // TODO(dtseng): we don't correctly support the attributes for these |
| 156 // roles. | 150 // roles. |
| 157 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole }, | 151 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole }, |
| 158 }; | 152 }; |
| 159 | 153 |
| 160 RoleMap role_map; | 154 return RoleMap(begin(roles), end(roles)); |
| 161 for (size_t i = 0; i < arraysize(roles); ++i) | |
| 162 role_map[roles[i].value] = roles[i].nativeValue; | |
| 163 return role_map; | |
| 164 } | 155 } |
| 165 | 156 |
| 166 RoleMap BuildSubroleMap() { | 157 RoleMap BuildSubroleMap() { |
| 167 const RoleMapEntry subroles[] = { | 158 const RoleMap::value_type subroles[] = { |
| 168 {ui::AX_ROLE_ALERT, @"AXApplicationAlert"}, | 159 {ui::AX_ROLE_ALERT, @"AXApplicationAlert"}, |
| 169 {ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog"}, | 160 {ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog"}, |
| 170 {ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication"}, | 161 {ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication"}, |
| 171 {ui::AX_ROLE_ARTICLE, @"AXDocumentArticle"}, | 162 {ui::AX_ROLE_ARTICLE, @"AXDocumentArticle"}, |
| 172 {ui::AX_ROLE_BANNER, @"AXLandmarkBanner"}, | 163 {ui::AX_ROLE_BANNER, @"AXLandmarkBanner"}, |
| 173 {ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary"}, | 164 {ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary"}, |
| 174 {ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo"}, | 165 {ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo"}, |
| 175 {ui::AX_ROLE_DEFINITION, @"AXDefinition"}, | 166 {ui::AX_ROLE_DEFINITION, @"AXDefinition"}, |
| 176 {ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDefinition"}, | 167 {ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDefinition"}, |
| 177 {ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm"}, | 168 {ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm"}, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 191 {ui::AX_ROLE_STATUS, @"AXApplicationStatus"}, | 182 {ui::AX_ROLE_STATUS, @"AXApplicationStatus"}, |
| 192 {ui::AX_ROLE_SWITCH, @"AXSwitch"}, | 183 {ui::AX_ROLE_SWITCH, @"AXSwitch"}, |
| 193 {ui::AX_ROLE_TAB_PANEL, @"AXTabPanel"}, | 184 {ui::AX_ROLE_TAB_PANEL, @"AXTabPanel"}, |
| 194 {ui::AX_ROLE_TERM, @"AXTerm"}, | 185 {ui::AX_ROLE_TERM, @"AXTerm"}, |
| 195 {ui::AX_ROLE_TIMER, @"AXApplicationTimer"}, | 186 {ui::AX_ROLE_TIMER, @"AXApplicationTimer"}, |
| 196 {ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton"}, | 187 {ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton"}, |
| 197 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"}, | 188 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"}, |
| 198 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole}, | 189 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole}, |
| 199 }; | 190 }; |
| 200 | 191 |
| 201 RoleMap subrole_map; | 192 return RoleMap(begin(subroles), end(subroles)); |
| 202 for (size_t i = 0; i < arraysize(subroles); ++i) | |
| 203 subrole_map[subroles[i].value] = subroles[i].nativeValue; | |
| 204 return subrole_map; | |
| 205 } | 193 } |
| 206 | 194 |
| 207 EventMap BuildEventMap() { | 195 EventMap BuildEventMap() { |
| 208 const EventMapEntry events[] = { | 196 const EventMap::value_type events[] = { |
| 209 {ui::AX_EVENT_FOCUS, NSAccessibilityFocusedUIElementChangedNotification}, | 197 {ui::AX_EVENT_FOCUS, NSAccessibilityFocusedUIElementChangedNotification}, |
| 210 {ui::AX_EVENT_TEXT_CHANGED, NSAccessibilityTitleChangedNotification}, | 198 {ui::AX_EVENT_TEXT_CHANGED, NSAccessibilityTitleChangedNotification}, |
| 211 {ui::AX_EVENT_VALUE_CHANGED, NSAccessibilityValueChangedNotification}, | 199 {ui::AX_EVENT_VALUE_CHANGED, NSAccessibilityValueChangedNotification}, |
| 212 {ui::AX_EVENT_TEXT_SELECTION_CHANGED, | 200 {ui::AX_EVENT_TEXT_SELECTION_CHANGED, |
| 213 NSAccessibilitySelectedTextChangedNotification}, | 201 NSAccessibilitySelectedTextChangedNotification}, |
| 214 // TODO(patricialor): Add more events. | 202 // TODO(patricialor): Add more events. |
| 215 }; | 203 }; |
| 216 | 204 |
| 217 EventMap event_map; | 205 return EventMap(begin(events), end(events)); |
| 218 for (size_t i = 0; i < arraysize(events); ++i) | 206 } |
| 219 event_map[events[i].value] = events[i].nativeValue; | 207 |
| 220 return event_map; | 208 ActionMap BuildActionMap() { |
| 209 const ActionMap::value_type entries[] = { | |
| 210 {ui::AX_ACTION_DECREMENT, NSAccessibilityDecrementAction}, | |
| 211 {kActionForPress, NSAccessibilityPressAction}, | |
| 212 {ui::AX_ACTION_INCREMENT, NSAccessibilityIncrementAction}, | |
| 213 {ui::AX_ACTION_SHOW_CONTEXT_MENU, NSAccessibilityShowMenuAction}, | |
| 214 }; | |
| 215 return ActionMap(begin(entries), end(entries)); | |
| 216 } | |
| 217 | |
| 218 const ActionMap& GetActionMap() { | |
| 219 CR_DEFINE_STATIC_LOCAL(const ActionMap, action_map, (BuildActionMap())); | |
| 220 return action_map; | |
| 221 } | 221 } |
| 222 | 222 |
| 223 void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { | 223 void NotifyMacEvent(AXPlatformNodeCocoa* target, ui::AXEvent event_type) { |
| 224 NSAccessibilityPostNotification( | 224 NSAccessibilityPostNotification( |
| 225 target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]); | 225 target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]); |
| 226 } | 226 } |
| 227 | 227 |
| 228 } // namespace | 228 } // namespace |
| 229 | 229 |
| 230 @interface AXPlatformNodeCocoa () | 230 @interface AXPlatformNodeCocoa () |
| 231 // Helper function for string attributes that don't require extra processing. | 231 // Helper function for string attributes that don't require extra processing. |
| 232 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute; | 232 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute; |
| 233 // Returns AXValue, or nil if AXValue isn't an NSString. | 233 // Returns AXValue, or nil if AXValue isn't an NSString. |
| 234 - (NSString*)getAXValueAsString; | 234 - (NSString*)getAXValueAsString; |
| 235 @end | 235 @end |
| 236 | 236 |
| 237 @implementation AXPlatformNodeCocoa { | 237 @implementation AXPlatformNodeCocoa { |
| 238 ui::AXPlatformNodeBase* node_; // Weak. Retains us. | 238 ui::AXPlatformNodeBase* node_; // Weak. Retains us. |
| 239 } | 239 } |
| 240 | 240 |
| 241 @synthesize node = node_; | 241 @synthesize node = node_; |
| 242 | 242 |
| 243 // A mapping of AX roles to native roles. | |
| 244 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role { | 243 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role { |
| 245 CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap())); | 244 CR_DEFINE_STATIC_LOCAL(const RoleMap, role_map, (BuildRoleMap())); |
| 246 RoleMap::iterator it = role_map.find(role); | 245 RoleMap::const_iterator it = role_map.find(role); |
| 247 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole; | 246 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole; |
| 248 } | 247 } |
| 249 | 248 |
| 250 // A mapping of AX roles to native subroles. | |
| 251 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role { | 249 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role { |
| 252 CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap())); | 250 CR_DEFINE_STATIC_LOCAL(const RoleMap, subrole_map, (BuildSubroleMap())); |
| 253 RoleMap::iterator it = subrole_map.find(role); | 251 RoleMap::const_iterator it = subrole_map.find(role); |
| 254 return it != subrole_map.end() ? it->second : nil; | 252 return it != subrole_map.end() ? it->second : nil; |
| 255 } | 253 } |
| 256 | 254 |
| 257 // A mapping of AX events to native notifications. | |
| 258 + (NSString*)nativeNotificationFromAXEvent:(ui::AXEvent)event { | 255 + (NSString*)nativeNotificationFromAXEvent:(ui::AXEvent)event { |
| 259 CR_DEFINE_STATIC_LOCAL(EventMap, event_map, (BuildEventMap())); | 256 CR_DEFINE_STATIC_LOCAL(const EventMap, event_map, (BuildEventMap())); |
| 260 EventMap::iterator it = event_map.find(event); | 257 EventMap::const_iterator it = event_map.find(event); |
| 261 return it != event_map.end() ? it->second : nil; | 258 return it != event_map.end() ? it->second : nil; |
| 262 } | 259 } |
| 263 | 260 |
| 264 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node { | 261 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node { |
| 265 if ((self = [super init])) { | 262 if ((self = [super init])) { |
| 266 node_ = node; | 263 node_ = node; |
| 267 } | 264 } |
| 268 return self; | 265 return self; |
| 269 } | 266 } |
| 270 | 267 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 316 } | 313 } |
| 317 | 314 |
| 318 - (id)accessibilityFocusedUIElement { | 315 - (id)accessibilityFocusedUIElement { |
| 319 return node_->GetDelegate()->GetFocus(); | 316 return node_->GetDelegate()->GetFocus(); |
| 320 } | 317 } |
| 321 | 318 |
| 322 - (NSArray*)accessibilityActionNames { | 319 - (NSArray*)accessibilityActionNames { |
| 323 base::scoped_nsobject<NSMutableArray> axActions( | 320 base::scoped_nsobject<NSMutableArray> axActions( |
| 324 [[NSMutableArray alloc] init]); | 321 [[NSMutableArray alloc] init]); |
| 325 | 322 |
| 323 const ui::AXNodeData& data = node_->GetData(); | |
| 324 | |
| 326 // VoiceOver expects the "press" action to be first. | 325 // VoiceOver expects the "press" action to be first. |
| 327 if (ui::IsRoleClickable(node_->GetData().role)) | 326 if (ui::IsRoleClickable(data.role) || data.HasAction(kActionForPress)) |
| 328 [axActions addObject:NSAccessibilityPressAction]; | 327 [axActions addObject:NSAccessibilityPressAction]; |
| 329 | 328 |
| 329 for (const ActionMap::value_type& entry : GetActionMap()) { | |
| 330 if (entry.first == kActionForPress) | |
| 331 continue; | |
| 332 | |
| 333 DCHECK(![entry.second isEqualToString:NSAccessibilityPressAction]); | |
| 334 | |
| 335 if (data.HasAction(entry.first)) | |
| 336 [axActions addObject:entry.second]; | |
| 337 } | |
| 338 | |
| 330 return axActions.autorelease(); | 339 return axActions.autorelease(); |
| 331 } | 340 } |
| 332 | 341 |
| 333 - (void)accessibilityPerformAction:(NSString*)action { | 342 - (void)accessibilityPerformAction:(NSString*)action { |
| 334 DCHECK([[self accessibilityActionNames] containsObject:action]); | 343 DCHECK([[self accessibilityActionNames] containsObject:action]); |
| 335 ui::AXActionData data; | 344 ui::AXActionData data; |
| 336 if ([action isEqualToString:NSAccessibilityPressAction]) | 345 for (const ActionMap::value_type& entry : GetActionMap()) { |
|
dmazzoni
2017/06/21 06:09:35
It doesn't look like you're ever using ActionMap a
tapted
2017/06/21 06:52:55
Indeed! good catch. and it's as easy as changing t
tapted
2017/06/21 11:13:11
Well.. everything _compiled_ fine with just the ty
| |
| 337 data.action = ui::AX_ACTION_DO_DEFAULT; | 346 if ([action isEqualToString:entry.second]) { |
| 347 data.action = entry.first; | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 338 | 351 |
| 339 // Note ui::AX_ACTIONs which are just overwriting an accessibility attribute | 352 // Note ui::AX_ACTIONs which are just overwriting an accessibility attribute |
| 340 // are already implemented in -accessibilitySetValue:forAttribute:, so ignore | 353 // are already implemented in -accessibilitySetValue:forAttribute:, so ignore |
| 341 // those here. | 354 // those here. |
| 342 | 355 |
| 343 if (data.action != ui::AX_ACTION_NONE) | 356 if (data.action != ui::AX_ACTION_NONE) |
| 344 node_->GetDelegate()->AccessibilityPerformAction(data); | 357 node_->GetDelegate()->AccessibilityPerformAction(data); |
| 345 } | 358 } |
| 346 | 359 |
| 347 - (NSArray*)accessibilityAttributeNames { | 360 - (NSArray*)accessibilityAttributeNames { |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 687 } | 700 } |
| 688 NotifyMacEvent(native_node_, event_type); | 701 NotifyMacEvent(native_node_, event_type); |
| 689 } | 702 } |
| 690 | 703 |
| 691 int AXPlatformNodeMac::GetIndexInParent() { | 704 int AXPlatformNodeMac::GetIndexInParent() { |
| 692 // TODO(dmazzoni): implement this. http://crbug.com/396137 | 705 // TODO(dmazzoni): implement this. http://crbug.com/396137 |
| 693 return -1; | 706 return -1; |
| 694 } | 707 } |
| 695 | 708 |
| 696 } // namespace ui | 709 } // namespace ui |
| OLD | NEW |