1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
11 #include "base/basictypes.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/app/strings/grit/content_strings.h"
16 #include "content/browser/accessibility/browser_accessibility_manager.h"
17 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
18 #include "content/public/common/content_client.h"
19 #import "ui/accessibility/platform/ax_platform_node_mac.h"
21 // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
22 // 10.6, and 10.7. It allows accessibility clients to observe events posted on
24 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
27 using content::BrowserAccessibility;
28 using content::BrowserAccessibilityDelegate;
29 using content::BrowserAccessibilityManager;
30 using content::BrowserAccessibilityManagerMac;
31 using content::ContentClient;
32 typedef ui::AXStringAttribute StringAttribute;
36 // Returns an autoreleased copy of the AXNodeData's attribute.
37 NSString* NSStringForStringAttribute(
38 BrowserAccessibility* browserAccessibility,
39 StringAttribute attribute) {
40 return base::SysUTF8ToNSString(
41 browserAccessibility->GetStringAttribute(attribute));
44 // GetState checks the bitmask used in AXNodeData to check
45 // if the given state was set on the accessibility object.
46 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
47 return ((accessibility->GetState() >> state) & 1);
50 // A mapping from an accessibility attribute to its method name.
51 NSDictionary* attributeToMethodNameMap = nil;
55 @implementation BrowserAccessibilityCocoa
61 } attributeToMethodNameContainer[] = {
62 { NSAccessibilityChildrenAttribute, @"children" },
63 { NSAccessibilityColumnsAttribute, @"columns" },
64 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
65 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
66 { NSAccessibilityContentsAttribute, @"contents" },
67 { NSAccessibilityDescriptionAttribute, @"description" },
68 { NSAccessibilityDisclosingAttribute, @"disclosing" },
69 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
70 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
71 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
72 { NSAccessibilityEnabledAttribute, @"enabled" },
73 { NSAccessibilityExpandedAttribute, @"expanded" },
74 { NSAccessibilityFocusedAttribute, @"focused" },
75 { NSAccessibilityHeaderAttribute, @"header" },
76 { NSAccessibilityHelpAttribute, @"help" },
77 { NSAccessibilityIndexAttribute, @"index" },
78 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
79 { NSAccessibilityMaxValueAttribute, @"maxValue" },
80 { NSAccessibilityMinValueAttribute, @"minValue" },
81 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
82 { NSAccessibilityOrientationAttribute, @"orientation" },
83 { NSAccessibilityParentAttribute, @"parent" },
84 { NSAccessibilityPositionAttribute, @"position" },
85 { NSAccessibilityRoleAttribute, @"role" },
86 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
87 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
88 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
89 { NSAccessibilityRowsAttribute, @"rows" },
90 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
91 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
92 { NSAccessibilitySizeAttribute, @"size" },
93 { NSAccessibilitySubroleAttribute, @"subrole" },
94 { NSAccessibilityTabsAttribute, @"tabs" },
95 { NSAccessibilityTitleAttribute, @"title" },
96 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
97 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
98 { NSAccessibilityURLAttribute, @"url" },
99 { NSAccessibilityValueAttribute, @"value" },
100 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
101 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
102 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
103 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
104 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
105 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
106 { NSAccessibilityWindowAttribute, @"window" },
107 { @"AXAccessKey", @"accessKey" },
108 { @"AXARIAAtomic", @"ariaAtomic" },
109 { @"AXARIABusy", @"ariaBusy" },
110 { @"AXARIALive", @"ariaLive" },
111 { @"AXARIARelevant", @"ariaRelevant" },
112 { @"AXDropEffects", @"dropeffect" },
113 { @"AXGrabbed", @"grabbed" },
114 { @"AXInvalid", @"invalid" },
115 { @"AXLoaded", @"loaded" },
116 { @"AXLoadingProgress", @"loadingProgress" },
117 { @"AXPlaceholder", @"placeholder" },
118 { @"AXRequired", @"required" },
119 { @"AXSortDirection", @"sortDirection" },
120 { @"AXVisited", @"visited" },
123 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
124 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
125 sizeof(attributeToMethodNameContainer[0]);
126 for (size_t i = 0; i < numAttributes; ++i) {
127 [dict setObject:attributeToMethodNameContainer[i].methodName
128 forKey:attributeToMethodNameContainer[i].attribute];
130 attributeToMethodNameMap = dict;
134 - (id)initWithObject:(BrowserAccessibility*)accessibility {
135 if ((self = [super init]))
136 browserAccessibility_ = accessibility;
141 if (browserAccessibility_) {
142 NSAccessibilityUnregisterUniqueIdForUIElement(self);
143 browserAccessibility_ = NULL;
147 - (NSString*)accessKey {
148 return NSStringForStringAttribute(
149 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
152 - (NSNumber*)ariaAtomic {
153 bool boolValue = browserAccessibility_->GetBoolAttribute(
154 ui::AX_ATTR_LIVE_ATOMIC);
155 return [NSNumber numberWithBool:boolValue];
158 - (NSNumber*)ariaBusy {
159 return [NSNumber numberWithBool:
160 GetState(browserAccessibility_, ui::AX_STATE_BUSY)];
163 - (NSString*)ariaLive {
164 return NSStringForStringAttribute(
165 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
168 - (NSString*)ariaRelevant {
169 return NSStringForStringAttribute(
170 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
173 // Returns an array of BrowserAccessibilityCocoa objects, representing the
174 // accessibility children of this object.
175 - (NSArray*)children {
177 uint32 childCount = browserAccessibility_->PlatformChildCount();
178 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
179 for (uint32 index = 0; index < childCount; ++index) {
180 BrowserAccessibilityCocoa* child =
181 browserAccessibility_->PlatformGetChild(index)->
182 ToBrowserAccessibilityCocoa();
183 if ([child isIgnored])
184 [children_ addObjectsFromArray:[child children]];
186 [children_ addObject:child];
189 // Also, add indirect children (if any).
190 const std::vector<int32>& indirectChildIds =
191 browserAccessibility_->GetIntListAttribute(
192 ui::AX_ATTR_INDIRECT_CHILD_IDS);
193 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
194 int32 child_id = indirectChildIds[i];
195 BrowserAccessibility* child =
196 browserAccessibility_->manager()->GetFromID(child_id);
198 // This only became necessary as a result of crbug.com/93095. It should be
199 // a DCHECK in the future.
201 BrowserAccessibilityCocoa* child_cocoa =
202 child->ToBrowserAccessibilityCocoa();
203 [children_ addObject:child_cocoa];
210 - (void)childrenChanged {
211 if (![self isIgnored]) {
214 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
219 - (NSArray*)columnHeaders {
220 if ([self internalRole] != ui::AX_ROLE_TABLE &&
221 [self internalRole] != ui::AX_ROLE_GRID) {
225 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
226 const std::vector<int32>& uniqueCellIds =
227 browserAccessibility_->GetIntListAttribute(
228 ui::AX_ATTR_UNIQUE_CELL_IDS);
229 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
230 int id = uniqueCellIds[i];
231 BrowserAccessibility* cell =
232 browserAccessibility_->manager()->GetFromID(id);
233 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
234 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
239 - (NSValue*)columnIndexRange {
240 if (!browserAccessibility_->IsCellOrTableHeaderRole())
245 browserAccessibility_->GetIntAttribute(
246 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
247 browserAccessibility_->GetIntAttribute(
248 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
249 if (column >= 0 && colspan >= 1)
250 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
254 - (NSArray*)columns {
255 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
256 for (BrowserAccessibilityCocoa* child in [self children]) {
257 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
258 [ret addObject:child];
263 - (NSString*)description {
264 std::string description;
265 if (browserAccessibility_->GetStringAttribute(
266 ui::AX_ATTR_DESCRIPTION, &description)) {
267 return base::SysUTF8ToNSString(description);
270 // If the role is anything other than an image, or if there's
271 // a title or title UI element, just return an empty string.
272 if (![[self role] isEqualToString:NSAccessibilityImageRole])
274 if (browserAccessibility_->HasStringAttribute(
278 if ([self titleUIElement])
281 // The remaining case is an image where there's no other title.
282 // Return the base part of the filename as the description.
284 if (browserAccessibility_->GetStringAttribute(
285 ui::AX_ATTR_URL, &url)) {
286 // Given a url like http://foo.com/bar/baz.png, just return the
287 // base name, e.g., "baz.png".
288 size_t leftIndex = url.rfind('/');
289 std::string basename =
290 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
291 return base::SysUTF8ToNSString(basename);
297 - (NSNumber*)disclosing {
298 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
299 return [NSNumber numberWithBool:
300 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
306 - (id)disclosedByRow {
307 // The row that contains this row.
308 // It should be the same as the first parent that is a treeitem.
312 - (NSNumber*)disclosureLevel {
313 ui::AXRole role = [self internalRole];
314 if (role == ui::AX_ROLE_ROW ||
315 role == ui::AX_ROLE_TREE_ITEM) {
316 int level = browserAccessibility_->GetIntAttribute(
317 ui::AX_ATTR_HIERARCHICAL_LEVEL);
318 // Mac disclosureLevel is 0-based, but web levels are 1-based.
321 return [NSNumber numberWithInt:level];
327 - (id)disclosedRows {
328 // The rows that are considered inside this row.
332 - (NSString*)dropeffect {
333 return NSStringForStringAttribute(
334 browserAccessibility_, ui::AX_ATTR_DROPEFFECT);
337 - (NSNumber*)enabled {
338 return [NSNumber numberWithBool:
339 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
342 - (NSNumber*)expanded {
343 return [NSNumber numberWithBool:
344 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
347 - (NSNumber*)focused {
348 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
349 NSNumber* ret = [NSNumber numberWithBool:
350 manager->GetFocus(NULL) == browserAccessibility_];
354 - (NSNumber*)grabbed {
355 bool boolValue = browserAccessibility_->GetBoolAttribute(ui::AX_ATTR_GRABBED);
356 return [NSNumber numberWithBool:boolValue];
360 int headerElementId = -1;
361 if ([self internalRole] == ui::AX_ROLE_TABLE ||
362 [self internalRole] == ui::AX_ROLE_GRID) {
363 browserAccessibility_->GetIntAttribute(
364 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
365 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
366 browserAccessibility_->GetIntAttribute(
367 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
368 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
369 browserAccessibility_->GetIntAttribute(
370 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
373 if (headerElementId > 0) {
374 BrowserAccessibility* headerObject =
375 browserAccessibility_->manager()->GetFromID(headerElementId);
377 return headerObject->ToBrowserAccessibilityCocoa();
383 return NSStringForStringAttribute(
384 browserAccessibility_, ui::AX_ATTR_HELP);
388 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
389 int columnIndex = browserAccessibility_->GetIntAttribute(
390 ui::AX_ATTR_TABLE_COLUMN_INDEX);
391 return [NSNumber numberWithInt:columnIndex];
392 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
393 int rowIndex = browserAccessibility_->GetIntAttribute(
394 ui::AX_ATTR_TABLE_ROW_INDEX);
395 return [NSNumber numberWithInt:rowIndex];
401 // Returns whether or not this node should be ignored in the
402 // accessibility tree.
404 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
407 - (NSString*)invalid {
409 if (!browserAccessibility_->GetIntAttribute(
410 ui::AX_ATTR_INVALID_STATE, &invalidState))
413 switch (invalidState) {
414 case ui::AX_INVALID_STATE_FALSE:
416 case ui::AX_INVALID_STATE_TRUE:
418 case ui::AX_INVALID_STATE_SPELLING:
420 case ui::AX_INVALID_STATE_GRAMMAR:
422 case ui::AX_INVALID_STATE_OTHER:
424 std::string ariaInvalidValue;
425 if (browserAccessibility_->GetStringAttribute(
426 ui::AX_ATTR_ARIA_INVALID_VALUE,
428 return base::SysUTF8ToNSString(ariaInvalidValue);
429 // Return @"true" since we cannot be more specific about the value.
439 - (NSString*)placeholder {
440 return NSStringForStringAttribute(
441 browserAccessibility_, ui::AX_ATTR_PLACEHOLDER);
444 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
445 addTo:(NSMutableArray*)outArray {
446 const std::vector<int32>& attributeValues =
447 browserAccessibility_->GetIntListAttribute(attribute);
448 for (size_t i = 0; i < attributeValues.size(); ++i) {
449 BrowserAccessibility* element =
450 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
452 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
456 - (NSArray*)linkedUIElements {
457 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
458 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
459 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
460 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
461 if ([ret count] == 0)
466 - (NSNumber*)loaded {
467 return [NSNumber numberWithBool:YES];
470 - (NSNumber*)loadingProgress {
471 float floatValue = browserAccessibility_->GetFloatAttribute(
472 ui::AX_ATTR_DOC_LOADING_PROGRESS);
473 return [NSNumber numberWithFloat:floatValue];
476 - (NSNumber*)maxValue {
477 float floatValue = browserAccessibility_->GetFloatAttribute(
478 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
479 return [NSNumber numberWithFloat:floatValue];
482 - (NSNumber*)minValue {
483 float floatValue = browserAccessibility_->GetFloatAttribute(
484 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
485 return [NSNumber numberWithFloat:floatValue];
488 - (NSString*)orientation {
489 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
490 return NSAccessibilityVerticalOrientationValue;
491 else if (GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL))
492 return NSAccessibilityHorizontalOrientationValue;
497 - (NSNumber*)numberOfCharacters {
498 std::string value = browserAccessibility_->GetStringAttribute(
500 return [NSNumber numberWithInt:value.size()];
503 // The origin of this accessibility object in the page's document.
504 // This is relative to webkit's top-left origin, not Cocoa's
505 // bottom-left origin.
507 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
508 return NSMakePoint(bounds.x(), bounds.y());
512 // A nil parent means we're the root.
513 if (browserAccessibility_->GetParent()) {
514 return NSAccessibilityUnignoredAncestor(
515 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
517 // Hook back up to RenderWidgetHostViewCocoa.
518 BrowserAccessibilityManagerMac* manager =
519 static_cast<BrowserAccessibilityManagerMac*>(
520 browserAccessibility_->manager());
521 return manager->parent_view();
525 - (NSValue*)position {
526 NSPoint origin = [self origin];
527 NSSize size = [[self size] sizeValue];
528 NSPoint pointInScreen = [self pointInScreen:origin size:size];
529 return [NSValue valueWithPoint:pointInScreen];
532 - (NSNumber*)required {
533 return [NSNumber numberWithBool:
534 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
537 // Returns an enum indicating the role from browserAccessibility_.
538 - (ui::AXRole)internalRole {
539 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
542 - (content::BrowserAccessibilityDelegate*)delegate {
543 return browserAccessibility_->manager() ?
544 browserAccessibility_->manager()->delegate() :
548 - (NSPoint)pointInScreen:(NSPoint)origin
550 if (!browserAccessibility_)
553 // Get the delegate for the topmost BrowserAccessibilityManager, because
554 // that's the only one that can convert points to their origin in the screen.
555 BrowserAccessibilityDelegate* delegate =
556 browserAccessibility_->manager()->GetDelegateFromRootManager();
558 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
559 gfx::Point point = delegate->AccessibilityOriginInScreen(bounds);
560 return NSMakePoint(point.x(), point.y());
566 // Returns a string indicating the NSAccessibility role of this object.
568 ui::AXRole role = [self internalRole];
569 if (role == ui::AX_ROLE_CANVAS &&
570 browserAccessibility_->GetBoolAttribute(
571 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
572 return NSAccessibilityGroupRole;
574 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
575 bool isAriaPressedDefined;
577 browserAccessibility_->GetAriaTristate("aria-pressed",
578 &isAriaPressedDefined,
580 if (isAriaPressedDefined)
581 return NSAccessibilityCheckBoxRole;
583 return NSAccessibilityButtonRole;
585 if (role == ui::AX_ROLE_TEXT_FIELD &&
586 browserAccessibility_->HasState(ui::AX_STATE_MULTILINE)) {
587 return NSAccessibilityTextAreaRole;
590 // If this is a web area for a presentational iframe, give it a role of
591 // something other than WebArea so that the fact that it's a separate doc
592 // is not exposed to AT.
593 if (browserAccessibility_->IsWebAreaForPresentationalIframe())
594 return NSAccessibilityGroupRole;
596 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
599 // Returns a string indicating the role description of this object.
600 - (NSString*)roleDescription {
601 NSString* role = [self role];
603 ContentClient* content_client = content::GetContentClient();
605 // The following descriptions are specific to webkit.
606 if ([role isEqualToString:@"AXWebArea"]) {
607 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
608 IDS_AX_ROLE_WEB_AREA));
611 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
612 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
616 if ([role isEqualToString:@"AXHeading"]) {
617 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
618 IDS_AX_ROLE_HEADING));
621 if (([role isEqualToString:NSAccessibilityGroupRole] ||
622 [role isEqualToString:NSAccessibilityRadioButtonRole]) &&
623 !browserAccessibility_->IsWebAreaForPresentationalIframe()) {
625 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
626 ui::AXRole internalRole = [self internalRole];
627 if ((internalRole != ui::AX_ROLE_GROUP &&
628 internalRole != ui::AX_ROLE_LIST_ITEM) ||
629 internalRole == ui::AX_ROLE_TAB) {
630 // TODO(dtseng): This is not localized; see crbug/84814.
631 return base::SysUTF8ToNSString(role);
636 switch([self internalRole]) {
637 case ui::AX_ROLE_ARTICLE:
638 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
639 IDS_AX_ROLE_ARTICLE));
640 case ui::AX_ROLE_BANNER:
641 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
642 IDS_AX_ROLE_BANNER));
643 case ui::AX_ROLE_COMPLEMENTARY:
644 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
645 IDS_AX_ROLE_COMPLEMENTARY));
646 case ui::AX_ROLE_CONTENT_INFO:
647 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
648 IDS_AX_ROLE_ADDRESS));
649 case ui::AX_ROLE_DESCRIPTION_LIST:
650 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
651 IDS_AX_ROLE_DESCRIPTION_LIST));
652 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
653 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
654 IDS_AX_ROLE_DESCRIPTION_DETAIL));
655 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
656 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
657 IDS_AX_ROLE_DESCRIPTION_TERM));
658 case ui::AX_ROLE_FIGURE:
659 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
660 IDS_AX_ROLE_FIGURE));
661 case ui::AX_ROLE_FOOTER:
662 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
663 IDS_AX_ROLE_FOOTER));
664 case ui::AX_ROLE_FORM:
665 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
667 case ui::AX_ROLE_MAIN:
668 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
669 IDS_AX_ROLE_MAIN_CONTENT));
670 case ui::AX_ROLE_MATH:
671 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
673 case ui::AX_ROLE_NAVIGATION:
674 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
675 IDS_AX_ROLE_NAVIGATIONAL_LINK));
676 case ui::AX_ROLE_REGION:
677 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
678 IDS_AX_ROLE_REGION));
679 case ui::AX_ROLE_SPIN_BUTTON:
680 // This control is similar to what VoiceOver calls a "stepper".
681 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
682 IDS_AX_ROLE_STEPPER));
683 case ui::AX_ROLE_STATUS:
684 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
685 IDS_AX_ROLE_STATUS));
686 case ui::AX_ROLE_SEARCH_BOX:
687 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
688 IDS_AX_ROLE_SEARCH_BOX));
689 case ui::AX_ROLE_SWITCH:
690 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
691 IDS_AX_ROLE_SWITCH));
692 case ui::AX_ROLE_TOGGLE_BUTTON:
693 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
694 IDS_AX_ROLE_TOGGLE_BUTTON));
699 return NSAccessibilityRoleDescription(role, nil);
702 - (NSArray*)rowHeaders {
703 if ([self internalRole] != ui::AX_ROLE_TABLE &&
704 [self internalRole] != ui::AX_ROLE_GRID) {
708 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
709 const std::vector<int32>& uniqueCellIds =
710 browserAccessibility_->GetIntListAttribute(
711 ui::AX_ATTR_UNIQUE_CELL_IDS);
712 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
713 int id = uniqueCellIds[i];
714 BrowserAccessibility* cell =
715 browserAccessibility_->manager()->GetFromID(id);
716 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
717 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
722 - (NSValue*)rowIndexRange {
723 if (!browserAccessibility_->IsCellOrTableHeaderRole())
728 browserAccessibility_->GetIntAttribute(
729 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
730 browserAccessibility_->GetIntAttribute(
731 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
732 if (row >= 0 && rowspan >= 1)
733 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
738 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
740 if ([self internalRole] == ui::AX_ROLE_TABLE||
741 [self internalRole] == ui::AX_ROLE_GRID) {
742 for (BrowserAccessibilityCocoa* child in [self children]) {
743 if ([[child role] isEqualToString:NSAccessibilityRowRole])
744 [ret addObject:child];
746 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
747 const std::vector<int32>& indirectChildIds =
748 browserAccessibility_->GetIntListAttribute(
749 ui::AX_ATTR_INDIRECT_CHILD_IDS);
750 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
751 int id = indirectChildIds[i];
752 BrowserAccessibility* rowElement =
753 browserAccessibility_->manager()->GetFromID(id);
755 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
762 - (NSArray*)selectedChildren {
763 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
765 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
766 BrowserAccessibility* focusedChild =
767 manager->GetFocus(browserAccessibility_);
768 if (focusedChild && focusedChild != browserAccessibility_) {
769 // First try the focused child.
770 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
772 // Next try the active descendant.
773 int activeDescendantId;
774 if (browserAccessibility_->GetIntAttribute(
775 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
776 BrowserAccessibility* activeDescendant =
777 manager->GetFromID(activeDescendantId);
778 if (activeDescendant)
779 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
781 // Otherwise return any children with the "selected" state, which
782 // may come from aria-selected.
783 uint32 childCount = browserAccessibility_->PlatformChildCount();
784 for (uint32 index = 0; index < childCount; ++index) {
785 BrowserAccessibility* child =
786 browserAccessibility_->PlatformGetChild(index);
787 if (child->HasState(ui::AX_STATE_SELECTED))
788 [ret addObject:child->ToBrowserAccessibilityCocoa()];
796 // Returns the size of this object.
798 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
799 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
802 - (NSString*)sortDirection {
804 if (!browserAccessibility_->GetIntAttribute(
805 ui::AX_ATTR_SORT_DIRECTION, &sortDirection))
808 switch (sortDirection) {
809 case ui::AX_SORT_DIRECTION_UNSORTED:
811 case ui::AX_SORT_DIRECTION_ASCENDING:
812 return @"AXSortDirectionAscending";
813 case ui::AX_SORT_DIRECTION_DESCENDING:
814 return @"AXSortDirectionDescending";
815 case ui::AX_SORT_DIRECTION_OTHER:
816 return @"AXSortDirectionUnknown";
824 // Returns a subrole based upon the role.
825 - (NSString*) subrole {
826 ui::AXRole browserAccessibilityRole = [self internalRole];
827 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
828 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
829 return @"AXSecureTextField";
832 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
833 return @"AXDefinitionList";
835 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
836 return @"AXContentList";
838 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
841 // Returns all tabs in this subtree.
843 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
845 if ([self internalRole] == ui::AX_ROLE_TAB)
846 [tabSubtree addObject:self];
848 for (uint i=0; i < [[self children] count]; ++i) {
849 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
850 if ([tabChildren count] > 0)
851 [tabSubtree addObjectsFromArray:tabChildren];
858 return NSStringForStringAttribute(
859 browserAccessibility_, ui::AX_ATTR_NAME);
862 - (id)titleUIElement {
864 if (browserAccessibility_->GetIntAttribute(
865 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
866 BrowserAccessibility* titleElement =
867 browserAccessibility_->manager()->GetFromID(titleElementId);
869 return titleElement->ToBrowserAccessibilityCocoa();
871 std::vector<int32> labelledby_ids =
872 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
873 if (labelledby_ids.size() == 1) {
874 BrowserAccessibility* titleElement =
875 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
877 return titleElement->ToBrowserAccessibilityCocoa();
884 StringAttribute urlAttribute =
885 [[self role] isEqualToString:@"AXWebArea"] ?
886 ui::AX_ATTR_DOC_URL :
889 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
893 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
897 // WebCore uses an attachmentView to get the below behavior.
898 // We do not have any native views backing this object, so need
899 // to approximate Cocoa ax behavior best as we can.
900 NSString* role = [self role];
901 if ([role isEqualToString:@"AXHeading"]) {
903 if (browserAccessibility_->GetIntAttribute(
904 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
905 return [NSNumber numberWithInt:level];
907 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
908 // AXValue does not make sense for pure buttons.
910 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
912 bool isAriaPressedDefined;
914 value = browserAccessibility_->GetAriaTristate(
915 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
920 return [NSNumber numberWithInt:value];
922 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
923 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
926 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
928 browserAccessibility_, ui::AX_STATE_SELECTED) ?
932 if (browserAccessibility_->GetBoolAttribute(
933 ui::AX_ATTR_BUTTON_MIXED)) {
936 return [NSNumber numberWithInt:value];
937 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
938 [role isEqualToString:NSAccessibilitySliderRole] ||
939 [role isEqualToString:NSAccessibilityIncrementorRole] ||
940 [role isEqualToString:NSAccessibilityScrollBarRole]) {
942 if (browserAccessibility_->GetFloatAttribute(
943 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
944 return [NSNumber numberWithFloat:floatValue];
946 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
947 int r = browserAccessibility_->GetIntAttribute(
948 ui::AX_ATTR_COLOR_VALUE_RED);
949 int g = browserAccessibility_->GetIntAttribute(
950 ui::AX_ATTR_COLOR_VALUE_GREEN);
951 int b = browserAccessibility_->GetIntAttribute(
952 ui::AX_ATTR_COLOR_VALUE_BLUE);
953 // This string matches the one returned by a native Mac color well.
954 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
955 r / 255., g / 255., b / 255.];
958 return NSStringForStringAttribute(
959 browserAccessibility_, ui::AX_ATTR_VALUE);
962 - (NSString*)valueDescription {
963 return NSStringForStringAttribute(
964 browserAccessibility_, ui::AX_ATTR_VALUE);
967 - (NSValue*)visibleCharacterRange {
968 std::string value = browserAccessibility_->GetStringAttribute(
970 return [NSValue valueWithRange:NSMakeRange(0, value.size())];
973 - (NSArray*)visibleCells {
974 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
975 const std::vector<int32>& uniqueCellIds =
976 browserAccessibility_->GetIntListAttribute(
977 ui::AX_ATTR_UNIQUE_CELL_IDS);
978 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
979 int id = uniqueCellIds[i];
980 BrowserAccessibility* cell =
981 browserAccessibility_->manager()->GetFromID(id);
983 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
988 - (NSArray*)visibleChildren {
989 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
990 uint32 childCount = browserAccessibility_->PlatformChildCount();
991 for (uint32 index = 0; index < childCount; ++index) {
992 BrowserAccessibilityCocoa* child =
993 browserAccessibility_->PlatformGetChild(index)->
994 ToBrowserAccessibilityCocoa();
995 [ret addObject:child];
1000 - (NSArray*)visibleColumns {
1001 return [self columns];
1004 - (NSArray*)visibleRows {
1008 - (NSNumber*)visited {
1009 return [NSNumber numberWithBool:
1010 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1014 if (!browserAccessibility_)
1017 BrowserAccessibilityManagerMac* manager =
1018 static_cast<BrowserAccessibilityManagerMac*>(
1019 browserAccessibility_->manager());
1020 if (!manager || !manager->parent_view())
1023 return [manager->parent_view() window];
1026 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1027 return [attributeToMethodNameMap objectForKey:attribute];
1030 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
1031 children_.swap(*other);
1034 // Returns the accessibility value for the given attribute. If the value isn't
1035 // supported this will return nil.
1036 - (id)accessibilityAttributeValue:(NSString*)attribute {
1037 if (!browserAccessibility_)
1041 NSSelectorFromString([self methodNameForAttribute:attribute]);
1043 return [self performSelector:selector];
1045 // TODO(dtseng): refactor remaining attributes.
1046 int selStart, selEnd;
1047 if (browserAccessibility_->GetIntAttribute(
1048 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1049 browserAccessibility_->
1050 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1051 if (selStart > selEnd)
1052 std::swap(selStart, selEnd);
1053 int selLength = selEnd - selStart;
1054 if ([attribute isEqualToString:
1055 NSAccessibilityInsertionPointLineNumberAttribute]) {
1056 const std::vector<int32>& line_breaks =
1057 browserAccessibility_->GetIntListAttribute(
1058 ui::AX_ATTR_LINE_BREAKS);
1059 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1060 if (line_breaks[i] > selStart)
1061 return [NSNumber numberWithInt:i];
1063 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1065 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1066 std::string value = browserAccessibility_->GetStringAttribute(
1068 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1070 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1071 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1077 // Returns the accessibility value for the given attribute and parameter. If the
1078 // value isn't supported this will return nil.
1079 - (id)accessibilityAttributeValue:(NSString*)attribute
1080 forParameter:(id)parameter {
1081 if (!browserAccessibility_)
1084 const std::vector<int32>& line_breaks =
1085 browserAccessibility_->GetIntListAttribute(
1086 ui::AX_ATTR_LINE_BREAKS);
1087 std::string value = browserAccessibility_->GetStringAttribute(
1089 int len = static_cast<int>(value.size());
1091 if ([attribute isEqualToString:
1092 NSAccessibilityStringForRangeParameterizedAttribute]) {
1093 NSRange range = [(NSValue*)parameter rangeValue];
1094 std::string value = browserAccessibility_->GetStringAttribute(
1096 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1099 if ([attribute isEqualToString:
1100 NSAccessibilityLineForIndexParameterizedAttribute]) {
1101 int index = [(NSNumber*)parameter intValue];
1102 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1103 if (line_breaks[i] > index)
1104 return [NSNumber numberWithInt:i];
1106 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1109 if ([attribute isEqualToString:
1110 NSAccessibilityRangeForLineParameterizedAttribute]) {
1111 int line_index = [(NSNumber*)parameter intValue];
1112 int line_count = static_cast<int>(line_breaks.size()) + 1;
1113 if (line_index < 0 || line_index >= line_count)
1115 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1116 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1117 return [NSValue valueWithRange:
1118 NSMakeRange(start, end - start)];
1121 if ([attribute isEqualToString:
1122 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1123 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1124 [self internalRole] != ui::AX_ROLE_GRID) {
1127 if (![parameter isKindOfClass:[NSArray self]])
1129 NSArray* array = parameter;
1130 int column = [[array objectAtIndex:0] intValue];
1131 int row = [[array objectAtIndex:1] intValue];
1132 int num_columns = browserAccessibility_->GetIntAttribute(
1133 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1134 int num_rows = browserAccessibility_->GetIntAttribute(
1135 ui::AX_ATTR_TABLE_ROW_COUNT);
1136 if (column < 0 || column >= num_columns ||
1137 row < 0 || row >= num_rows) {
1141 i < browserAccessibility_->PlatformChildCount();
1143 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1144 if (child->GetRole() != ui::AX_ROLE_ROW)
1147 if (!child->GetIntAttribute(
1148 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1156 j < child->PlatformChildCount();
1158 BrowserAccessibility* cell = child->PlatformGetChild(j);
1159 if (!cell->IsCellOrTableHeaderRole())
1162 if (!cell->GetIntAttribute(
1163 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1167 if (colIndex == column)
1168 return cell->ToBrowserAccessibilityCocoa();
1169 if (colIndex > column)
1176 if ([attribute isEqualToString:
1177 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1178 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1180 NSRange range = [(NSValue*)parameter rangeValue];
1181 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1182 range.location, range.length);
1183 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1184 NSSize size = NSMakeSize(rect.width(), rect.height());
1185 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1186 NSRect nsrect = NSMakeRect(
1187 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1188 return [NSValue valueWithRect:nsrect];
1191 // TODO(dtseng): support the following attributes.
1192 if ([attribute isEqualTo:
1193 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1194 [attribute isEqualTo:
1195 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1196 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1197 [attribute isEqualTo:
1198 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1204 // Returns an array of parameterized attributes names that this object will
1206 - (NSArray*)accessibilityParameterizedAttributeNames {
1207 if (!browserAccessibility_)
1210 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1211 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1212 return [NSArray arrayWithObjects:
1213 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1216 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole]) {
1217 return [NSArray arrayWithObjects:
1218 NSAccessibilityLineForIndexParameterizedAttribute,
1219 NSAccessibilityRangeForLineParameterizedAttribute,
1220 NSAccessibilityStringForRangeParameterizedAttribute,
1221 NSAccessibilityRangeForPositionParameterizedAttribute,
1222 NSAccessibilityRangeForIndexParameterizedAttribute,
1223 NSAccessibilityBoundsForRangeParameterizedAttribute,
1224 NSAccessibilityRTFForRangeParameterizedAttribute,
1225 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1226 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1229 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1230 return [NSArray arrayWithObjects:
1231 NSAccessibilityBoundsForRangeParameterizedAttribute,
1237 // Returns an array of action names that this object will respond to.
1238 - (NSArray*)accessibilityActionNames {
1239 if (!browserAccessibility_)
1242 NSMutableArray* ret =
1243 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1244 NSString* role = [self role];
1245 // TODO(dtseng): this should only get set when there's a default action.
1246 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1247 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1248 [ret addObject:NSAccessibilityPressAction];
1254 // Returns a sub-array of values for the given attribute value, starting at
1255 // index, with up to maxCount items. If the given index is out of bounds,
1256 // or there are no values for the given attribute, it will return nil.
1257 // This method is used for querying subsets of values, without having to
1258 // return a large set of data, such as elements with a large number of
1260 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1261 index:(NSUInteger)index
1262 maxCount:(NSUInteger)maxCount {
1263 if (!browserAccessibility_)
1266 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1269 NSUInteger arrayCount = [fullArray count];
1270 if (index >= arrayCount)
1273 if ((index + maxCount) > arrayCount) {
1274 subRange = NSMakeRange(index, arrayCount - index);
1276 subRange = NSMakeRange(index, maxCount);
1278 return [fullArray subarrayWithRange:subRange];
1281 // Returns the count of the specified accessibility array attribute.
1282 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1283 if (!browserAccessibility_)
1286 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1287 return [fullArray count];
1290 // Returns the list of accessibility attributes that this object supports.
1291 - (NSArray*)accessibilityAttributeNames {
1292 if (!browserAccessibility_)
1295 // General attributes.
1296 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1297 NSAccessibilityChildrenAttribute,
1298 NSAccessibilityDescriptionAttribute,
1299 NSAccessibilityEnabledAttribute,
1300 NSAccessibilityFocusedAttribute,
1301 NSAccessibilityHelpAttribute,
1302 NSAccessibilityLinkedUIElementsAttribute,
1303 NSAccessibilityParentAttribute,
1304 NSAccessibilityPositionAttribute,
1305 NSAccessibilityRoleAttribute,
1306 NSAccessibilityRoleDescriptionAttribute,
1307 NSAccessibilitySizeAttribute,
1308 NSAccessibilitySubroleAttribute,
1309 NSAccessibilityTitleAttribute,
1310 NSAccessibilityTopLevelUIElementAttribute,
1311 NSAccessibilityValueAttribute,
1312 NSAccessibilityWindowAttribute,
1318 // Specific role attributes.
1319 NSString* role = [self role];
1320 NSString* subrole = [self subrole];
1321 if ([role isEqualToString:NSAccessibilityTableRole] ||
1322 [role isEqualToString:NSAccessibilityGridRole]) {
1323 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1324 NSAccessibilityColumnsAttribute,
1325 NSAccessibilityVisibleColumnsAttribute,
1326 NSAccessibilityRowsAttribute,
1327 NSAccessibilityVisibleRowsAttribute,
1328 NSAccessibilityVisibleCellsAttribute,
1329 NSAccessibilityHeaderAttribute,
1330 NSAccessibilityColumnHeaderUIElementsAttribute,
1331 NSAccessibilityRowHeaderUIElementsAttribute,
1333 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1334 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1335 NSAccessibilityIndexAttribute,
1336 NSAccessibilityHeaderAttribute,
1337 NSAccessibilityRowsAttribute,
1338 NSAccessibilityVisibleRowsAttribute,
1340 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1341 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1342 NSAccessibilityColumnIndexRangeAttribute,
1343 NSAccessibilityRowIndexRangeAttribute,
1346 } else if ([role isEqualToString:@"AXWebArea"]) {
1347 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1349 @"AXLoadingProgress",
1351 } else if ([role isEqualToString:NSAccessibilityTextFieldRole]) {
1352 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1353 NSAccessibilityInsertionPointLineNumberAttribute,
1354 NSAccessibilityNumberOfCharactersAttribute,
1355 NSAccessibilitySelectedTextAttribute,
1356 NSAccessibilitySelectedTextRangeAttribute,
1357 NSAccessibilityVisibleCharacterRangeAttribute,
1359 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1360 [ret addObject:NSAccessibilityTabsAttribute];
1361 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1362 [role isEqualToString:NSAccessibilitySliderRole] ||
1363 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1364 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1365 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1366 NSAccessibilityMaxValueAttribute,
1367 NSAccessibilityMinValueAttribute,
1368 NSAccessibilityValueDescriptionAttribute,
1370 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1371 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1372 NSAccessibilityDisclosingAttribute,
1373 NSAccessibilityDisclosedByRowAttribute,
1374 NSAccessibilityDisclosureLevelAttribute,
1375 NSAccessibilityDisclosedRowsAttribute,
1377 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1378 if (browserAccessibility_->GetParent()) {
1379 base::string16 parentRole;
1380 browserAccessibility_->GetParent()->GetHtmlAttribute(
1381 "role", &parentRole);
1382 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1383 if (parentRole == treegridRole) {
1384 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1385 NSAccessibilityDisclosingAttribute,
1386 NSAccessibilityDisclosedByRowAttribute,
1387 NSAccessibilityDisclosureLevelAttribute,
1388 NSAccessibilityDisclosedRowsAttribute,
1391 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1392 NSAccessibilityIndexAttribute,
1396 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1397 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1398 NSAccessibilitySelectedChildrenAttribute,
1399 NSAccessibilityVisibleChildrenAttribute,
1403 // Add the url attribute only if it has a valid url.
1404 if ([self url] != nil) {
1405 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1406 NSAccessibilityURLAttribute,
1411 if (browserAccessibility_->HasStringAttribute(
1412 ui::AX_ATTR_LIVE_STATUS)) {
1413 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1417 if (browserAccessibility_->HasStringAttribute(
1418 ui::AX_ATTR_LIVE_RELEVANT)) {
1419 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1423 if (browserAccessibility_->HasBoolAttribute(
1424 ui::AX_ATTR_LIVE_ATOMIC)) {
1425 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1429 if (browserAccessibility_->HasBoolAttribute(
1430 ui::AX_ATTR_LIVE_BUSY)) {
1431 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1436 if (browserAccessibility_->HasStringAttribute(
1437 ui::AX_ATTR_DROPEFFECT)) {
1438 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1443 // Add aria-grabbed attribute only if it has true.
1444 if (browserAccessibility_->HasBoolAttribute(ui::AX_ATTR_GRABBED)) {
1445 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1450 // Add expanded attribute only if it has expanded or collapsed state.
1451 if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) ||
1452 GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) {
1453 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1454 NSAccessibilityExpandedAttribute,
1458 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)
1459 || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) {
1460 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1461 NSAccessibilityOrientationAttribute, nil]];
1464 if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) {
1465 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1466 @"AXPlaceholder", nil]];
1469 if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) {
1470 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1471 @"AXRequired", nil]];
1474 // Title UI Element.
1475 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1476 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1477 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1479 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1480 NSAccessibilityTitleUIElementAttribute,
1483 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1484 // for elements which are referred to by labelledby or are labels
1489 // Returns the index of the child in this objects array of children.
1490 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1491 if (!browserAccessibility_)
1494 NSUInteger index = 0;
1495 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1496 if ([child isEqual:childToCheck])
1503 // Returns whether or not the specified attribute can be set by the
1504 // accessibility API via |accessibilitySetValue:forAttribute:|.
1505 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1506 if (!browserAccessibility_)
1509 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1510 return GetState(browserAccessibility_,
1511 ui::AX_STATE_FOCUSABLE);
1512 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1513 return browserAccessibility_->GetBoolAttribute(
1514 ui::AX_ATTR_CAN_SET_VALUE);
1516 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1517 ([[self role] isEqualToString:NSAccessibilityTextFieldRole]))
1523 // Returns whether or not this object should be ignored in the accessibility
1525 - (BOOL)accessibilityIsIgnored {
1526 if (!browserAccessibility_)
1529 return [self isIgnored];
1532 // Performs the given accessibility action on the webkit accessibility object
1533 // that backs this object.
1534 - (void)accessibilityPerformAction:(NSString*)action {
1535 if (!browserAccessibility_)
1538 // TODO(dmazzoni): Support more actions.
1539 if ([action isEqualToString:NSAccessibilityPressAction]) {
1540 [self delegate]->AccessibilityDoDefaultAction(
1541 browserAccessibility_->GetId());
1542 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1543 NSPoint objOrigin = [self origin];
1544 NSSize size = [[self size] sizeValue];
1545 gfx::Point origin = [self delegate]->AccessibilityOriginInScreen(
1546 gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
1547 origin.Offset(size.width / 2, size.height / 2);
1548 [self delegate]->AccessibilityShowMenu(origin);
1552 // Returns the description of the given action.
1553 - (NSString*)accessibilityActionDescription:(NSString*)action {
1554 if (!browserAccessibility_)
1557 return NSAccessibilityActionDescription(action);
1560 // Sets an override value for a specific accessibility attribute.
1561 // This class does not support this.
1562 - (BOOL)accessibilitySetOverrideValue:(id)value
1563 forAttribute:(NSString*)attribute {
1567 // Sets the value for an accessibility attribute via the accessibility API.
1568 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1569 if (!browserAccessibility_)
1572 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1573 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1574 NSNumber* focusedNumber = value;
1575 BOOL focused = [focusedNumber intValue];
1577 manager->SetFocus(browserAccessibility_, true);
1579 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1580 NSRange range = [(NSValue*)value rangeValue];
1581 [self delegate]->AccessibilitySetTextSelection(
1582 browserAccessibility_->GetId(),
1583 range.location, range.location + range.length);
1587 // Returns the deepest accessibility child that should not be ignored.
1588 // It is assumed that the hit test has been narrowed down to this object
1589 // or one of its children, so this will never return nil unless this
1590 // object is invalid.
1591 - (id)accessibilityHitTest:(NSPoint)point {
1592 if (!browserAccessibility_)
1595 BrowserAccessibilityCocoa* hit = self;
1596 for (BrowserAccessibilityCocoa* child in [self children]) {
1597 if (!child->browserAccessibility_)
1599 NSPoint origin = [child origin];
1600 NSSize size = [[child size] sizeValue];
1602 rect.origin = origin;
1604 if (NSPointInRect(point, rect)) {
1606 id childResult = [child accessibilityHitTest:point];
1607 if (![childResult accessibilityIsIgnored]) {
1613 return NSAccessibilityUnignoredAncestor(hit);
1616 - (BOOL)isEqual:(id)object {
1617 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1619 return ([self hash] == [object hash]);
1622 - (NSUInteger)hash {
1623 // Potentially called during dealloc.
1624 if (!browserAccessibility_)
1625 return [super hash];
1626 return browserAccessibility_->GetId();
1629 - (BOOL)accessibilityShouldUseUniqueId {