Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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; |
| 399 if ([value isKindOfClass:[NSString class]]) { | 409 |
| 400 data.value = base::SysNSStringToUTF16(value); | 410 // Check for attributes first. Only the |data.action| should be set here - any |
| 401 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { | 411 // type-specific information, if needed, should be set below. |
| 402 data.action = ui::AX_ACTION_SET_VALUE; | 412 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { |
| 403 } else if ([attribute | 413 data.action = node_->GetData().role == ui::AX_ROLE_TAB |
| 404 isEqualToString:NSAccessibilitySelectedTextAttribute]) { | 414 ? ui::AX_ACTION_SET_SELECTION |
| 405 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; | 415 : ui::AX_ACTION_SET_VALUE; |
| 406 } | 416 } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { |
| 407 } else if ([value isKindOfClass:[NSNumber class]]) { | 417 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; |
| 408 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { | 418 } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { |
| 419 if ([value isKindOfClass:[NSNumber class]]) { | |
| 409 data.action = | 420 data.action = |
| 410 [value boolValue] ? ui::AX_ACTION_FOCUS : ui::AX_ACTION_BLUR; | 421 [value boolValue] ? ui::AX_ACTION_FOCUS : ui::AX_ACTION_BLUR; |
| 411 } | 422 } |
| 412 } | 423 } |
| 413 | 424 |
| 425 // Set type-specific information as necessary for actions set above. | |
| 426 if ([value isKindOfClass:[NSString class]]) { | |
|
tapted
2017/01/12 16:33:55
nit: no curlies needed
Patti Lor
2017/01/13 05:48:38
Done.
| |
| 427 data.value = base::SysNSStringToUTF16(value); | |
| 428 } | |
| 429 | |
| 414 if (data.action != ui::AX_ACTION_NONE) | 430 if (data.action != ui::AX_ACTION_NONE) |
| 415 node_->GetDelegate()->AccessibilityPerformAction(data); | 431 node_->GetDelegate()->AccessibilityPerformAction(data); |
| 416 | 432 |
| 417 // TODO(patricialor): Plumb through all the other writable attributes as | 433 // TODO(patricialor): Plumb through all the other writable attributes as |
| 418 // specified in accessibilityIsAttributeSettable. | 434 // specified in accessibilityIsAttributeSettable. |
| 419 } | 435 } |
| 420 | 436 |
| 421 - (id)accessibilityAttributeValue:(NSString*)attribute { | 437 - (id)accessibilityAttributeValue:(NSString*)attribute { |
| 422 SEL selector = NSSelectorFromString(attribute); | 438 SEL selector = NSSelectorFromString(attribute); |
| 423 if ([self respondsToSelector:selector]) | 439 if ([self respondsToSelector:selector]) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 461 ui::AX_STATE_PROTECTED)) | 477 ui::AX_STATE_PROTECTED)) |
| 462 return NSAccessibilitySecureTextFieldSubrole; | 478 return NSAccessibilitySecureTextFieldSubrole; |
| 463 break; | 479 break; |
| 464 default: | 480 default: |
| 465 break; | 481 break; |
| 466 } | 482 } |
| 467 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; | 483 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; |
| 468 } | 484 } |
| 469 | 485 |
| 470 - (NSString*)AXRoleDescription { | 486 - (NSString*)AXRoleDescription { |
| 487 switch (node_->GetData().role) { | |
| 488 case ui::AX_ROLE_TAB: | |
| 489 // There is no NSAccessibilityTabRole or similar (AXRadioButton is used | |
| 490 // instead). Do the same as NSTabView and put "tab" in the description. | |
| 491 return [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TAB_ROLE_DESCRIPTION) | |
| 492 lowercaseString]; | |
| 493 default: | |
| 494 break; | |
| 495 } | |
| 471 return NSAccessibilityRoleDescription([self AXRole], [self AXSubrole]); | 496 return NSAccessibilityRoleDescription([self AXRole], [self AXSubrole]); |
| 472 } | 497 } |
| 473 | 498 |
| 474 - (NSValue*)AXSize { | 499 - (NSValue*)AXSize { |
| 475 return [NSValue valueWithSize:self.boundsInScreen.size]; | 500 return [NSValue valueWithSize:self.boundsInScreen.size]; |
| 476 } | 501 } |
| 477 | 502 |
| 478 - (NSString*)AXTitle { | 503 - (NSString*)AXTitle { |
| 479 return [self getStringAttribute:ui::AX_ATTR_NAME]; | 504 return [self getStringAttribute:ui::AX_ATTR_NAME]; |
| 480 } | 505 } |
| 481 | 506 |
| 482 - (NSString*)AXValue { | 507 - (id)AXValue { |
| 508 if (node_->GetData().role == ui::AX_ROLE_TAB) | |
| 509 return [self AXSelected]; | |
| 483 return [self getStringAttribute:ui::AX_ATTR_VALUE]; | 510 return [self getStringAttribute:ui::AX_ATTR_VALUE]; |
| 484 } | 511 } |
| 485 | 512 |
| 486 - (NSValue*)AXEnabled { | 513 - (NSValue*)AXEnabled { |
| 487 return [NSNumber | 514 return [NSNumber |
| 488 numberWithBool:!ui::AXNodeData::IsFlagSet(node_->GetData().state, | 515 numberWithBool:!ui::AXNodeData::IsFlagSet(node_->GetData().state, |
| 489 ui::AX_STATE_DISABLED)]; | 516 ui::AX_STATE_DISABLED)]; |
| 490 } | 517 } |
| 491 | 518 |
| 492 - (NSValue*)AXFocused { | 519 - (NSValue*)AXFocused { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 543 int beginSelectionIndex = (end > start && !isReversed) ? start : end; | 570 int beginSelectionIndex = (end > start && !isReversed) ? start : end; |
| 544 return [NSValue | 571 return [NSValue |
| 545 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))]; | 572 valueWithRange:NSMakeRange(beginSelectionIndex, abs(end - start))]; |
| 546 } | 573 } |
| 547 | 574 |
| 548 - (NSValue*)AXVisibleCharacterRange { | 575 - (NSValue*)AXVisibleCharacterRange { |
| 549 return [NSValue | 576 return [NSValue |
| 550 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])]; | 577 valueWithRange:NSMakeRange(0, [[self AXNumberOfCharacters] intValue])]; |
| 551 } | 578 } |
| 552 | 579 |
| 580 // Misc NSAccessibility attributes. | |
| 581 | |
| 582 - (NSNumber*)AXSelected { | |
| 583 return [NSNumber | |
| 584 numberWithBool:node_->GetData().HasStateFlag(ui::AX_STATE_SELECTED)]; | |
| 585 } | |
| 586 | |
| 553 @end | 587 @end |
| 554 | 588 |
| 555 namespace ui { | 589 namespace ui { |
| 556 | 590 |
| 557 // static | 591 // static |
| 558 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { | 592 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { |
| 559 AXPlatformNodeBase* node = new AXPlatformNodeMac(); | 593 AXPlatformNodeBase* node = new AXPlatformNodeMac(); |
| 560 node->Init(delegate); | 594 node->Init(delegate); |
| 561 return node; | 595 return node; |
| 562 } | 596 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 596 } | 630 } |
| 597 NotifyMacEvent(native_node_, event_type); | 631 NotifyMacEvent(native_node_, event_type); |
| 598 } | 632 } |
| 599 | 633 |
| 600 int AXPlatformNodeMac::GetIndexInParent() { | 634 int AXPlatformNodeMac::GetIndexInParent() { |
| 601 // TODO(dmazzoni): implement this. http://crbug.com/396137 | 635 // TODO(dmazzoni): implement this. http://crbug.com/396137 |
| 602 return -1; | 636 return -1; |
| 603 } | 637 } |
| 604 | 638 |
| 605 } // namespace ui | 639 } // namespace ui |
| OLD | NEW |