| 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 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 // Returns AXValue, or nil if AXValue isn't an NSString. |
| 234 - (NSString*)getAXValueAsString; |
| 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 Loading... |
| 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*)getAXValueAsString { |
| 293 id value = [self AXValue]; |
| 294 return [value isKindOfClass:[NSString class]] ? value : nil; |
| 295 } |
| 296 |
| 290 // NSAccessibility informal protocol implementation. | 297 // NSAccessibility informal protocol implementation. |
| 291 | 298 |
| 292 - (BOOL)accessibilityIsIgnored { | 299 - (BOOL)accessibilityIsIgnored { |
| 293 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] || | 300 return [[self AXRole] isEqualToString:NSAccessibilityUnknownRole] || |
| 294 node_->GetData().HasState(ui::AX_STATE_INVISIBLE); | 301 node_->GetData().HasState(ui::AX_STATE_INVISIBLE); |
| 295 } | 302 } |
| 296 | 303 |
| 297 - (id)accessibilityHitTest:(NSPoint)point { | 304 - (id)accessibilityHitTest:(NSPoint)point { |
| 298 for (AXPlatformNodeCocoa* child in [self AXChildren]) { | 305 for (AXPlatformNodeCocoa* child in [self AXChildren]) { |
| 299 if (![child accessibilityIsIgnored] && | 306 if (![child accessibilityIsIgnored] && |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 NSAccessibilityEnabledAttribute, | 363 NSAccessibilityEnabledAttribute, |
| 357 NSAccessibilityFocusedAttribute, | 364 NSAccessibilityFocusedAttribute, |
| 358 NSAccessibilityHelpAttribute, | 365 NSAccessibilityHelpAttribute, |
| 359 NSAccessibilityTopLevelUIElementAttribute, | 366 NSAccessibilityTopLevelUIElementAttribute, |
| 360 NSAccessibilityWindowAttribute, | 367 NSAccessibilityWindowAttribute, |
| 361 ]; | 368 ]; |
| 362 | 369 |
| 363 // Attributes required for user-editable controls. | 370 // Attributes required for user-editable controls. |
| 364 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ]; | 371 NSArray* const kValueAttributes = @[ NSAccessibilityValueAttribute ]; |
| 365 | 372 |
| 366 // Attributes required for unprotected textfields. | 373 // Attributes required for unprotected textfields and labels. |
| 367 NSArray* const kUnprotectedTextfieldAttributes = @[ | 374 NSArray* const kUnprotectedTextAttributes = @[ |
| 368 NSAccessibilityInsertionPointLineNumberAttribute, | 375 NSAccessibilityInsertionPointLineNumberAttribute, |
| 369 NSAccessibilityNumberOfCharactersAttribute, | 376 NSAccessibilityNumberOfCharactersAttribute, |
| 370 NSAccessibilitySelectedTextAttribute, | 377 NSAccessibilitySelectedTextAttribute, |
| 371 NSAccessibilitySelectedTextRangeAttribute, | 378 NSAccessibilitySelectedTextRangeAttribute, |
| 372 NSAccessibilityVisibleCharacterRangeAttribute, | 379 NSAccessibilityVisibleCharacterRangeAttribute, |
| 373 ]; | 380 ]; |
| 374 | 381 |
| 375 // Required for all textfields, including protected ones. | 382 // Required for all text, including protected textfields. |
| 376 NSString* const kTextfieldAttributes = | 383 NSString* const kTextAttributes = NSAccessibilityPlaceholderValueAttribute; |
| 377 NSAccessibilityPlaceholderValueAttribute; | |
| 378 | 384 |
| 379 base::scoped_nsobject<NSMutableArray> axAttributes( | 385 base::scoped_nsobject<NSMutableArray> axAttributes( |
| 380 [[NSMutableArray alloc] init]); | 386 [[NSMutableArray alloc] init]); |
| 381 | 387 |
| 382 [axAttributes addObjectsFromArray:kAllRoleAttributes]; | 388 [axAttributes addObjectsFromArray:kAllRoleAttributes]; |
| 389 |
| 383 switch (node_->GetData().role) { | 390 switch (node_->GetData().role) { |
| 384 case ui::AX_ROLE_TEXT_FIELD: | 391 case ui::AX_ROLE_TEXT_FIELD: |
| 385 [axAttributes addObject:kTextfieldAttributes]; | 392 case ui::AX_ROLE_STATIC_TEXT: |
| 393 [axAttributes addObject:kTextAttributes]; |
| 386 if (!node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | 394 if (!node_->GetData().HasState(ui::AX_STATE_PROTECTED)) |
| 387 [axAttributes addObjectsFromArray:kUnprotectedTextfieldAttributes]; | 395 [axAttributes addObjectsFromArray:kUnprotectedTextAttributes]; |
| 388 // Fallthrough. | 396 // Fallthrough. |
| 389 case ui::AX_ROLE_CHECK_BOX: | 397 case ui::AX_ROLE_CHECK_BOX: |
| 390 case ui::AX_ROLE_COMBO_BOX: | 398 case ui::AX_ROLE_COMBO_BOX: |
| 391 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: | 399 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: |
| 392 case ui::AX_ROLE_MENU_ITEM_RADIO: | 400 case ui::AX_ROLE_MENU_ITEM_RADIO: |
| 393 case ui::AX_ROLE_RADIO_BUTTON: | 401 case ui::AX_ROLE_RADIO_BUTTON: |
| 394 case ui::AX_ROLE_SEARCH_BOX: | 402 case ui::AX_ROLE_SEARCH_BOX: |
| 395 case ui::AX_ROLE_SLIDER: | 403 case ui::AX_ROLE_SLIDER: |
| 396 case ui::AX_ROLE_SLIDER_THUMB: | 404 case ui::AX_ROLE_SLIDER_THUMB: |
| 397 case ui::AX_ROLE_TOGGLE_BUTTON: | 405 case ui::AX_ROLE_TOGGLE_BUTTON: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 412 // writable attribute will only appear as such if the accessibility element | 420 // writable attribute will only appear as such if the accessibility element |
| 413 // has a value set for that attribute. | 421 // has a value set for that attribute. |
| 414 if ([attributeName | 422 if ([attributeName |
| 415 isEqualToString:NSAccessibilitySelectedChildrenAttribute] || | 423 isEqualToString:NSAccessibilitySelectedChildrenAttribute] || |
| 416 [attributeName | 424 [attributeName |
| 417 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { | 425 isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { |
| 418 return NO; | 426 return NO; |
| 419 } | 427 } |
| 420 | 428 |
| 421 if ([attributeName isEqualToString:NSAccessibilityValueAttribute]) { | 429 if ([attributeName isEqualToString:NSAccessibilityValueAttribute]) { |
| 422 // NSSecureTextField doesn't allow values to be edited (despite showing up | |
| 423 // as editable), match its behavior. | |
| 424 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | |
| 425 return NO; | |
| 426 // Since tabs use the Radio Button role on Mac, the standard way to set | 430 // Since tabs use the Radio Button role on Mac, the standard way to set |
| 427 // them is via the value attribute rather than the selected attribute. | 431 // them is via the value attribute rather than the selected attribute. |
| 428 if (node_->GetData().role == ui::AX_ROLE_TAB) | 432 if (node_->GetData().role == ui::AX_ROLE_TAB) |
| 429 return !node_->GetData().HasState(ui::AX_STATE_SELECTED); | 433 return !node_->GetData().HasState(ui::AX_STATE_SELECTED); |
| 430 } | 434 } |
| 431 | 435 |
| 432 if ([attributeName isEqualToString:NSAccessibilityValueAttribute] || | 436 if ([attributeName isEqualToString:NSAccessibilityValueAttribute] || |
| 433 [attributeName isEqualToString:NSAccessibilitySelectedTextAttribute] || | 437 [attributeName isEqualToString:NSAccessibilitySelectedTextAttribute] || |
| 434 [attributeName | 438 [attributeName |
| 435 isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { | 439 isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { |
| 436 return !node_->GetData().HasState(ui::AX_STATE_READ_ONLY); | 440 return !node_->GetData().HasState(ui::AX_STATE_READ_ONLY); |
| 437 } | 441 } |
| 438 | 442 |
| 439 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) { | 443 if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) { |
| 440 return node_->GetData().HasState(ui::AX_STATE_FOCUSABLE); | 444 return node_->GetData().HasState(ui::AX_STATE_FOCUSABLE); |
| 441 } | 445 } |
| 442 | 446 |
| 443 // TODO(patricialor): Add callbacks for updating the above attributes except | 447 // TODO(patricialor): Add callbacks for updating the above attributes except |
| 444 // NSAccessibilityValueAttribute and return YES. | 448 // NSAccessibilityValueAttribute and return YES. |
| 445 return NO; | 449 return NO; |
| 446 } | 450 } |
| 447 | 451 |
| 448 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { | 452 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { |
| 449 ui::AXActionData data; | 453 ui::AXActionData data; |
| 450 | 454 |
| 451 // Check for attributes first. Only the |data.action| should be set here - any | 455 // Check for attributes first. Only the |data.action| should be set here - any |
| 452 // type-specific information, if needed, should be set below. | 456 // type-specific information, if needed, should be set below. |
| 453 if ([attribute isEqualToString:NSAccessibilityValueAttribute] && | 457 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { |
| 454 !node_->GetData().HasState(ui::AX_STATE_PROTECTED)) { | |
| 455 data.action = node_->GetData().role == ui::AX_ROLE_TAB | 458 data.action = node_->GetData().role == ui::AX_ROLE_TAB |
| 456 ? ui::AX_ACTION_SET_SELECTION | 459 ? ui::AX_ACTION_SET_SELECTION |
| 457 : ui::AX_ACTION_SET_VALUE; | 460 : ui::AX_ACTION_SET_VALUE; |
| 458 } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { | 461 } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { |
| 459 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; | 462 data.action = ui::AX_ACTION_REPLACE_SELECTED_TEXT; |
| 460 } else if ([attribute | 463 } else if ([attribute |
| 461 isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { | 464 isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { |
| 462 data.action = ui::AX_ACTION_SET_SELECTION; | 465 data.action = ui::AX_ACTION_SET_SELECTION; |
| 463 } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { | 466 } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { |
| 464 if ([value isKindOfClass:[NSNumber class]]) { | 467 if ([value isKindOfClass:[NSNumber class]]) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 break; | 527 break; |
| 525 } | 528 } |
| 526 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; | 529 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:role]; |
| 527 } | 530 } |
| 528 | 531 |
| 529 - (NSString*)AXHelp { | 532 - (NSString*)AXHelp { |
| 530 return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION]; | 533 return [self getStringAttribute:ui::AX_ATTR_DESCRIPTION]; |
| 531 } | 534 } |
| 532 | 535 |
| 533 - (id)AXValue { | 536 - (id)AXValue { |
| 534 if (node_->GetData().role == ui::AX_ROLE_TAB) | 537 switch (node_->GetData().role) { |
| 535 return [self AXSelected]; | 538 case ui::AX_ROLE_TAB: |
| 539 return [self AXSelected]; |
| 540 case ui::AX_ROLE_STATIC_TEXT: |
| 541 return [self AXTitle]; |
| 542 default: |
| 543 break; |
| 544 } |
| 536 return [self getStringAttribute:ui::AX_ATTR_VALUE]; | 545 return [self getStringAttribute:ui::AX_ATTR_VALUE]; |
| 537 } | 546 } |
| 538 | 547 |
| 539 - (NSNumber*)AXEnabled { | 548 - (NSNumber*)AXEnabled { |
| 540 return @(!node_->GetData().HasState(ui::AX_STATE_DISABLED)); | 549 return @(!node_->GetData().HasState(ui::AX_STATE_DISABLED)); |
| 541 } | 550 } |
| 542 | 551 |
| 543 - (NSNumber*)AXFocused { | 552 - (NSNumber*)AXFocused { |
| 544 if (node_->GetData().HasState(ui::AX_STATE_FOCUSABLE)) | 553 if (node_->GetData().HasState(ui::AX_STATE_FOCUSABLE)) |
| 545 return | 554 return |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 589 return @(node_->GetData().HasState(ui::AX_STATE_SELECTED)); | 598 return @(node_->GetData().HasState(ui::AX_STATE_SELECTED)); |
| 590 } | 599 } |
| 591 | 600 |
| 592 - (NSString*)AXPlaceholderValue { | 601 - (NSString*)AXPlaceholderValue { |
| 593 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER]; | 602 return [self getStringAttribute:ui::AX_ATTR_PLACEHOLDER]; |
| 594 } | 603 } |
| 595 | 604 |
| 596 // Text-specific attributes. | 605 // Text-specific attributes. |
| 597 | 606 |
| 598 - (NSString*)AXSelectedText { | 607 - (NSString*)AXSelectedText { |
| 599 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | |
| 600 return nil; | |
| 601 | |
| 602 NSRange selectedTextRange; | 608 NSRange selectedTextRange; |
| 603 [[self AXSelectedTextRange] getValue:&selectedTextRange]; | 609 [[self AXSelectedTextRange] getValue:&selectedTextRange]; |
| 604 return [[self AXValue] substringWithRange:selectedTextRange]; | 610 return [[self getAXValueAsString] substringWithRange:selectedTextRange]; |
| 605 } | 611 } |
| 606 | 612 |
| 607 - (NSValue*)AXSelectedTextRange { | 613 - (NSValue*)AXSelectedTextRange { |
| 608 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | 614 // Selection might not be supported. Return (NSRange){0,0} in that case. |
| 609 return nil; | 615 int start = 0, end = 0; |
| 610 | |
| 611 int textDir, start, end; | |
| 612 node_->GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, &textDir); | |
| 613 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start); | 616 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, &start); |
| 614 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end); | 617 node_->GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &end); |
| 615 // NSRange cannot represent the direction the text was selected in, so make | 618 |
| 616 // sure the correct selection index is used when creating a new range, taking | 619 // NSRange cannot represent the direction the text was selected in. |
| 617 // into account the textfield text direction as well. | 620 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 } | 621 } |
| 623 | 622 |
| 624 - (NSNumber*)AXNumberOfCharacters { | 623 - (NSNumber*)AXNumberOfCharacters { |
| 625 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | 624 return @([[self getAXValueAsString] length]); |
| 626 return nil; | |
| 627 return @([[self AXValue] length]); | |
| 628 } | 625 } |
| 629 | 626 |
| 630 - (NSValue*)AXVisibleCharacterRange { | 627 - (NSValue*)AXVisibleCharacterRange { |
| 631 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | 628 return [NSValue valueWithRange:{0, [[self getAXValueAsString] length]}]; |
| 632 return nil; | |
| 633 return [NSValue valueWithRange:{0, [[self AXNumberOfCharacters] intValue]}]; | |
| 634 } | 629 } |
| 635 | 630 |
| 636 - (NSNumber*)AXInsertionPointLineNumber { | 631 - (NSNumber*)AXInsertionPointLineNumber { |
| 637 if (node_->GetData().HasState(ui::AX_STATE_PROTECTED)) | |
| 638 return nil; | |
| 639 // Multiline is not supported on views. | 632 // Multiline is not supported on views. |
| 640 return @0; | 633 return @0; |
| 641 } | 634 } |
| 642 | 635 |
| 643 @end | 636 @end |
| 644 | 637 |
| 645 namespace ui { | 638 namespace ui { |
| 646 | 639 |
| 647 // static | 640 // static |
| 648 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { | 641 AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 694 } | 687 } |
| 695 NotifyMacEvent(native_node_, event_type); | 688 NotifyMacEvent(native_node_, event_type); |
| 696 } | 689 } |
| 697 | 690 |
| 698 int AXPlatformNodeMac::GetIndexInParent() { | 691 int AXPlatformNodeMac::GetIndexInParent() { |
| 699 // TODO(dmazzoni): implement this. http://crbug.com/396137 | 692 // TODO(dmazzoni): implement this. http://crbug.com/396137 |
| 700 return -1; | 693 return -1; |
| 701 } | 694 } |
| 702 | 695 |
| 703 } // namespace ui | 696 } // namespace ui |
| OLD | NEW |