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

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

Issue 2578303003: a11y: Add a11y information to views::Tab and manually ignore its a11y children. (Closed)
Patch Set: Review comments. Created 3 years, 11 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_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/platform/ax_platform_node_delegate.h" 14 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
15 #include "ui/base/l10n/l10n_util.h"
15 #import "ui/gfx/mac/coordinate_conversion.h" 16 #import "ui/gfx/mac/coordinate_conversion.h"
17 #include "ui/strings/grit/ui_strings.h"
16 18
17 namespace { 19 namespace {
18 20
19 struct RoleMapEntry { 21 struct RoleMapEntry {
20 ui::AXRole value; 22 ui::AXRole value;
21 NSString* nativeValue; 23 NSString* nativeValue;
22 }; 24 };
23 25
24 struct EventMapEntry { 26 struct EventMapEntry {
25 ui::AXEvent value; 27 ui::AXEvent value;
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 {ui::AX_ROLE_MAIN, @"AXLandmarkMain"}, 179 {ui::AX_ROLE_MAIN, @"AXLandmarkMain"},
178 {ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee"}, 180 {ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee"},
179 {ui::AX_ROLE_MATH, @"AXDocumentMath"}, 181 {ui::AX_ROLE_MATH, @"AXDocumentMath"},
180 {ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation"}, 182 {ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation"},
181 {ui::AX_ROLE_NOTE, @"AXDocumentNote"}, 183 {ui::AX_ROLE_NOTE, @"AXDocumentNote"},
182 {ui::AX_ROLE_REGION, @"AXDocumentRegion"}, 184 {ui::AX_ROLE_REGION, @"AXDocumentRegion"},
183 {ui::AX_ROLE_SEARCH, @"AXLandmarkSearch"}, 185 {ui::AX_ROLE_SEARCH, @"AXLandmarkSearch"},
184 {ui::AX_ROLE_SEARCH_BOX, @"AXSearchField"}, 186 {ui::AX_ROLE_SEARCH_BOX, @"AXSearchField"},
185 {ui::AX_ROLE_STATUS, @"AXApplicationStatus"}, 187 {ui::AX_ROLE_STATUS, @"AXApplicationStatus"},
186 {ui::AX_ROLE_SWITCH, @"AXSwitch"}, 188 {ui::AX_ROLE_SWITCH, @"AXSwitch"},
189 {ui::AX_ROLE_TAB, @"AXTab"},
187 {ui::AX_ROLE_TAB_PANEL, @"AXTabPanel"}, 190 {ui::AX_ROLE_TAB_PANEL, @"AXTabPanel"},
188 {ui::AX_ROLE_TIMER, @"AXApplicationTimer"}, 191 {ui::AX_ROLE_TIMER, @"AXApplicationTimer"},
189 {ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton"}, 192 {ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton"},
190 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"}, 193 {ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip"},
191 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole}, 194 {ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole},
192 }; 195 };
193 196
194 RoleMap subrole_map; 197 RoleMap subrole_map;
195 for (size_t i = 0; i < arraysize(subroles); ++i) 198 for (size_t i = 0; i < arraysize(subroles); ++i)
196 subrole_map[subroles[i].value] = subroles[i].nativeValue; 199 subrole_map[subroles[i].value] = subroles[i].nativeValue;
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 default: 365 default:
363 break; 366 break;
364 } 367 }
365 return axAttributes.autorelease(); 368 return axAttributes.autorelease();
366 } 369 }
367 370
368 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName { 371 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName {
369 // Allow certain attributes to be written via an accessibility client. A 372 // Allow certain attributes to be written via an accessibility client. A
370 // writable attribute will only appear as such if the accessibility element 373 // writable attribute will only appear as such if the accessibility element
371 // has a value set for that attribute. 374 // has a value set for that attribute.
372 if ([attributeName isEqualToString:NSAccessibilitySelectedAttribute] || 375 if ([attributeName
373 [attributeName
374 isEqualToString:NSAccessibilitySelectedChildrenAttribute] || 376 isEqualToString:NSAccessibilitySelectedChildrenAttribute] ||
375 [attributeName 377 [attributeName
376 isEqualToString:NSAccessibilitySelectedTextRangeAttribute] || 378 isEqualToString:NSAccessibilitySelectedTextRangeAttribute] ||
377 [attributeName 379 [attributeName
378 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { 380 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
379 return NO; 381 return NO;
380 } 382 }
381 383
384 // Since tabs use the Radio Button role on Mac, the standard way to set them
385 // is via the value attribute rather than the selected attribute.
386 if ([attributeName isEqualToString:NSAccessibilityValueAttribute] &&
387 node_->GetData().role == ui::AX_ROLE_TAB) {
388 return !node_->GetData().HasStateFlag(ui::AX_STATE_SELECTED);
389 }
390
382 if ([attributeName isEqualToString:NSAccessibilityValueAttribute] || 391 if ([attributeName isEqualToString:NSAccessibilityValueAttribute] ||
383 [attributeName isEqualToString:NSAccessibilitySelectedTextAttribute]) 392 [attributeName isEqualToString:NSAccessibilitySelectedTextAttribute]) {
384 return !ui::AXNodeData::IsFlagSet(node_->GetData().state, 393 return !ui::AXNodeData::IsFlagSet(node_->GetData().state,
385 ui::AX_STATE_READ_ONLY); 394 ui::AX_STATE_READ_ONLY);
395 }
386 396
387 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) { 397 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) {
388 return ui::AXNodeData::IsFlagSet(node_->GetData().state, 398 return ui::AXNodeData::IsFlagSet(node_->GetData().state,
389 ui::AX_STATE_FOCUSABLE); 399 ui::AX_STATE_FOCUSABLE);
390 } 400 }
391 401
392 // TODO(patricialor): Add callbacks for updating the above attributes except 402 // TODO(patricialor): Add callbacks for updating the above attributes except
393 // NSAccessibilityValueAttribute and return YES. 403 // NSAccessibilityValueAttribute and return YES.
394 return NO; 404 return NO;
395 } 405 }
396 406
397 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { 407 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
398 ui::AXActionData data; 408 ui::AXActionData data;
409
410 // Check for attributes which accept any |value| type (i.e. ids) first. Only
411 // the |data.action| should be set here - any type-specific information, if
412 // needed, should be set below, where there are type checks.
413 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
414 data.action = node_->GetData().role == ui::AX_ROLE_TAB
415 ? ui::AX_ACTION_SET_SELECTION
416 : data.action = ui::AX_ACTION_SET_VALUE;
tapted 2017/01/10 16:22:18 the single `=` doesn't look right on this line, al
Patti Lor 2017/01/11 05:51:44 Oops, thank you - joined the line but didn't edit
417 }
418
419 // Set actions for attributes restricted to a specific type. Also set
420 // type-specific information as necessary for actions set above.
tapted 2017/01/10 16:22:18 It looks easy to introduce codepaths where the stu
Patti Lor 2017/01/11 05:51:44 Not intentional - I was trying to make a nice way
399 if ([value isKindOfClass:[NSString class]]) { 421 if ([value isKindOfClass:[NSString class]]) {
400 data.value = base::SysNSStringToUTF16(value); 422 data.value = base::SysNSStringToUTF16(value);
401 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { 423 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
402 data.action = ui::AX_ACTION_SET_VALUE;
403 } else if ([attribute
404 isEqualToString:NSAccessibilitySelectedTextAttribute]) {
405 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; 424 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT;
406 } 425 }
407 } else if ([value isKindOfClass:[NSNumber class]]) { 426 } else if ([value isKindOfClass:[NSNumber class]]) {
408 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { 427 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
409 data.action = 428 data.action =
410 [value boolValue] ? ui::AX_ACTION_FOCUS : ui::AX_ACTION_BLUR; 429 [value boolValue] ? ui::AX_ACTION_FOCUS : ui::AX_ACTION_BLUR;
411 } 430 }
412 } 431 }
413 432
414 if (data.action != ui::AX_ACTION_NONE) 433 if (data.action != ui::AX_ACTION_NONE)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 ui::AX_STATE_PROTECTED)) 480 ui::AX_STATE_PROTECTED))
462 return NSAccessibilitySecureTextFieldSubrole; 481 return NSAccessibilitySecureTextFieldSubrole;
463 break; 482 break;
464 default: 483 default:
465 break; 484 break;
466 } 485 }
467 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; 486 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role];
468 } 487 }
469 488
470 - (NSString*)AXRoleDescription { 489 - (NSString*)AXRoleDescription {
490 switch (node_->GetData().role) {
491 case ui::AX_ROLE_TAB:
492 // There is no NSAccessibilityTabRole or similar (AXRadioButton is used
493 // instead). Do the same as NSTabView and put "tab" in the description.
494 return [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TAB_ROLE_DESCRIPTION)
495 lowercaseString];
496 default:
497 break;
498 }
471 return NSAccessibilityRoleDescription([self AXRole], [self AXSubrole]); 499 return NSAccessibilityRoleDescription([self AXRole], [self AXSubrole]);
472 } 500 }
473 501
474 - (NSValue*)AXSize { 502 - (NSValue*)AXSize {
475 return [NSValue valueWithSize:self.boundsInScreen.size]; 503 return [NSValue valueWithSize:self.boundsInScreen.size];
476 } 504 }
477 505
478 - (NSString*)AXTitle { 506 - (NSString*)AXTitle {
479 return [self getStringAttribute:ui::AX_ATTR_NAME]; 507 return [self getStringAttribute:ui::AX_ATTR_NAME];
480 } 508 }
481 509
482 - (NSString*)AXValue { 510 - (id)AXValue {
511 if (node_->GetData().role == ui::AX_ROLE_TAB)
512 return [self AXSelected];
483 return [self getStringAttribute:ui::AX_ATTR_VALUE]; 513 return [self getStringAttribute:ui::AX_ATTR_VALUE];
484 } 514 }
485 515
486 - (NSValue*)AXEnabled { 516 - (NSValue*)AXEnabled {
487 return [NSNumber 517 return [NSNumber
488 numberWithBool:!ui::AXNodeData::IsFlagSet(node_->GetData().state, 518 numberWithBool:!ui::AXNodeData::IsFlagSet(node_->GetData().state,
489 ui::AX_STATE_DISABLED)]; 519 ui::AX_STATE_DISABLED)];
490 } 520 }
491 521
492 - (NSValue*)AXFocused { 522 - (NSValue*)AXFocused {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 int beginSelectionIndex = (end > start && !isReversed) ? start : end; 573 int beginSelectionIndex = (end > start && !isReversed) ? start : end;
544 return [NSValue 574 return [NSValue
545 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))]; 575 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))];
546 } 576 }
547 577
548 - (NSValue*)AXVisibleCharacterRange { 578 - (NSValue*)AXVisibleCharacterRange {
549 return [NSValue 579 return [NSValue
550 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])]; 580 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])];
551 } 581 }
552 582
583 // Misc NSAccessibility attributes.
584
585 - (NSNumber*)AXSelected {
586 return [NSNumber
587 numberWithBool:node_->GetData().HasStateFlag(ui::AX_STATE_SELECTED)];
588 }
589
553 @end 590 @end
554 591
555 namespace ui { 592 namespace ui {
556 593
557 // static 594 // static
558 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { 595 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) {
559 AXPlatformNodeBase* node = new AXPlatformNodeMac(); 596 AXPlatformNodeBase* node = new AXPlatformNodeMac();
560 node->Init(delegate); 597 node->Init(delegate);
561 return node; 598 return node;
562 } 599 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 } 633 }
597 NotifyMacEvent(native_node_, event_type); 634 NotifyMacEvent(native_node_, event_type);
598 } 635 }
599 636
600 int AXPlatformNodeMac::GetIndexInParent() { 637 int AXPlatformNodeMac::GetIndexInParent() {
601 // TODO(dmazzoni): implement this. http://crbug.com/396137 638 // TODO(dmazzoni): implement this. http://crbug.com/396137
602 return -1; 639 return -1;
603 } 640 }
604 641
605 } // namespace ui 642 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698