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

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

Issue 2934993002: MacViews a11y: Allow VoiceOver to read out views::Label word-by-word. (Closed)
Patch Set: Neater 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 unified diff | Download patch
« no previous file with comments | « no previous file | ui/views/widget/native_widget_mac_accessibility_unittest.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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"
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 // The value that should be read out by screen readers.
234 - (NSString*)getStringValue;
233 @end 235 @end
234 236
235 @implementation AXPlatformNodeCocoa { 237 @implementation AXPlatformNodeCocoa {
236 ui::AXPlatformNodeBase* node_; // Weak. Retains us. 238 ui::AXPlatformNodeBase* node_; // Weak. Retains us.
237 } 239 }
238 240
239 @synthesize node = node_; 241 @synthesize node = node_;
240 242
241 // A mapping of AX roles to native roles. 243 // A mapping of AX roles to native roles.
242 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role { 244 + (NSString*)nativeRoleFromAXRole:(ui::AXRole)role {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 return gfx::ScreenRectToNSRect(node_->GetBoundsInScreen()); 282 return gfx::ScreenRectToNSRect(node_->GetBoundsInScreen());
281 } 283 }
282 284
283 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute { 285 - (NSString*)getStringAttribute:(ui::AXStringAttribute)attribute {
284 std::string attributeValue; 286 std::string attributeValue;
285 if (node_->GetStringAttribute(attribute, &attributeValue)) 287 if (node_->GetStringAttribute(attribute, &attributeValue))
286 return base::SysUTF8ToNSString(attributeValue); 288 return base::SysUTF8ToNSString(attributeValue);
287 return nil; 289 return nil;
288 } 290 }
289 291
292 - (NSString*)getStringValue {
293 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED))
dmazzoni 2017/06/13 20:35:54 I thought we were supposed to expose something for
tapted 2017/06/14 07:21:59 My best guess is that macOS had a regression that
294 return nil;
295 id value = [self AXValue];
296 return [value isKindOfClass:[NSString class]] ? value : [self AXTitle];
dmazzoni 2017/06/13 20:35:54 This seems like a backwards way of doing it to me.
tapted 2017/06/14 07:21:59 Part of the concern was that some of the methods w
297 }
298
290 // NSAccessibility informal protocol implementation. 299 // NSAccessibility informal protocol implementation.
291 300
292 - (BOOL)accessibilityIsIgnored { 301 - (BOOL)accessibilityIsIgnored {
293 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] || 302 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] ||
294 node_->GetData().HasState(ui::AX_STATE_INVISIBLE); 303 node_->GetData().HasState(ui::AX_STATE_INVISIBLE);
295 } 304 }
296 305
297 - (id)accessibilityHitTest:(NSPoint)point { 306 - (id)accessibilityHitTest:(NSPoint)point {
298 for (AXPlatformNodeCocoa* child in [self AXChildren]) { 307 for (AXPlatformNodeCocoa* child in [self AXChildren]) {
299 if (![child accessibilityIsIgnored] && 308 if (![child accessibilityIsIgnored] &&
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 NSAccessibilityEnabledAttribute, 365 NSAccessibilityEnabledAttribute,
357 NSAccessibilityFocusedAttribute, 366 NSAccessibilityFocusedAttribute,
358 NSAccessibilityHelpAttribute, 367 NSAccessibilityHelpAttribute,
359 NSAccessibilityTopLevelUIElementAttribute, 368 NSAccessibilityTopLevelUIElementAttribute,
360 NSAccessibilityWindowAttribute, 369 NSAccessibilityWindowAttribute,
361 ]; 370 ];
362 371
363 // Attributes required for user-editable controls. 372 // Attributes required for user-editable controls.
364 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ]; 373 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ];
365 374
366 // Attributes required for unprotected textfields. 375 // Attributes required for unprotected textfields and labels.
367 NSArray* const kUnprotectedTextfieldAttributes = @[ 376 NSArray* const kUnprotectedTextAttributes = @[
368 NSAccessibilityInsertionPointLineNumberAttribute, 377 NSAccessibilityInsertionPointLineNumberAttribute,
369 NSAccessibilityNumberOfCharactersAttribute, 378 NSAccessibilityNumberOfCharactersAttribute,
370 NSAccessibilitySelectedTextAttribute, 379 NSAccessibilitySelectedTextAttribute,
371 NSAccessibilitySelectedTextRangeAttribute, 380 NSAccessibilitySelectedTextRangeAttribute,
372 NSAccessibilityVisibleCharacterRangeAttribute, 381 NSAccessibilityVisibleCharacterRangeAttribute,
373 ]; 382 ];
374 383
375 // Required for all textfields, including protected ones. 384 // Required for all text, including protected textfields.
376 NSString* const kTextfieldAttributes = 385 NSString* const kTextAttributes = NSAccessibilityPlaceholderValueAttribute;
377 NSAccessibilityPlaceholderValueAttribute;
378 386
379 base::scoped_nsobject<NSMutableArray> axAttributes( 387 base::scoped_nsobject<NSMutableArray> axAttributes(
380 [[NSMutableArray alloc] init]); 388 [[NSMutableArray alloc] init]);
381 389
382 [axAttributes addObjectsFromArray:kAllRoleAttributes]; 390 [axAttributes addObjectsFromArray:kAllRoleAttributes];
391
383 switch (node_->GetData().role) { 392 switch (node_->GetData().role) {
384 case ui::AX_ROLE_TEXT_FIELD: 393 case ui::AX_ROLE_TEXT_FIELD:
385 [axAttributes addObject:kTextfieldAttributes]; 394 case ui::AX_ROLE_STATIC_TEXT:
395 [axAttributes addObject:kTextAttributes];
386 if (!node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 396 if (!node_->GetData().HasState(ui::AX_STATE_PROTECTED))
387 [axAttributes addObjectsFromArray:kUnprotectedTextfieldAttributes]; 397 [axAttributes addObjectsFromArray:kUnprotectedTextAttributes];
388 // Fallthrough. 398 // Fallthrough.
389 case ui::AX_ROLE_CHECK_BOX: 399 case ui::AX_ROLE_CHECK_BOX:
390 case ui::AX_ROLE_COMBO_BOX: 400 case ui::AX_ROLE_COMBO_BOX:
391 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: 401 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
392 case ui::AX_ROLE_MENU_ITEM_RADIO: 402 case ui::AX_ROLE_MENU_ITEM_RADIO:
393 case ui::AX_ROLE_RADIO_BUTTON: 403 case ui::AX_ROLE_RADIO_BUTTON:
394 case ui::AX_ROLE_SEARCH_BOX: 404 case ui::AX_ROLE_SEARCH_BOX:
395 case ui::AX_ROLE_SLIDER: 405 case ui::AX_ROLE_SLIDER:
396 case ui::AX_ROLE_SLIDER_THUMB: 406 case ui::AX_ROLE_SLIDER_THUMB:
397 case ui::AX_ROLE_TOGGLE_BUTTON: 407 case ui::AX_ROLE_TOGGLE_BUTTON:
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 break; 534 break;
525 } 535 }
526 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; 536 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role];
527 } 537 }
528 538
529 - (NSString*)AXHelp { 539 - (NSString*)AXHelp {
530 return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION]; 540 return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION];
531 } 541 }
532 542
533 - (id)AXValue { 543 - (id)AXValue {
534 if (node_->GetData().role == ui::AX_ROLE_TAB) 544 switch (node_->GetData().role) {
535 return [self AXSelected]; 545 case ui::AX_ROLE_TAB:
546 return [self AXSelected];
547 case ui::AX_ROLE_STATIC_TEXT:
548 return [self AXTitle];
549 default:
550 break;
551 }
536 return [self getStringAttribute:ui::AX_ATTR_VALUE]; 552 return [self getStringAttribute:ui::AX_ATTR_VALUE];
537 } 553 }
538 554
539 - (NSNumber*)AXEnabled { 555 - (NSNumber*)AXEnabled {
540 return @(!node_->GetData().HasState(ui::AX_STATE_DISABLED)); 556 return @(!node_->GetData().HasState(ui::AX_STATE_DISABLED));
541 } 557 }
542 558
543 - (NSNumber*)AXFocused { 559 - (NSNumber*)AXFocused {
544 if (node_->GetData().HasState(ui::AX_STATE_FOCUSABLE)) 560 if (node_->GetData().HasState(ui::AX_STATE_FOCUSABLE))
545 return 561 return
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
589 return @(node_->GetData().HasState(ui::AX_STATE_SELECTED)); 605 return @(node_->GetData().HasState(ui::AX_STATE_SELECTED));
590 } 606 }
591 607
592 - (NSString*)AXPlaceholderValue { 608 - (NSString*)AXPlaceholderValue {
593 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER]; 609 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER];
594 } 610 }
595 611
596 // Text-specific attributes. 612 // Text-specific attributes.
597 613
598 - (NSString*)AXSelectedText { 614 - (NSString*)AXSelectedText {
599 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 615 NSString* value = [self getStringValue];
616 if (!value)
600 return nil; 617 return nil;
601 618
602 NSRange selectedTextRange; 619 NSRange selectedTextRange;
603 [[self AXSelectedTextRange] getValue:&selectedTextRange]; 620 [[self AXSelectedTextRange] getValue:&selectedTextRange];
604 return [[self AXValue] substringWithRange:selectedTextRange]; 621 return [value substringWithRange:selectedTextRange];
605 } 622 }
606 623
607 - (NSValue*)AXSelectedTextRange { 624 - (NSValue*)AXSelectedTextRange {
608 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 625 if (![self getStringValue])
609 return nil; 626 return nil;
610 627
611 int textDir, start, end; 628 // Selection might not be supported. Return (NSRange){0,0} in that case.
612 node_->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, &textDir); 629 int start = 0, end = 0;
613 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start); 630 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start);
614 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end); 631 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end);
615 // NSRange cannot represent the direction the text was selected in, so make 632
616 // sure the correct selection index is used when creating a new range, taking 633 // NSRange cannot represent the direction the text was selected in.
617 // into account the textfield text direction as well. 634 return [NSValue valueWithRange:{std::min(start, end), abs(end - start)}];
618 bool isReversed = (textDir == ui::AX_TEXT_DIRECTION_RTL) ||
619 (textDir == ui::AX_TEXT_DIRECTION_BTT);
620 int beginSelectionIndex = (end > start && !isReversed) ? start : end;
621 return [NSValue valueWithRange:{beginSelectionIndex, abs(end - start)}];
622 } 635 }
623 636
624 - (NSNumber*)AXNumberOfCharacters { 637 - (NSNumber*)AXNumberOfCharacters {
625 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 638 NSString* value = [self getStringValue];
626 return nil; 639 return value ? @([value length]) : nil;
627 return @([[self AXValue] length]);
628 } 640 }
629 641
630 - (NSValue*)AXVisibleCharacterRange { 642 - (NSValue*)AXVisibleCharacterRange {
631 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 643 NSString* value = [self getStringValue];
632 return nil; 644 return value ? [NSValue valueWithRange:{0, [value length]}] : nil;
633 return [NSValue valueWithRange:{0, [[self AXNumberOfCharacters] intValue]}];
634 } 645 }
635 646
636 - (NSNumber*)AXInsertionPointLineNumber { 647 - (NSNumber*)AXInsertionPointLineNumber {
637 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) 648 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED))
638 return nil; 649 return nil;
639 // Multiline is not supported on views. 650 // Multiline is not supported on views.
640 return @0; 651 return @0;
641 } 652 }
642 653
643 @end 654 @end
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
694 } 705 }
695 NotifyMacEvent(native_node_, event_type); 706 NotifyMacEvent(native_node_, event_type);
696 } 707 }
697 708
698 int AXPlatformNodeMac::GetIndexInParent() { 709 int AXPlatformNodeMac::GetIndexInParent() {
699 // TODO(dmazzoni): implement this. http://crbug.com/396137 710 // TODO(dmazzoni): implement this. http://crbug.com/396137
700 return -1; 711 return -1;
701 } 712 }
702 713
703 } // namespace ui 714 } // namespace ui
OLDNEW
« no previous file with comments | « no previous file | ui/views/widget/native_widget_mac_accessibility_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698