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

Side by Side Diff: ui/accessibility/platform/ax_platform_node_mac.mm

Issue 2106953005: Mac: Add new accessibility attributes for textfields (and some for all views). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@ax-attrs
Patch Set: Address review comments. Created 4 years, 5 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 unified diff | Download patch
OLDNEW
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_node_data.h" 12 #include "ui/accessibility/ax_node_data.h"
13 #include "ui/accessibility/ax_view_state.h" 13 #include "ui/accessibility/ax_view_state.h"
14 #include "ui/accessibility/platform/ax_platform_node_delegate.h" 14 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
15 #import "ui/gfx/mac/coordinate_conversion.h" 15 #import "ui/gfx/mac/coordinate_conversion.h"
16 16
17 namespace { 17 namespace {
18 18
19 struct MapEntry { 19 struct RoleMapEntry {
20 ui::AXRole value; 20 ui::AXRole value;
21 NSString* nativeValue; 21 NSString* nativeValue;
22 }; 22 };
23 23
24 struct EventMapEntry {
25 ui::AXEvent value;
26 NSString* nativeValue;
27 };
28
24 typedef std::map<ui::AXRole, NSString*> RoleMap; 29 typedef std::map<ui::AXRole, NSString*> RoleMap;
30 typedef std::map<ui::AXEvent, NSString*> EventMap;
25 31
26 RoleMap BuildRoleMap() { 32 RoleMap BuildRoleMap() {
27 const MapEntry roles[] = { 33 const RoleMapEntry roles[] = {
28 {ui::AX_ROLE_ABBR, NSAccessibilityGroupRole}, 34 {ui::AX_ROLE_ABBR, NSAccessibilityGroupRole},
29 {ui::AX_ROLE_ALERT, NSAccessibilityGroupRole}, 35 {ui::AX_ROLE_ALERT, NSAccessibilityGroupRole},
30 {ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole}, 36 {ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole},
31 {ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole}, 37 {ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole},
32 {ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole}, 38 {ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole},
33 {ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole}, 39 {ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole},
34 {ui::AX_ROLE_BANNER, NSAccessibilityGroupRole}, 40 {ui::AX_ROLE_BANNER, NSAccessibilityGroupRole},
35 {ui::AX_ROLE_BLOCKQUOTE, NSAccessibilityGroupRole}, 41 {ui::AX_ROLE_BLOCKQUOTE, NSAccessibilityGroupRole},
36 {ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole}, 42 {ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole},
37 {ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole}, 43 {ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole},
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole }, 149 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
144 }; 150 };
145 151
146 RoleMap role_map; 152 RoleMap role_map;
147 for (size_t i = 0; i < arraysize(roles); ++i) 153 for (size_t i = 0; i < arraysize(roles); ++i)
148 role_map[roles[i].value] = roles[i].nativeValue; 154 role_map[roles[i].value] = roles[i].nativeValue;
149 return role_map; 155 return role_map;
150 } 156 }
151 157
152 RoleMap BuildSubroleMap() { 158 RoleMap BuildSubroleMap() {
153 const MapEntry subroles[] = { 159 const RoleMapEntry subroles[] = {
154 {ui::AX_ROLE_ALERT, @"AXApplicationAlert"}, 160 {ui::AX_ROLE_ALERT, @"AXApplicationAlert"},
155 {ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog"}, 161 {ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog"},
156 {ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication"}, 162 {ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication"},
157 {ui::AX_ROLE_ARTICLE, @"AXDocumentArticle"}, 163 {ui::AX_ROLE_ARTICLE, @"AXDocumentArticle"},
158 {ui::AX_ROLE_BANNER, @"AXLandmarkBanner"}, 164 {ui::AX_ROLE_BANNER, @"AXLandmarkBanner"},
159 {ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary"}, 165 {ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary"},
160 {ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo"}, 166 {ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo"},
161 {ui::AX_ROLE_DEFINITION, @"AXDefinition"}, 167 {ui::AX_ROLE_DEFINITION, @"AXDefinition"},
162 {ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDefinition"}, 168 {ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDefinition"},
163 {ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm"}, 169 {ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm"},
(...skipping 18 matching lines...) Expand all
182 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"}, 188 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"},
183 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole}, 189 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole},
184 }; 190 };
185 191
186 RoleMap subrole_map; 192 RoleMap subrole_map;
187 for (size_t i = 0; i < arraysize(subroles); ++i) 193 for (size_t i = 0; i < arraysize(subroles); ++i)
188 subrole_map[subroles[i].value] = subroles[i].nativeValue; 194 subrole_map[subroles[i].value] = subroles[i].nativeValue;
189 return subrole_map; 195 return subrole_map;
190 } 196 }
191 197
198 EventMap BuildEventMap() {
199 const EventMapEntry events[] = {
200 {ui::AX_EVENT_TEXT_CHANGED, NSAccessibilityTitleChangedNotification},
201 {ui::AX_EVENT_VALUE_CHANGED, NSAccessibilityValueChangedNotification},
202 {ui::AX_EVENT_TEXT_SELECTION_CHANGED,
203 NSAccessibilitySelectedTextChangedNotification},
204 // TODO(patricialor): Add more events.
205 };
206
207 EventMap event_map;
208 for (size_t i = 0; i < arraysize(events); ++i)
209 event_map[events[i].value] = events[i].nativeValue;
210 return event_map;
211 }
212
213 void NotifyMacEvent(NSView* target, ui::AXEvent event_type) {
214 NSAccessibilityPostNotification(
215 target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]);
216 }
217
192 } // namespace 218 } // namespace
193 219
194 @interface AXPlatformNodeCocoa () 220 @interface AXPlatformNodeCocoa ()
195 // Helper function for string attributes that don't require extra processing. 221 // Helper function for string attributes that don't require extra processing.
196 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute; 222 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute;
197 @end 223 @end
198 224
199 @implementation AXPlatformNodeCocoa 225 @implementation AXPlatformNodeCocoa
200 226
201 // A mapping of AX roles to native roles. 227 // A mapping of AX roles to native roles.
202 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role { 228 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role {
203 CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap())); 229 CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap()));
204 RoleMap::iterator it = role_map.find(role); 230 RoleMap::iterator it = role_map.find(role);
205 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole; 231 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole;
206 } 232 }
207 233
208 // A mapping of AX roles to native subroles. 234 // A mapping of AX roles to native subroles.
209 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role { 235 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role {
210 CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap())); 236 CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap()));
211 RoleMap::iterator it = subrole_map.find(role); 237 RoleMap::iterator it = subrole_map.find(role);
212 return it != subrole_map.end() ? it->second : nil; 238 return it != subrole_map.end() ? it->second : nil;
213 } 239 }
214 240
241 // A mapping of AX events to native notifications.
242 + (NSString*)nativeNotificationFromAXEvent:(ui::AXEvent)event {
243 CR_DEFINE_STATIC_LOCAL(EventMap, event_map, (BuildEventMap()));
244 EventMap::iterator it = event_map.find(event);
245 return it != event_map.end() ? it->second : nil;
246 }
247
215 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node { 248 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node {
216 if ((self = [super init])) { 249 if ((self = [super init])) {
217 node_ = node; 250 node_ = node;
218 } 251 }
219 return self; 252 return self;
220 } 253 }
221 254
222 - (void)detach { 255 - (void)detach {
223 node_ = nil; 256 node_ = nil;
224 } 257 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 NSAccessibilityRoleAttribute, 296 NSAccessibilityRoleAttribute,
264 NSAccessibilitySizeAttribute, 297 NSAccessibilitySizeAttribute,
265 NSAccessibilitySubroleAttribute, 298 NSAccessibilitySubroleAttribute,
266 299
267 // Title is required for most elements. Cocoa asks for the value even if it 300 // Title is required for most elements. Cocoa asks for the value even if it
268 // is omitted here, but won't present it to accessibility APIs without this. 301 // is omitted here, but won't present it to accessibility APIs without this.
269 NSAccessibilityTitleAttribute, 302 NSAccessibilityTitleAttribute,
270 303
271 // Attributes which are not required, but are general to all roles. 304 // Attributes which are not required, but are general to all roles.
272 NSAccessibilityRoleDescriptionAttribute, 305 NSAccessibilityRoleDescriptionAttribute,
306 NSAccessibilityEnabledAttribute,
307 NSAccessibilityFocusedAttribute,
273 ]; 308 ];
274 309
275 // Attributes required for user-editable controls. 310 // Attributes required for user-editable controls.
276 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ]; 311 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ];
277 312
313 // Attributes required for textfields.
314 NSArray* const kTextfieldAttributes = @[
315 NSAccessibilityInsertionPointLineNumberAttribute,
316 NSAccessibilityNumberOfCharactersAttribute,
317 NSAccessibilityPlaceholderValueAttribute,
318 NSAccessibilitySelectedTextAttribute,
319 NSAccessibilitySelectedTextRangeAttribute,
320 NSAccessibilityVisibleCharacterRangeAttribute,
321 ];
322
278 base::scoped_nsobject<NSMutableArray> axAttributes( 323 base::scoped_nsobject<NSMutableArray> axAttributes(
279 [[NSMutableArray alloc] init]); 324 [[NSMutableArray alloc] init]);
280 325
281 [axAttributes addObjectsFromArray:kAllRoleAttributes]; 326 [axAttributes addObjectsFromArray:kAllRoleAttributes];
282 switch (node_->GetData().role) { 327 switch (node_->GetData().role) {
328 case ui::AX_ROLE_TEXT_FIELD:
329 [axAttributes addObjectsFromArray:kTextfieldAttributes];
330 // Fallthrough.
283 case ui::AX_ROLE_CHECK_BOX: 331 case ui::AX_ROLE_CHECK_BOX:
284 case ui::AX_ROLE_COMBO_BOX: 332 case ui::AX_ROLE_COMBO_BOX:
285 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: 333 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
286 case ui::AX_ROLE_MENU_ITEM_RADIO: 334 case ui::AX_ROLE_MENU_ITEM_RADIO:
287 case ui::AX_ROLE_RADIO_BUTTON: 335 case ui::AX_ROLE_RADIO_BUTTON:
288 case ui::AX_ROLE_SEARCH_BOX: 336 case ui::AX_ROLE_SEARCH_BOX:
289 case ui::AX_ROLE_SLIDER: 337 case ui::AX_ROLE_SLIDER:
290 case ui::AX_ROLE_SLIDER_THUMB: 338 case ui::AX_ROLE_SLIDER_THUMB:
291 case ui::AX_ROLE_TOGGLE_BUTTON: 339 case ui::AX_ROLE_TOGGLE_BUTTON:
292 case ui::AX_ROLE_TEXT_FIELD:
293 [axAttributes addObjectsFromArray:kValueAttributes]; 340 [axAttributes addObjectsFromArray:kValueAttributes];
294 break; 341 break;
295 // TODO(tapted): Add additional attributes based on role. 342 // TODO(tapted): Add additional attributes based on role.
296 default: 343 default:
297 break; 344 break;
298 } 345 }
299 return axAttributes.autorelease(); 346 return axAttributes.autorelease();
300 } 347 }
301 348
302 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute { 349 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 } 411 }
365 412
366 - (NSString*)AXTitle { 413 - (NSString*)AXTitle {
367 return [self getStringAttribute:ui::AX_ATTR_NAME]; 414 return [self getStringAttribute:ui::AX_ATTR_NAME];
368 } 415 }
369 416
370 - (NSString*)AXValue { 417 - (NSString*)AXValue {
371 return [self getStringAttribute:ui::AX_ATTR_VALUE]; 418 return [self getStringAttribute:ui::AX_ATTR_VALUE];
372 } 419 }
373 420
421 - (NSValue*)AXEnabled {
422 return [NSNumber
423 numberWithBool:!ui::AXViewState::IsFlagSet(node_->GetData().state,
424 ui::AX_STATE_DISABLED)];
425 }
426
427 - (NSValue*)AXFocused {
428 if (ui::AXViewState::IsFlagSet(node_->GetData().state,
429 ui::AX_STATE_FOCUSABLE))
430 return [NSNumber numberWithBool:(node_->GetDelegate()->GetFocus() ==
431 node_->GetNativeViewAccessible())];
432 return [NSNumber numberWithBool:NO];
433 }
434
435 // Textfield-specific NSAccessibility attributes.
436
437 - (NSNumber*)AXInsertionPointLineNumber {
438 // Multiline is not supported on views.
439 return [NSNumber numberWithInteger:0];
440 }
441
442 - (NSNumber*)AXNumberOfCharacters {
443 return [NSNumber numberWithInteger:[[self AXValue] length]];
444 }
445
446 - (NSString*)AXPlaceholderValue {
447 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER];
448 }
449
450 - (NSString*)AXSelectedText {
451 NSRange selectedTextRange;
452 [[self AXSelectedTextRange] getValue:&selectedTextRange];
453 return [[self AXValue] substringWithRange:selectedTextRange];
454 }
455
456 - (NSValue*)AXSelectedTextRange {
457 int textDir, start, end;
458 node_->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, &textDir);
459 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start);
460 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end);
461 // NSRange cannot represent the direction the text was selected in, so make
462 // sure the correct selection index is used when creating a new range, taking
463 // into account the textfield text direction as well.
464 bool isReversed = (textDir == ui::AX_TEXT_DIRECTION_RTL) ||
465 (textDir == ui::AX_TEXT_DIRECTION_BTT);
466 int beginSelectionIndex = (end > start && !isReversed) ? start : end;
467 return [NSValue
468 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))];
469 }
470
471 - (NSValue*)AXVisibleCharacterRange {
472 return [NSValue
473 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])];
474 }
475
374 @end 476 @end
375 477
376 namespace ui { 478 namespace ui {
377 479
378 // static 480 // static
379 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { 481 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) {
380 AXPlatformNodeBase* node = new AXPlatformNodeMac(); 482 AXPlatformNodeBase* node = new AXPlatformNodeMac();
381 node->Init(delegate); 483 node->Init(delegate);
382 return node; 484 return node;
383 } 485 }
(...skipping 10 matching lines...) Expand all
394 AXPlatformNodeBase::Destroy(); 496 AXPlatformNodeBase::Destroy();
395 } 497 }
396 498
397 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() { 499 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() {
398 if (!native_node_) 500 if (!native_node_)
399 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]); 501 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]);
400 return native_node_.get(); 502 return native_node_.get();
401 } 503 }
402 504
403 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) { 505 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) {
404 // TODO(dmazzoni): implement this. http://crbug.com/396137 506 NSView* target = GetDelegate()->GetTargetForNativeAccessibilityEvent();
507
508 // Add mappings between ui::AXEvent and NSAccessibility notifications using
509 // the EventMap above. This switch contains exceptions to those mappings.
510 switch (event_type) {
511 case ui::AX_EVENT_TEXT_CHANGED:
512 // If the view is a user-editable textfield, this should change the value.
513 if (GetData().role == ui::AX_ROLE_TEXT_FIELD) {
514 NotifyMacEvent(target, ui::AX_EVENT_VALUE_CHANGED);
515 return;
516 }
517 break;
518 default:
519 break;
520 }
521 NotifyMacEvent(target, event_type);
405 } 522 }
406 523
407 int AXPlatformNodeMac::GetIndexInParent() { 524 int AXPlatformNodeMac::GetIndexInParent() {
408 // TODO(dmazzoni): implement this. http://crbug.com/396137 525 // TODO(dmazzoni): implement this. http://crbug.com/396137
409 return -1; 526 return -1;
410 } 527 }
411 528
412 } // namespace ui 529 } // namespace ui
OLDNEW
« no previous file with comments | « ui/accessibility/platform/ax_platform_node_mac.h ('k') | ui/views/accessibility/native_view_accessibility.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698