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

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: Remove focused attribute, other 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) {
tapted 2016/06/30 01:58:57 nit: no curlies (consistent with the others)
Patti Lor 2016/06/30 05:29:54 Done.
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) {
tapted 2016/06/30 01:58:57 AcceleratedWidget -> NSView* (it's easier to read
Patti Lor 2016/06/30 05:29:54 Done.
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];
331 // Fallthrough.
283 case ui::AX_ROLE_CHECK_BOX: 332 case ui::AX_ROLE_CHECK_BOX:
284 case ui::AX_ROLE_COMBO_BOX: 333 case ui::AX_ROLE_COMBO_BOX:
285 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: 334 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
286 case ui::AX_ROLE_MENU_ITEM_RADIO: 335 case ui::AX_ROLE_MENU_ITEM_RADIO:
287 case ui::AX_ROLE_RADIO_BUTTON: 336 case ui::AX_ROLE_RADIO_BUTTON:
288 case ui::AX_ROLE_SEARCH_BOX: 337 case ui::AX_ROLE_SEARCH_BOX:
289 case ui::AX_ROLE_SLIDER: 338 case ui::AX_ROLE_SLIDER:
290 case ui::AX_ROLE_SLIDER_THUMB: 339 case ui::AX_ROLE_SLIDER_THUMB:
291 case ui::AX_ROLE_TOGGLE_BUTTON: 340 case ui::AX_ROLE_TOGGLE_BUTTON:
292 case ui::AX_ROLE_TEXT_FIELD:
293 [axAttributes addObjectsFromArray:kValueAttributes]; 341 [axAttributes addObjectsFromArray:kValueAttributes];
294 break; 342 break;
295 // TODO(tapted): Add additional attributes based on role. 343 // TODO(tapted): Add additional attributes based on role.
296 default: 344 default:
297 break; 345 break;
298 } 346 }
299 return axAttributes.autorelease(); 347 return axAttributes.autorelease();
300 } 348 }
301 349
302 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute { 350 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 } 412 }
365 413
366 - (NSString*)AXTitle { 414 - (NSString*)AXTitle {
367 return [self getStringAttribute:ui::AX_ATTR_NAME]; 415 return [self getStringAttribute:ui::AX_ATTR_NAME];
368 } 416 }
369 417
370 - (NSString*)AXValue { 418 - (NSString*)AXValue {
371 return [self getStringAttribute:ui::AX_ATTR_VALUE]; 419 return [self getStringAttribute:ui::AX_ATTR_VALUE];
372 } 420 }
373 421
422 - (NSValue*)AXEnabled {
423 return [NSNumber
424 numberWithBool:!ui::AXViewState::IsFlagSet(node_->GetData().state,
425 ui::AX_STATE_DISABLED)];
426 }
427
428 - (NSValue*)AXFocused {
429 if (ui::AXViewState::IsFlagSet(node_->GetData().state,
430 ui::AX_STATE_FOCUSABLE))
431 return [NSNumber numberWithBool:(node_->GetDelegate()->GetFocus() ==
432 node_->GetNativeViewAccessible())];
433 return [NSNumber numberWithBool:NO];
434 }
435
436 // Textfield-specific NSAccessibility attributes.
437
438 - (NSNumber*)AXInsertionPointLineNumber {
439 // Multiline is not supported on views.
440 return [NSNumber numberWithInteger:0];
441 }
442
443 - (NSNumber*)AXNumberOfCharacters {
444 return [NSNumber numberWithInteger:[[self AXValue] length]];
445 }
446
447 - (NSString*)AXPlaceholderValue {
448 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER];
449 }
450
451 - (NSString*)AXSelectedText {
452 NSRange selectedTextRange;
453 [[self AXSelectedTextRange] getValue:&selectedTextRange];
454 return [[self AXValue] substringWithRange:selectedTextRange];
455 }
456
457 - (NSValue*)AXSelectedTextRange {
458 int textDir, start, end;
459 node_->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, &textDir);
460 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start);
461 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end);
462 // NSRange cannot represent the direction the text was selected in, so make
463 // sure the correct selection index is used when creating a new range, taking
464 // into account the textfield text direction as well.
465 bool isReversed = (textDir == ui::AX_TEXT_DIRECTION_RTL) ||
466 (textDir == ui::AX_TEXT_DIRECTION_BTT);
467 int beginSelectionIndex = (end > start && !isReversed) ? start : end;
468 return [NSValue
469 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))];
470 }
471
472 - (NSValue*)AXVisibleCharacterRange {
473 return [NSValue
474 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])];
475 }
476
374 @end 477 @end
375 478
376 namespace ui { 479 namespace ui {
377 480
378 // static 481 // static
379 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { 482 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) {
380 AXPlatformNodeBase* node = new AXPlatformNodeMac(); 483 AXPlatformNodeBase* node = new AXPlatformNodeMac();
381 node->Init(delegate); 484 node->Init(delegate);
382 return node; 485 return node;
383 } 486 }
(...skipping 10 matching lines...) Expand all
394 AXPlatformNodeBase::Destroy(); 497 AXPlatformNodeBase::Destroy();
395 } 498 }
396 499
397 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() { 500 gfx::NativeViewAccessible AXPlatformNodeMac::GetNativeViewAccessible() {
398 if (!native_node_) 501 if (!native_node_)
399 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]); 502 native_node_.reset([[AXPlatformNodeCocoa alloc] initWithNode:this]);
400 return native_node_.get(); 503 return native_node_.get();
401 } 504 }
402 505
403 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) { 506 void AXPlatformNodeMac::NotifyAccessibilityEvent(ui::AXEvent event_type) {
404 // TODO(dmazzoni): implement this. http://crbug.com/396137 507 gfx::AcceleratedWidget target =
tapted 2016/06/30 01:58:57 nit: NSView*
Patti Lor 2016/06/30 05:29:54 Done.
508 GetDelegate()->GetTargetForNativeAccessibilityEvent();
509
510 // Add mappings between ui::AXEvent and NSAccessibility notifications using
511 // the EventMap above. This switch contains exceptions to those mappings.
512 switch (event_type) {
513 case ui::AX_EVENT_TEXT_CHANGED:
514 // If the view is a user-editable textfield, this should change the value.
515 if (GetData().role == ui::AX_ROLE_TEXT_FIELD) {
516 NSAccessibilityPostNotification(
tapted 2016/06/30 01:58:57 I'd go with NotifyMacEvent(target, ui::AX_EVENT_V
Patti Lor 2016/06/30 05:29:54 Done, thanks!
517 target, NSAccessibilityValueChangedNotification);
518 return;
519 }
520 break;
521 default:
522 break;
523 }
524 NotifyMacEvent(event_type, target);
405 } 525 }
406 526
407 int AXPlatformNodeMac::GetIndexInParent() { 527 int AXPlatformNodeMac::GetIndexInParent() {
408 // TODO(dmazzoni): implement this. http://crbug.com/396137 528 // TODO(dmazzoni): implement this. http://crbug.com/396137
409 return -1; 529 return -1;
410 } 530 }
411 531
412 } // namespace ui 532 } // 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