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

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: Typo. 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 }
211 return event_map;
212 }
213
214 void NotifyMacEvent(ui::AXEvent event_type, gfx::AcceleratedWidget target) {
215 NSAccessibilityPostNotification(
216 target, [AXPlatformNodeCocoa nativeNotificationFromAXEvent:event_type]);
217 }
218
192 } // namespace 219 } // namespace
193 220
194 @interface AXPlatformNodeCocoa () 221 @interface AXPlatformNodeCocoa ()
195 // Helper function for string attributes that don't require extra processing. 222 // Helper function for string attributes that don't require extra processing.
196 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute; 223 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute;
197 @end 224 @end
198 225
199 @implementation AXPlatformNodeCocoa 226 @implementation AXPlatformNodeCocoa
200 227
201 // A mapping of AX roles to native roles. 228 // A mapping of AX roles to native roles.
202 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role { 229 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role {
203 CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap())); 230 CR_DEFINE_STATIC_LOCAL(RoleMap, role_map, (BuildRoleMap()));
204 RoleMap::iterator it = role_map.find(role); 231 RoleMap::iterator it = role_map.find(role);
205 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole; 232 return it != role_map.end() ? it->second : NSAccessibilityUnknownRole;
206 } 233 }
207 234
208 // A mapping of AX roles to native subroles. 235 // A mapping of AX roles to native subroles.
209 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role { 236 + (NSString*)nativeSubroleFromAXRole:(ui::AXRole)role {
210 CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap())); 237 CR_DEFINE_STATIC_LOCAL(RoleMap, subrole_map, (BuildSubroleMap()));
211 RoleMap::iterator it = subrole_map.find(role); 238 RoleMap::iterator it = subrole_map.find(role);
212 return it != subrole_map.end() ? it->second : nil; 239 return it != subrole_map.end() ? it->second : nil;
213 } 240 }
214 241
242 // A mapping of AX events to native notifications.
243 + (NSString*)nativeNotificationFromAXEvent:(ui::AXEvent)event {
244 CR_DEFINE_STATIC_LOCAL(EventMap, event_map, (BuildEventMap()));
245 EventMap::iterator it = event_map.find(event);
246 return it != event_map.end() ? it->second : nil;
247 }
248
215 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node { 249 - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node {
216 if ((self = [super init])) { 250 if ((self = [super init])) {
217 node_ = node; 251 node_ = node;
218 } 252 }
219 return self; 253 return self;
220 } 254 }
221 255
222 - (void)detach { 256 - (void)detach {
223 node_ = nil; 257 node_ = nil;
224 } 258 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
263 NSAccessibilityRoleAttribute, 297 NSAccessibilityRoleAttribute,
264 NSAccessibilitySizeAttribute, 298 NSAccessibilitySizeAttribute,
265 NSAccessibilitySubroleAttribute, 299 NSAccessibilitySubroleAttribute,
266 300
267 // Title is required for most elements. Cocoa asks for the value even if it 301 // 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. 302 // is omitted here, but won't present it to accessibility APIs without this.
269 NSAccessibilityTitleAttribute, 303 NSAccessibilityTitleAttribute,
270 304
271 // Attributes which are not required, but are general to all roles. 305 // Attributes which are not required, but are general to all roles.
272 NSAccessibilityRoleDescriptionAttribute, 306 NSAccessibilityRoleDescriptionAttribute,
307 NSAccessibilityEnabledAttribute,
308 NSAccessibilityFocusedAttribute,
273 ]; 309 ];
274 310
275 // Attributes required for user-editable controls. 311 // Attributes required for user-editable controls.
276 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ]; 312 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ];
277 313
314 // Attributes required for textfields.
315 NSArray* const kTextfieldAttributes = @[
316 NSAccessibilityInsertionPointLineNumberAttribute,
317 NSAccessibilityNumberOfCharactersAttribute,
318 NSAccessibilityPlaceholderValueAttribute,
319 NSAccessibilitySelectedTextAttribute,
320 NSAccessibilitySelectedTextRangeAttribute,
321 NSAccessibilityVisibleCharacterRangeAttribute,
322 ];
323
278 base::scoped_nsobject<NSMutableArray> axAttributes( 324 base::scoped_nsobject<NSMutableArray> axAttributes(
279 [[NSMutableArray alloc] init]); 325 [[NSMutableArray alloc] init]);
280 326
281 [axAttributes addObjectsFromArray:kAllRoleAttributes]; 327 [axAttributes addObjectsFromArray:kAllRoleAttributes];
282 switch (node_->GetData().role) { 328 switch (node_->GetData().role) {
329 case ui::AX_ROLE_TEXT_FIELD:
330 [axAttributes addObjectsFromArray:kTextfieldAttributes];
dmazzoni 2016/06/29 16:24:11 Please add a comment like "fallthrough" so that it
Patti Lor 2016/06/30 05:29:54 Done.
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 // TODO(patricialor): Check with dmazzoni@ to see if AX_STATE_ENABLED is still
dmazzoni 2016/06/29 16:24:11 This is correct for now, no TODO needed. My plan i
Patti Lor 2016/06/30 05:29:54 Removed, thanks for clarifying!
423 // relevant.
424 return [NSNumber
425 numberWithBool:!ui::AXViewState::IsFlagSet(node_->GetData().state,
426 ui::AX_STATE_DISABLED)];
427 }
428
429 - (NSValue*)AXFocused {
430 if (ui::AXViewState::IsFlagSet(node_->GetData().state,
431 ui::AX_STATE_FOCUSABLE) &&
432 ui::AXViewState::IsFlagSet(node_->GetData().state, ui::AX_STATE_FOCUSED))
433 return [NSNumber numberWithBool:YES];
434 return [NSNumber numberWithBool:NO];
435 }
436
437 // Textfield-specific NSAccessibility attributes.
438
439 - (NSNumber*)AXInsertionPointLineNumber {
440 // Multiline is not supported on views.
441 return [NSNumber numberWithInteger:0];
442 }
443
444 - (NSNumber*)AXNumberOfCharacters {
445 return [NSNumber numberWithInteger:[[self AXValue] length]];
446 }
447
448 - (NSString*)AXPlaceholderValue {
449 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER];
450 }
451
452 - (NSString*)AXSelectedText {
453 NSRange selectedTextRange;
454 [[self AXSelectedTextRange] getValue:&selectedTextRange];
455 return [[self AXValue] substringWithRange:selectedTextRange];
456 }
457
458 - (NSValue*)AXSelectedTextRange {
459 int textDir, start, end;
460 node_->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, &textDir);
461 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start);
462 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end);
463 // NSRange cannot represent the direction the text was selected in, so make
464 // sure the correct selection index is used when creating a new range, taking
465 // into account the textfield text direction as well.
466 bool isReversed = (textDir == ui::AX_TEXT_DIRECTION_RTL) ||
467 (textDir == ui::AX_TEXT_DIRECTION_BTT);
468 int beginSelectionIndex = (end > start && !isReversed) ? start : end;
469 return [NSValue
470 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))];
471 }
472
473 - (NSValue*)AXVisibleCharacterRange {
474 return [NSValue
475 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])];
476 }
477
374 @end 478 @end
375 479
376 namespace ui { 480 namespace ui {
377 481
378 // static 482 // static
379 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { 483 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) {
380 AXPlatformNodeBase* node = new AXPlatformNodeMac(); 484 AXPlatformNodeBase* node = new AXPlatformNodeMac();
381 node->Init(delegate); 485 node->Init(delegate);
382 return node; 486 return node;
383 } 487 }
(...skipping 10 matching lines...) Expand all
394 AXPlatformNodeBase::Destroy(); 498 AXPlatformNodeBase::Destroy();
395 } 499 }
396 500
397 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() { 501 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() {
398 if (!native_node_) 502 if (!native_node_)
399 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]); 503 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]);
400 return native_node_.get(); 504 return native_node_.get();
401 } 505 }
402 506
403 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) { 507 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) {
404 // TODO(dmazzoni): implement this. http://crbug.com/396137 508 gfx::AcceleratedWidget target =
509 GetDelegate()->GetTargetForNativeAccessibilityEvent();
510
511 // Add mappings between ui::AXEvent and NSAccessibility notifications using
512 // the EventMap above. This switch contains exceptions to those mappings.
513 switch (event_type) {
514 case ui::AX_EVENT_TEXT_CHANGED:
515 // If the view is a user-editable textfield, this should change the value.
516 if (GetData().role == ui::AX_ROLE_TEXT_FIELD) {
517 NSAccessibilityPostNotification(
518 target, NSAccessibilityValueChangedNotification);
519 return;
520 }
521 break;
522 default:
523 break;
524 }
525 NotifyMacEvent(event_type, target);
405 } 526 }
406 527
407 int AXPlatformNodeMac::GetIndexInParent() { 528 int AXPlatformNodeMac::GetIndexInParent() {
408 // TODO(dmazzoni): implement this. http://crbug.com/396137 529 // TODO(dmazzoni): implement this. http://crbug.com/396137
409 return -1; 530 return -1;
410 } 531 }
411 532
412 } // namespace ui 533 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698