| 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" |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 } | 304 } |
| 305 | 305 |
| 306 - (NSString*)getAXValueAsString { | 306 - (NSString*)getAXValueAsString { |
| 307 id value = [self AXValue]; | 307 id value = [self AXValue]; |
| 308 return [value isKindOfClass:[NSString class]] ? value : nil; | 308 return [value isKindOfClass:[NSString class]] ? value : nil; |
| 309 } | 309 } |
| 310 | 310 |
| 311 // NSAccessibility informal protocol implementation. | 311 // NSAccessibility informal protocol implementation. |
| 312 | 312 |
| 313 - (BOOL)accessibilityIsIgnored { | 313 - (BOOL)accessibilityIsIgnored { |
| 314 if (!node_) |
| 315 return YES; |
| 316 |
| 314 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] || | 317 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] || |
| 315 node_->GetData().HasState(ui::AX_STATE_INVISIBLE); | 318 node_->GetData().HasState(ui::AX_STATE_INVISIBLE); |
| 316 } | 319 } |
| 317 | 320 |
| 318 - (id)accessibilityHitTest:(NSPoint)point { | 321 - (id)accessibilityHitTest:(NSPoint)point { |
| 319 for (AXPlatformNodeCocoa* child in [self AXChildren]) { | 322 for (AXPlatformNodeCocoa* child in [self AXChildren]) { |
| 320 if (![child accessibilityIsIgnored] && | 323 if (![child accessibilityIsIgnored] && |
| 321 NSPointInRect(point, child.boundsInScreen)) { | 324 NSPointInRect(point, child.boundsInScreen)) { |
| 322 return [child accessibilityHitTest:point]; | 325 return [child accessibilityHitTest:point]; |
| 323 } | 326 } |
| 324 } | 327 } |
| 325 return NSAccessibilityUnignoredAncestor(self); | 328 return NSAccessibilityUnignoredAncestor(self); |
| 326 } | 329 } |
| 327 | 330 |
| 328 - (BOOL)accessibilityNotifiesWhenDestroyed { | 331 - (BOOL)accessibilityNotifiesWhenDestroyed { |
| 329 return YES; | 332 return YES; |
| 330 } | 333 } |
| 331 | 334 |
| 332 - (id)accessibilityFocusedUIElement { | 335 - (id)accessibilityFocusedUIElement { |
| 333 return node_->GetDelegate()->GetFocus(); | 336 return node_ ? node_->GetDelegate()->GetFocus() : nil; |
| 334 } | 337 } |
| 335 | 338 |
| 336 - (NSArray*)accessibilityActionNames { | 339 - (NSArray*)accessibilityActionNames { |
| 340 if (!node_) |
| 341 return @[]; |
| 342 |
| 337 base::scoped_nsobject<NSMutableArray> axActions( | 343 base::scoped_nsobject<NSMutableArray> axActions( |
| 338 [[NSMutableArray alloc] init]); | 344 [[NSMutableArray alloc] init]); |
| 339 | 345 |
| 340 const ui::AXNodeData& data = node_->GetData(); | 346 const ui::AXNodeData& data = node_->GetData(); |
| 341 const ActionList& action_list = GetActionList(); | 347 const ActionList& action_list = GetActionList(); |
| 342 | 348 |
| 343 // VoiceOver expects the "press" action to be first. Note that some roles | 349 // VoiceOver expects the "press" action to be first. Note that some roles |
| 344 // should be given a press action implicitly. | 350 // should be given a press action implicitly. |
| 345 DCHECK([action_list[0].second isEqualToString:NSAccessibilityPressAction]); | 351 DCHECK([action_list[0].second isEqualToString:NSAccessibilityPressAction]); |
| 346 for (const auto item : action_list) { | 352 for (const auto item : action_list) { |
| 347 if (data.HasAction(item.first) || HasImplicitAction(data, item.first)) | 353 if (data.HasAction(item.first) || HasImplicitAction(data, item.first)) |
| 348 [axActions addObject:item.second]; | 354 [axActions addObject:item.second]; |
| 349 } | 355 } |
| 350 | 356 |
| 351 if (AlsoUseShowMenuActionForDefaultAction(data)) | 357 if (AlsoUseShowMenuActionForDefaultAction(data)) |
| 352 [axActions addObject:NSAccessibilityShowMenuAction]; | 358 [axActions addObject:NSAccessibilityShowMenuAction]; |
| 353 | 359 |
| 354 return axActions.autorelease(); | 360 return axActions.autorelease(); |
| 355 } | 361 } |
| 356 | 362 |
| 357 - (void)accessibilityPerformAction:(NSString*)action { | 363 - (void)accessibilityPerformAction:(NSString*)action { |
| 358 DCHECK([[self accessibilityActionNames] containsObject:action]); | 364 // Actions are performed asynchronously, so it's always possible for an object |
| 365 // to change its mind after previously reporting an action as available. |
| 366 if (![[self accessibilityActionNames] containsObject:action]) |
| 367 return; |
| 368 |
| 359 ui::AXActionData data; | 369 ui::AXActionData data; |
| 360 if ([action isEqualToString:NSAccessibilityShowMenuAction] && | 370 if ([action isEqualToString:NSAccessibilityShowMenuAction] && |
| 361 AlsoUseShowMenuActionForDefaultAction(node_->GetData())) { | 371 AlsoUseShowMenuActionForDefaultAction(node_->GetData())) { |
| 362 data.action = ui::AX_ACTION_DO_DEFAULT; | 372 data.action = ui::AX_ACTION_DO_DEFAULT; |
| 363 } else { | 373 } else { |
| 364 for (const ActionList::value_type& entry : GetActionList()) { | 374 for (const ActionList::value_type& entry : GetActionList()) { |
| 365 if ([action isEqualToString:entry.second]) { | 375 if ([action isEqualToString:entry.second]) { |
| 366 data.action = entry.first; | 376 data.action = entry.first; |
| 367 break; | 377 break; |
| 368 } | 378 } |
| 369 } | 379 } |
| 370 } | 380 } |
| 371 | 381 |
| 372 // Note ui::AX_ACTIONs which are just overwriting an accessibility attribute | 382 // Note ui::AX_ACTIONs which are just overwriting an accessibility attribute |
| 373 // are already implemented in -accessibilitySetValue:forAttribute:, so ignore | 383 // are already implemented in -accessibilitySetValue:forAttribute:, so ignore |
| 374 // those here. | 384 // those here. |
| 375 | 385 |
| 376 if (data.action != ui::AX_ACTION_NONE) | 386 if (data.action != ui::AX_ACTION_NONE) |
| 377 node_->GetDelegate()->AccessibilityPerformAction(data); | 387 node_->GetDelegate()->AccessibilityPerformAction(data); |
| 378 } | 388 } |
| 379 | 389 |
| 380 - (NSArray*)accessibilityAttributeNames { | 390 - (NSArray*)accessibilityAttributeNames { |
| 391 if (!node_) |
| 392 return @[]; |
| 393 |
| 381 // These attributes are required on all accessibility objects. | 394 // These attributes are required on all accessibility objects. |
| 382 NSArray* const kAllRoleAttributes = @[ | 395 NSArray* const kAllRoleAttributes = @[ |
| 383 NSAccessibilityChildrenAttribute, | 396 NSAccessibilityChildrenAttribute, |
| 384 NSAccessibilityParentAttribute, | 397 NSAccessibilityParentAttribute, |
| 385 NSAccessibilityPositionAttribute, | 398 NSAccessibilityPositionAttribute, |
| 386 NSAccessibilityRoleAttribute, | 399 NSAccessibilityRoleAttribute, |
| 387 NSAccessibilitySizeAttribute, | 400 NSAccessibilitySizeAttribute, |
| 388 NSAccessibilitySubroleAttribute, | 401 NSAccessibilitySubroleAttribute, |
| 389 | 402 |
| 390 // Title is required for most elements. Cocoa asks for the value even if it | 403 // Title is required for most elements. Cocoa asks for the value even if it |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 break; | 453 break; |
| 441 // TODO(tapted): Add additional attributes based on role. | 454 // TODO(tapted): Add additional attributes based on role. |
| 442 default: | 455 default: |
| 443 break; | 456 break; |
| 444 } | 457 } |
| 445 return axAttributes.autorelease(); | 458 return axAttributes.autorelease(); |
| 446 } | 459 } |
| 447 | 460 |
| 448 - (NSArray*)accessibilityParameterizedAttributeNames { | 461 - (NSArray*)accessibilityParameterizedAttributeNames { |
| 449 if (!node_) | 462 if (!node_) |
| 450 return nil; | 463 return @[]; |
| 451 | 464 |
| 452 static NSArray* const kSelectableTextAttributes = [@[ | 465 static NSArray* const kSelectableTextAttributes = [@[ |
| 453 NSAccessibilityLineForIndexParameterizedAttribute, | 466 NSAccessibilityLineForIndexParameterizedAttribute, |
| 454 NSAccessibilityRangeForLineParameterizedAttribute, | 467 NSAccessibilityRangeForLineParameterizedAttribute, |
| 455 NSAccessibilityStringForRangeParameterizedAttribute, | 468 NSAccessibilityStringForRangeParameterizedAttribute, |
| 456 NSAccessibilityRangeForPositionParameterizedAttribute, | 469 NSAccessibilityRangeForPositionParameterizedAttribute, |
| 457 NSAccessibilityRangeForIndexParameterizedAttribute, | 470 NSAccessibilityRangeForIndexParameterizedAttribute, |
| 458 NSAccessibilityBoundsForRangeParameterizedAttribute, | 471 NSAccessibilityBoundsForRangeParameterizedAttribute, |
| 459 NSAccessibilityRTFForRangeParameterizedAttribute, | 472 NSAccessibilityRTFForRangeParameterizedAttribute, |
| 460 NSAccessibilityStyleRangeForIndexParameterizedAttribute, | 473 NSAccessibilityStyleRangeForIndexParameterizedAttribute, |
| 461 NSAccessibilityAttributedStringForRangeParameterizedAttribute, | 474 NSAccessibilityAttributedStringForRangeParameterizedAttribute, |
| 462 ] retain]; | 475 ] retain]; |
| 463 | 476 |
| 464 switch (node_->GetData().role) { | 477 switch (node_->GetData().role) { |
| 465 case ui::AX_ROLE_TEXT_FIELD: | 478 case ui::AX_ROLE_TEXT_FIELD: |
| 466 case ui::AX_ROLE_STATIC_TEXT: | 479 case ui::AX_ROLE_STATIC_TEXT: |
| 467 return kSelectableTextAttributes; | 480 return kSelectableTextAttributes; |
| 468 default: | 481 default: |
| 469 break; | 482 break; |
| 470 } | 483 } |
| 471 return nil; | 484 return nil; |
| 472 } | 485 } |
| 473 | 486 |
| 474 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName { | 487 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName { |
| 488 if (!node_) |
| 489 return NO; |
| 490 |
| 475 if (node_->GetData().HasState(ui::AX_STATE_DISABLED)) | 491 if (node_->GetData().HasState(ui::AX_STATE_DISABLED)) |
| 476 return NO; | 492 return NO; |
| 477 | 493 |
| 478 // Allow certain attributes to be written via an accessibility client. A | 494 // Allow certain attributes to be written via an accessibility client. A |
| 479 // writable attribute will only appear as such if the accessibility element | 495 // writable attribute will only appear as such if the accessibility element |
| 480 // has a value set for that attribute. | 496 // has a value set for that attribute. |
| 481 if ([attributeName | 497 if ([attributeName |
| 482 isEqualToString:NSAccessibilitySelectedChildrenAttribute] || | 498 isEqualToString:NSAccessibilitySelectedChildrenAttribute] || |
| 483 [attributeName | 499 [attributeName |
| 484 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { | 500 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 502 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) { | 518 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) { |
| 503 return node_->GetData().HasState(ui::AX_STATE_FOCUSABLE); | 519 return node_->GetData().HasState(ui::AX_STATE_FOCUSABLE); |
| 504 } | 520 } |
| 505 | 521 |
| 506 // TODO(patricialor): Add callbacks for updating the above attributes except | 522 // TODO(patricialor): Add callbacks for updating the above attributes except |
| 507 // NSAccessibilityValueAttribute and return YES. | 523 // NSAccessibilityValueAttribute and return YES. |
| 508 return NO; | 524 return NO; |
| 509 } | 525 } |
| 510 | 526 |
| 511 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { | 527 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { |
| 528 if (!node_) |
| 529 return; |
| 530 |
| 512 ui::AXActionData data; | 531 ui::AXActionData data; |
| 513 | 532 |
| 514 // Check for attributes first. Only the |data.action| should be set here - any | 533 // Check for attributes first. Only the |data.action| should be set here - any |
| 515 // type-specific information, if needed, should be set below. | 534 // type-specific information, if needed, should be set below. |
| 516 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { | 535 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { |
| 517 data.action = node_->GetData().role == ui::AX_ROLE_TAB | 536 data.action = node_->GetData().role == ui::AX_ROLE_TAB |
| 518 ? ui::AX_ACTION_SET_SELECTION | 537 ? ui::AX_ACTION_SET_SELECTION |
| 519 : ui::AX_ACTION_SET_VALUE; | 538 : ui::AX_ACTION_SET_VALUE; |
| 520 } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { | 539 } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { |
| 521 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; | 540 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 540 } | 559 } |
| 541 | 560 |
| 542 if (data.action != ui::AX_ACTION_NONE) | 561 if (data.action != ui::AX_ACTION_NONE) |
| 543 node_->GetDelegate()->AccessibilityPerformAction(data); | 562 node_->GetDelegate()->AccessibilityPerformAction(data); |
| 544 | 563 |
| 545 // TODO(patricialor): Plumb through all the other writable attributes as | 564 // TODO(patricialor): Plumb through all the other writable attributes as |
| 546 // specified in accessibilityIsAttributeSettable. | 565 // specified in accessibilityIsAttributeSettable. |
| 547 } | 566 } |
| 548 | 567 |
| 549 - (id)accessibilityAttributeValue:(NSString*)attribute { | 568 - (id)accessibilityAttributeValue:(NSString*)attribute { |
| 569 if (!node_) |
| 570 return nil; // Return nil when detached. Even for AXRole. |
| 571 |
| 550 SEL selector = NSSelectorFromString(attribute); | 572 SEL selector = NSSelectorFromString(attribute); |
| 551 if ([self respondsToSelector:selector]) | 573 if ([self respondsToSelector:selector]) |
| 552 return [self performSelector:selector]; | 574 return [self performSelector:selector]; |
| 553 return nil; | 575 return nil; |
| 554 } | 576 } |
| 555 | 577 |
| 556 - (id)accessibilityAttributeValue:(NSString*)attribute | 578 - (id)accessibilityAttributeValue:(NSString*)attribute |
| 557 forParameter:(id)parameter { | 579 forParameter:(id)parameter { |
| 580 if (!node_) |
| 581 return nil; |
| 582 |
| 558 SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]); | 583 SEL selector = NSSelectorFromString([attribute stringByAppendingString:@":"]); |
| 559 if ([self respondsToSelector:selector]) | 584 if ([self respondsToSelector:selector]) |
| 560 return [self performSelector:selector withObject:parameter]; | 585 return [self performSelector:selector withObject:parameter]; |
| 561 return nil; | 586 return nil; |
| 562 } | 587 } |
| 563 | 588 |
| 564 // NSAccessibility attributes. Order them according to | 589 // NSAccessibility attributes. Order them according to |
| 565 // NSAccessibilityConstants.h, or see https://crbug.com/678898. | 590 // NSAccessibilityConstants.h, or see https://crbug.com/678898. |
| 566 | 591 |
| 567 - (NSString*)AXRole { | 592 - (NSString*)AXRole { |
| 568 if (!node_) | 593 if (!node_) |
| 569 return nil; | 594 return nil; |
| 595 |
| 570 return [[self class] nativeRoleFromAXRole:node_->GetData().role]; | 596 return [[self class] nativeRoleFromAXRole:node_->GetData().role]; |
| 571 } | 597 } |
| 572 | 598 |
| 573 - (NSString*)AXRoleDescription { | 599 - (NSString*)AXRoleDescription { |
| 574 switch (node_->GetData().role) { | 600 switch (node_->GetData().role) { |
| 575 case ui::AX_ROLE_TAB: | 601 case ui::AX_ROLE_TAB: |
| 576 // There is no NSAccessibilityTabRole or similar (AXRadioButton is used | 602 // There is no NSAccessibilityTabRole or similar (AXRadioButton is used |
| 577 // instead). Do the same as NSTabView and put "tab" in the description. | 603 // instead). Do the same as NSTabView and put "tab" in the description. |
| 578 return [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TAB_ROLE_DESCRIPTION) | 604 return [l10n_util::GetNSStringWithFixup(IDS_ACCNAME_TAB_ROLE_DESCRIPTION) |
| 579 lowercaseString]; | 605 lowercaseString]; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 627 } | 653 } |
| 628 | 654 |
| 629 - (id)AXParent { | 655 - (id)AXParent { |
| 630 if (!node_) | 656 if (!node_) |
| 631 return nil; | 657 return nil; |
| 632 return NSAccessibilityUnignoredAncestor(node_->GetParent()); | 658 return NSAccessibilityUnignoredAncestor(node_->GetParent()); |
| 633 } | 659 } |
| 634 | 660 |
| 635 - (NSArray*)AXChildren { | 661 - (NSArray*)AXChildren { |
| 636 if (!node_) | 662 if (!node_) |
| 637 return nil; | 663 return @[]; |
| 664 |
| 638 int count = node_->GetChildCount(); | 665 int count = node_->GetChildCount(); |
| 639 NSMutableArray* children = [NSMutableArray arrayWithCapacity:count]; | 666 NSMutableArray* children = [NSMutableArray arrayWithCapacity:count]; |
| 640 for (int i = 0; i < count; ++i) | 667 for (int i = 0; i < count; ++i) |
| 641 [children addObject:node_->ChildAtIndex(i)]; | 668 [children addObject:node_->ChildAtIndex(i)]; |
| 642 return NSAccessibilityUnignoredChildren(children); | 669 return NSAccessibilityUnignoredChildren(children); |
| 643 } | 670 } |
| 644 | 671 |
| 645 - (id)AXWindow { | 672 - (id)AXWindow { |
| 646 return node_->GetDelegate()->GetTopLevelWidget(); | 673 return node_->GetDelegate()->GetTopLevelWidget(); |
| 647 } | 674 } |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 820 } | 847 } |
| 821 NotifyMacEvent(native_node_, event_type); | 848 NotifyMacEvent(native_node_, event_type); |
| 822 } | 849 } |
| 823 | 850 |
| 824 int AXPlatformNodeMac::GetIndexInParent() { | 851 int AXPlatformNodeMac::GetIndexInParent() { |
| 825 // TODO(dmazzoni): implement this. http://crbug.com/396137 | 852 // TODO(dmazzoni): implement this. http://crbug.com/396137 |
| 826 return -1; | 853 return -1; |
| 827 } | 854 } |
| 828 | 855 |
| 829 } // namespace ui | 856 } // namespace ui |
| OLD | NEW |