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