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::BrowserAccessibilityManager;
29 using content::BrowserAccessibilityManagerMac;
30 using content::ContentClient;
31 typedef ui::AXStringAttribute StringAttribute;
35 // Returns an autoreleased copy of the AXNodeData's attribute.
36 NSString* NSStringForStringAttribute(
37 BrowserAccessibility* browserAccessibility,
38 StringAttribute attribute) {
39 return base::SysUTF8ToNSString(
40 browserAccessibility->GetStringAttribute(attribute));
43 // GetState checks the bitmask used in AXNodeData to check
44 // if the given state was set on the accessibility object.
45 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
46 return ((accessibility->GetState() >> state) & 1);
49 // A mapping from an accessibility attribute to its method name.
50 NSDictionary* attributeToMethodNameMap = nil;
54 @implementation BrowserAccessibilityCocoa
60 } attributeToMethodNameContainer[] = {
61 { NSAccessibilityChildrenAttribute, @"children" },
62 { NSAccessibilityColumnsAttribute, @"columns" },
63 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
64 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
65 { NSAccessibilityContentsAttribute, @"contents" },
66 { NSAccessibilityDescriptionAttribute, @"description" },
67 { NSAccessibilityDisclosingAttribute, @"disclosing" },
68 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
69 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
70 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
71 { NSAccessibilityEnabledAttribute, @"enabled" },
72 { NSAccessibilityFocusedAttribute, @"focused" },
73 { NSAccessibilityHeaderAttribute, @"header" },
74 { NSAccessibilityHelpAttribute, @"help" },
75 { NSAccessibilityIndexAttribute, @"index" },
76 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
77 { NSAccessibilityMaxValueAttribute, @"maxValue" },
78 { NSAccessibilityMinValueAttribute, @"minValue" },
79 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
80 { NSAccessibilityOrientationAttribute, @"orientation" },
81 { NSAccessibilityParentAttribute, @"parent" },
82 { NSAccessibilityPositionAttribute, @"position" },
83 { NSAccessibilityRoleAttribute, @"role" },
84 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
85 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
86 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
87 { NSAccessibilityRowsAttribute, @"rows" },
88 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
89 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
90 { NSAccessibilitySizeAttribute, @"size" },
91 { NSAccessibilitySubroleAttribute, @"subrole" },
92 { NSAccessibilityTabsAttribute, @"tabs" },
93 { NSAccessibilityTitleAttribute, @"title" },
94 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
95 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
96 { NSAccessibilityURLAttribute, @"url" },
97 { NSAccessibilityValueAttribute, @"value" },
98 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
99 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
100 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
101 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
102 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
103 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
104 { NSAccessibilityWindowAttribute, @"window" },
105 { @"AXAccessKey", @"accessKey" },
106 { @"AXARIAAtomic", @"ariaAtomic" },
107 { @"AXARIABusy", @"ariaBusy" },
108 { @"AXARIALive", @"ariaLive" },
109 { @"AXARIARelevant", @"ariaRelevant" },
110 { @"AXInvalid", @"invalid" },
111 { @"AXLoaded", @"loaded" },
112 { @"AXLoadingProgress", @"loadingProgress" },
113 { @"AXRequired", @"required" },
114 { @"AXVisited", @"visited" },
117 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
118 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
119 sizeof(attributeToMethodNameContainer[0]);
120 for (size_t i = 0; i < numAttributes; ++i) {
121 [dict setObject:attributeToMethodNameContainer[i].methodName
122 forKey:attributeToMethodNameContainer[i].attribute];
124 attributeToMethodNameMap = dict;
128 - (id)initWithObject:(BrowserAccessibility*)accessibility {
129 if ((self = [super init]))
130 browserAccessibility_ = accessibility;
135 if (browserAccessibility_) {
136 NSAccessibilityUnregisterUniqueIdForUIElement(self);
137 browserAccessibility_ = NULL;
141 - (NSString*)accessKey {
142 return NSStringForStringAttribute(
143 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
146 - (NSNumber*)ariaAtomic {
147 bool boolValue = browserAccessibility_->GetBoolAttribute(
148 ui::AX_ATTR_LIVE_ATOMIC);
149 return [NSNumber numberWithBool:boolValue];
152 - (NSNumber*)ariaBusy {
153 bool boolValue = browserAccessibility_->GetBoolAttribute(
154 ui::AX_ATTR_LIVE_BUSY);
155 return [NSNumber numberWithBool:boolValue];
158 - (NSString*)ariaLive {
159 return NSStringForStringAttribute(
160 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
163 - (NSString*)ariaRelevant {
164 return NSStringForStringAttribute(
165 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
168 // Returns an array of BrowserAccessibilityCocoa objects, representing the
169 // accessibility children of this object.
170 - (NSArray*)children {
172 uint32 childCount = browserAccessibility_->PlatformChildCount();
173 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
174 for (uint32 index = 0; index < childCount; ++index) {
175 BrowserAccessibilityCocoa* child =
176 browserAccessibility_->PlatformGetChild(index)->
177 ToBrowserAccessibilityCocoa();
178 if ([child isIgnored])
179 [children_ addObjectsFromArray:[child children]];
181 [children_ addObject:child];
184 // Also, add indirect children (if any).
185 const std::vector<int32>& indirectChildIds =
186 browserAccessibility_->GetIntListAttribute(
187 ui::AX_ATTR_INDIRECT_CHILD_IDS);
188 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
189 int32 child_id = indirectChildIds[i];
190 BrowserAccessibility* child =
191 browserAccessibility_->manager()->GetFromID(child_id);
193 // This only became necessary as a result of crbug.com/93095. It should be
194 // a DCHECK in the future.
196 BrowserAccessibilityCocoa* child_cocoa =
197 child->ToBrowserAccessibilityCocoa();
198 [children_ addObject:child_cocoa];
205 - (void)childrenChanged {
206 if (![self isIgnored]) {
209 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
214 - (NSArray*)columnHeaders {
215 if ([self internalRole] != ui::AX_ROLE_TABLE &&
216 [self internalRole] != ui::AX_ROLE_GRID) {
220 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
221 const std::vector<int32>& uniqueCellIds =
222 browserAccessibility_->GetIntListAttribute(
223 ui::AX_ATTR_UNIQUE_CELL_IDS);
224 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
225 int id = uniqueCellIds[i];
226 BrowserAccessibility* cell =
227 browserAccessibility_->manager()->GetFromID(id);
228 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
229 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
234 - (NSValue*)columnIndexRange {
235 if ([self internalRole] != ui::AX_ROLE_CELL)
240 browserAccessibility_->GetIntAttribute(
241 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
242 browserAccessibility_->GetIntAttribute(
243 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
244 if (column >= 0 && colspan >= 1)
245 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
249 - (NSArray*)columns {
250 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
251 for (BrowserAccessibilityCocoa* child in [self children]) {
252 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
253 [ret addObject:child];
258 - (NSString*)description {
259 std::string description;
260 if (browserAccessibility_->GetStringAttribute(
261 ui::AX_ATTR_DESCRIPTION, &description)) {
262 return base::SysUTF8ToNSString(description);
265 // If the role is anything other than an image, or if there's
266 // a title or title UI element, just return an empty string.
267 if (![[self role] isEqualToString:NSAccessibilityImageRole])
269 if (browserAccessibility_->HasStringAttribute(
273 if ([self titleUIElement])
276 // The remaining case is an image where there's no other title.
277 // Return the base part of the filename as the description.
279 if (browserAccessibility_->GetStringAttribute(
280 ui::AX_ATTR_URL, &url)) {
281 // Given a url like http://foo.com/bar/baz.png, just return the
282 // base name, e.g., "baz.png".
283 size_t leftIndex = url.rfind('/');
284 std::string basename =
285 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
286 return base::SysUTF8ToNSString(basename);
292 - (NSNumber*)disclosing {
293 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
294 return [NSNumber numberWithBool:
295 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
301 - (id)disclosedByRow {
302 // The row that contains this row.
303 // It should be the same as the first parent that is a treeitem.
307 - (NSNumber*)disclosureLevel {
308 ui::AXRole role = [self internalRole];
309 if (role == ui::AX_ROLE_ROW ||
310 role == ui::AX_ROLE_TREE_ITEM) {
311 int level = browserAccessibility_->GetIntAttribute(
312 ui::AX_ATTR_HIERARCHICAL_LEVEL);
313 // Mac disclosureLevel is 0-based, but web levels are 1-based.
316 return [NSNumber numberWithInt:level];
322 - (id)disclosedRows {
323 // The rows that are considered inside this row.
327 - (NSNumber*)enabled {
328 return [NSNumber numberWithBool:
329 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
332 - (NSNumber*)focused {
333 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
334 NSNumber* ret = [NSNumber numberWithBool:
335 manager->GetFocus(NULL) == browserAccessibility_];
340 int headerElementId = -1;
341 if ([self internalRole] == ui::AX_ROLE_TABLE ||
342 [self internalRole] == ui::AX_ROLE_GRID) {
343 browserAccessibility_->GetIntAttribute(
344 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
345 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
346 browserAccessibility_->GetIntAttribute(
347 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
348 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
349 browserAccessibility_->GetIntAttribute(
350 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
353 if (headerElementId > 0) {
354 BrowserAccessibility* headerObject =
355 browserAccessibility_->manager()->GetFromID(headerElementId);
357 return headerObject->ToBrowserAccessibilityCocoa();
363 return NSStringForStringAttribute(
364 browserAccessibility_, ui::AX_ATTR_HELP);
368 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
369 int columnIndex = browserAccessibility_->GetIntAttribute(
370 ui::AX_ATTR_TABLE_COLUMN_INDEX);
371 return [NSNumber numberWithInt:columnIndex];
372 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
373 int rowIndex = browserAccessibility_->GetIntAttribute(
374 ui::AX_ATTR_TABLE_ROW_INDEX);
375 return [NSNumber numberWithInt:rowIndex];
381 // Returns whether or not this node should be ignored in the
382 // accessibility tree.
384 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
387 - (NSString*)invalid {
388 base::string16 invalidUTF;
389 if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
391 NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
392 if ([invalid isEqualToString:@"false"] ||
393 [invalid isEqualToString:@""]) {
399 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
400 addTo:(NSMutableArray*)outArray {
401 const std::vector<int32>& attributeValues =
402 browserAccessibility_->GetIntListAttribute(attribute);
403 for (size_t i = 0; i < attributeValues.size(); ++i) {
404 BrowserAccessibility* element =
405 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
407 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
411 - (NSArray*)linkedUIElements {
412 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
413 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
414 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
415 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
416 if ([ret count] == 0)
421 - (NSNumber*)loaded {
422 return [NSNumber numberWithBool:YES];
425 - (NSNumber*)loadingProgress {
426 float floatValue = browserAccessibility_->GetFloatAttribute(
427 ui::AX_ATTR_DOC_LOADING_PROGRESS);
428 return [NSNumber numberWithFloat:floatValue];
431 - (NSNumber*)maxValue {
432 float floatValue = browserAccessibility_->GetFloatAttribute(
433 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
434 return [NSNumber numberWithFloat:floatValue];
437 - (NSNumber*)minValue {
438 float floatValue = browserAccessibility_->GetFloatAttribute(
439 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
440 return [NSNumber numberWithFloat:floatValue];
443 - (NSString*)orientation {
444 // We present a spin button as a vertical slider, with a role description
446 if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
447 return NSAccessibilityVerticalOrientationValue;
449 if ([self internalRole] == ui::AX_ROLE_LIST ||
450 [self internalRole] == ui::AX_ROLE_LIST_BOX) {
451 return NSAccessibilityVerticalOrientationValue;
454 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
455 return NSAccessibilityVerticalOrientationValue;
457 return NSAccessibilityHorizontalOrientationValue;
460 - (NSNumber*)numberOfCharacters {
461 return [NSNumber numberWithInt:browserAccessibility_->value().length()];
464 // The origin of this accessibility object in the page's document.
465 // This is relative to webkit's top-left origin, not Cocoa's
466 // bottom-left origin.
468 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
469 return NSMakePoint(bounds.x(), bounds.y());
473 // A nil parent means we're the root.
474 if (browserAccessibility_->GetParent()) {
475 return NSAccessibilityUnignoredAncestor(
476 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
478 // Hook back up to RenderWidgetHostViewCocoa.
479 BrowserAccessibilityManagerMac* manager =
480 static_cast<BrowserAccessibilityManagerMac*>(
481 browserAccessibility_->manager());
482 return manager->parent_view();
486 - (NSValue*)position {
487 NSPoint origin = [self origin];
488 NSSize size = [[self size] sizeValue];
489 NSPoint pointInScreen = [self pointInScreen:origin size:size];
490 return [NSValue valueWithPoint:pointInScreen];
493 - (NSNumber*)required {
494 return [NSNumber numberWithBool:
495 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
498 // Returns an enum indicating the role from browserAccessibility_.
499 - (ui::AXRole)internalRole {
500 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
503 - (content::BrowserAccessibilityDelegate*)delegate {
504 return browserAccessibility_->manager() ?
505 browserAccessibility_->manager()->delegate() :
509 - (NSPoint)pointInScreen:(NSPoint)origin
511 if (!browserAccessibility_)
514 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
515 gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
516 return NSMakePoint(point.x(), point.y());
519 // Returns a string indicating the NSAccessibility role of this object.
521 ui::AXRole role = [self internalRole];
522 if (role == ui::AX_ROLE_CANVAS &&
523 browserAccessibility_->GetBoolAttribute(
524 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
525 return NSAccessibilityGroupRole;
527 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
528 bool isAriaPressedDefined;
530 browserAccessibility_->GetAriaTristate("aria-pressed",
531 &isAriaPressedDefined,
533 if (isAriaPressedDefined)
534 return NSAccessibilityCheckBoxRole;
536 return NSAccessibilityButtonRole;
538 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
541 // Returns a string indicating the role description of this object.
542 - (NSString*)roleDescription {
543 NSString* role = [self role];
545 ContentClient* content_client = content::GetContentClient();
547 // The following descriptions are specific to webkit.
548 if ([role isEqualToString:@"AXWebArea"]) {
549 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
550 IDS_AX_ROLE_WEB_AREA));
553 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
554 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
558 if ([role isEqualToString:@"AXHeading"]) {
559 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
560 IDS_AX_ROLE_HEADING));
563 if ([role isEqualToString:NSAccessibilityGroupRole] ||
564 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
566 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
567 ui::AXRole internalRole = [self internalRole];
568 if ((internalRole != ui::AX_ROLE_GROUP &&
569 internalRole != ui::AX_ROLE_LIST_ITEM) ||
570 internalRole == ui::AX_ROLE_TAB) {
571 // TODO(dtseng): This is not localized; see crbug/84814.
572 return base::SysUTF8ToNSString(role);
577 switch([self internalRole]) {
578 case ui::AX_ROLE_BANNER:
579 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
580 IDS_AX_ROLE_BANNER));
582 case ui::AX_ROLE_FOOTER:
583 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
584 IDS_AX_ROLE_FOOTER));
585 case ui::AX_ROLE_REGION:
586 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
587 IDS_AX_ROLE_REGION));
588 case ui::AX_ROLE_SPIN_BUTTON:
589 // This control is similar to what VoiceOver calls a "stepper".
590 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
591 IDS_AX_ROLE_STEPPER));
592 case ui::AX_ROLE_TOGGLE_BUTTON:
593 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
594 IDS_AX_ROLE_TOGGLE_BUTTON));
599 return NSAccessibilityRoleDescription(role, nil);
602 - (NSArray*)rowHeaders {
603 if ([self internalRole] != ui::AX_ROLE_TABLE &&
604 [self internalRole] != ui::AX_ROLE_GRID) {
608 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
609 const std::vector<int32>& uniqueCellIds =
610 browserAccessibility_->GetIntListAttribute(
611 ui::AX_ATTR_UNIQUE_CELL_IDS);
612 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
613 int id = uniqueCellIds[i];
614 BrowserAccessibility* cell =
615 browserAccessibility_->manager()->GetFromID(id);
616 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
617 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
622 - (NSValue*)rowIndexRange {
623 if ([self internalRole] != ui::AX_ROLE_CELL)
628 browserAccessibility_->GetIntAttribute(
629 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
630 browserAccessibility_->GetIntAttribute(
631 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
632 if (row >= 0 && rowspan >= 1)
633 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
638 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
640 if ([self internalRole] == ui::AX_ROLE_TABLE||
641 [self internalRole] == ui::AX_ROLE_GRID) {
642 for (BrowserAccessibilityCocoa* child in [self children]) {
643 if ([[child role] isEqualToString:NSAccessibilityRowRole])
644 [ret addObject:child];
646 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
647 const std::vector<int32>& indirectChildIds =
648 browserAccessibility_->GetIntListAttribute(
649 ui::AX_ATTR_INDIRECT_CHILD_IDS);
650 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
651 int id = indirectChildIds[i];
652 BrowserAccessibility* rowElement =
653 browserAccessibility_->manager()->GetFromID(id);
655 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
662 - (NSArray*)selectedChildren {
663 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
665 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
666 BrowserAccessibility* focusedChild =
667 manager->GetFocus(browserAccessibility_);
668 if (focusedChild && focusedChild != browserAccessibility_) {
669 // First try the focused child.
670 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
672 // Next try the active descendant.
673 int activeDescendantId;
674 if (browserAccessibility_->GetIntAttribute(
675 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
676 BrowserAccessibility* activeDescendant =
677 manager->GetFromID(activeDescendantId);
678 if (activeDescendant)
679 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
681 // Otherwise return any children with the "selected" state, which
682 // may come from aria-selected.
683 uint32 childCount = browserAccessibility_->PlatformChildCount();
684 for (uint32 index = 0; index < childCount; ++index) {
685 BrowserAccessibility* child =
686 browserAccessibility_->PlatformGetChild(index);
687 if (child->HasState(ui::AX_STATE_SELECTED))
688 [ret addObject:child->ToBrowserAccessibilityCocoa()];
696 // Returns the size of this object.
698 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
699 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
702 // Returns a subrole based upon the role.
703 - (NSString*) subrole {
704 ui::AXRole browserAccessibilityRole = [self internalRole];
705 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
706 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
707 return @"AXSecureTextField";
710 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
711 return @"AXDescriptionList";
713 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
714 return @"AXContentList";
716 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
719 // Returns all tabs in this subtree.
721 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
723 if ([self internalRole] == ui::AX_ROLE_TAB)
724 [tabSubtree addObject:self];
726 for (uint i=0; i < [[self children] count]; ++i) {
727 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
728 if ([tabChildren count] > 0)
729 [tabSubtree addObjectsFromArray:tabChildren];
736 return NSStringForStringAttribute(
737 browserAccessibility_, ui::AX_ATTR_NAME);
740 - (id)titleUIElement {
742 if (browserAccessibility_->GetIntAttribute(
743 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
744 BrowserAccessibility* titleElement =
745 browserAccessibility_->manager()->GetFromID(titleElementId);
747 return titleElement->ToBrowserAccessibilityCocoa();
749 std::vector<int32> labelledby_ids =
750 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
751 if (labelledby_ids.size() == 1) {
752 BrowserAccessibility* titleElement =
753 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
755 return titleElement->ToBrowserAccessibilityCocoa();
762 StringAttribute urlAttribute =
763 [[self role] isEqualToString:@"AXWebArea"] ?
764 ui::AX_ATTR_DOC_URL :
767 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
771 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
775 // WebCore uses an attachmentView to get the below behavior.
776 // We do not have any native views backing this object, so need
777 // to approximate Cocoa ax behavior best as we can.
778 NSString* role = [self role];
779 if ([role isEqualToString:@"AXHeading"]) {
781 if (browserAccessibility_->GetIntAttribute(
782 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
783 return [NSNumber numberWithInt:level];
785 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
786 // AXValue does not make sense for pure buttons.
788 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
790 bool isAriaPressedDefined;
792 value = browserAccessibility_->GetAriaTristate(
793 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
798 return [NSNumber numberWithInt:value];
800 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
801 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
804 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
806 browserAccessibility_, ui::AX_STATE_SELECTED) ?
810 if (browserAccessibility_->GetBoolAttribute(
811 ui::AX_ATTR_BUTTON_MIXED)) {
814 return [NSNumber numberWithInt:value];
815 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
816 [role isEqualToString:NSAccessibilitySliderRole] ||
817 [role isEqualToString:NSAccessibilityScrollBarRole]) {
819 if (browserAccessibility_->GetFloatAttribute(
820 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
821 return [NSNumber numberWithFloat:floatValue];
823 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
824 int r = browserAccessibility_->GetIntAttribute(
825 ui::AX_ATTR_COLOR_VALUE_RED);
826 int g = browserAccessibility_->GetIntAttribute(
827 ui::AX_ATTR_COLOR_VALUE_GREEN);
828 int b = browserAccessibility_->GetIntAttribute(
829 ui::AX_ATTR_COLOR_VALUE_BLUE);
830 // This string matches the one returned by a native Mac color well.
831 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
832 r / 255., g / 255., b / 255.];
835 return NSStringForStringAttribute(
836 browserAccessibility_, ui::AX_ATTR_VALUE);
839 - (NSString*)valueDescription {
840 return NSStringForStringAttribute(
841 browserAccessibility_, ui::AX_ATTR_VALUE);
844 - (NSValue*)visibleCharacterRange {
845 return [NSValue valueWithRange:
846 NSMakeRange(0, browserAccessibility_->value().length())];
849 - (NSArray*)visibleCells {
850 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
851 const std::vector<int32>& uniqueCellIds =
852 browserAccessibility_->GetIntListAttribute(
853 ui::AX_ATTR_UNIQUE_CELL_IDS);
854 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
855 int id = uniqueCellIds[i];
856 BrowserAccessibility* cell =
857 browserAccessibility_->manager()->GetFromID(id);
859 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
864 - (NSArray*)visibleChildren {
865 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
866 uint32 childCount = browserAccessibility_->PlatformChildCount();
867 for (uint32 index = 0; index < childCount; ++index) {
868 BrowserAccessibilityCocoa* child =
869 browserAccessibility_->PlatformGetChild(index)->
870 ToBrowserAccessibilityCocoa();
871 [ret addObject:child];
876 - (NSArray*)visibleColumns {
877 return [self columns];
880 - (NSArray*)visibleRows {
884 - (NSNumber*)visited {
885 return [NSNumber numberWithBool:
886 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
890 if (!browserAccessibility_)
893 BrowserAccessibilityManagerMac* manager =
894 static_cast<BrowserAccessibilityManagerMac*>(
895 browserAccessibility_->manager());
896 return [manager->parent_view() window];
899 - (NSString*)methodNameForAttribute:(NSString*)attribute {
900 return [attributeToMethodNameMap objectForKey:attribute];
903 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
904 children_.swap(*other);
907 // Returns the accessibility value for the given attribute. If the value isn't
908 // supported this will return nil.
909 - (id)accessibilityAttributeValue:(NSString*)attribute {
910 if (!browserAccessibility_)
914 NSSelectorFromString([self methodNameForAttribute:attribute]);
916 return [self performSelector:selector];
918 // TODO(dtseng): refactor remaining attributes.
919 int selStart, selEnd;
920 if (browserAccessibility_->GetIntAttribute(
921 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
922 browserAccessibility_->
923 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
924 if (selStart > selEnd)
925 std::swap(selStart, selEnd);
926 int selLength = selEnd - selStart;
927 if ([attribute isEqualToString:
928 NSAccessibilityInsertionPointLineNumberAttribute]) {
929 const std::vector<int32>& line_breaks =
930 browserAccessibility_->GetIntListAttribute(
931 ui::AX_ATTR_LINE_BREAKS);
932 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
933 if (line_breaks[i] > selStart)
934 return [NSNumber numberWithInt:i];
936 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
938 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
939 std::string value = browserAccessibility_->GetStringAttribute(
941 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
943 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
944 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
950 // Returns the accessibility value for the given attribute and parameter. If the
951 // value isn't supported this will return nil.
952 - (id)accessibilityAttributeValue:(NSString*)attribute
953 forParameter:(id)parameter {
954 if (!browserAccessibility_)
957 const std::vector<int32>& line_breaks =
958 browserAccessibility_->GetIntListAttribute(
959 ui::AX_ATTR_LINE_BREAKS);
960 int len = static_cast<int>(browserAccessibility_->value().size());
962 if ([attribute isEqualToString:
963 NSAccessibilityStringForRangeParameterizedAttribute]) {
964 NSRange range = [(NSValue*)parameter rangeValue];
965 std::string value = browserAccessibility_->GetStringAttribute(
967 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
970 if ([attribute isEqualToString:
971 NSAccessibilityLineForIndexParameterizedAttribute]) {
972 int index = [(NSNumber*)parameter intValue];
973 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
974 if (line_breaks[i] > index)
975 return [NSNumber numberWithInt:i];
977 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
980 if ([attribute isEqualToString:
981 NSAccessibilityRangeForLineParameterizedAttribute]) {
982 int line_index = [(NSNumber*)parameter intValue];
983 int line_count = static_cast<int>(line_breaks.size()) + 1;
984 if (line_index < 0 || line_index >= line_count)
986 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
987 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
988 return [NSValue valueWithRange:
989 NSMakeRange(start, end - start)];
992 if ([attribute isEqualToString:
993 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
994 if ([self internalRole] != ui::AX_ROLE_TABLE &&
995 [self internalRole] != ui::AX_ROLE_GRID) {
998 if (![parameter isKindOfClass:[NSArray self]])
1000 NSArray* array = parameter;
1001 int column = [[array objectAtIndex:0] intValue];
1002 int row = [[array objectAtIndex:1] intValue];
1003 int num_columns = browserAccessibility_->GetIntAttribute(
1004 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1005 int num_rows = browserAccessibility_->GetIntAttribute(
1006 ui::AX_ATTR_TABLE_ROW_COUNT);
1007 if (column < 0 || column >= num_columns ||
1008 row < 0 || row >= num_rows) {
1012 i < browserAccessibility_->PlatformChildCount();
1014 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1015 if (child->GetRole() != ui::AX_ROLE_ROW)
1018 if (!child->GetIntAttribute(
1019 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1027 j < child->PlatformChildCount();
1029 BrowserAccessibility* cell = child->PlatformGetChild(j);
1030 if (cell->GetRole() != ui::AX_ROLE_CELL)
1033 if (!cell->GetIntAttribute(
1034 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1038 if (colIndex == column)
1039 return cell->ToBrowserAccessibilityCocoa();
1040 if (colIndex > column)
1047 if ([attribute isEqualToString:
1048 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1049 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1051 NSRange range = [(NSValue*)parameter rangeValue];
1052 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1053 range.location, range.length);
1054 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1055 NSSize size = NSMakeSize(rect.width(), rect.height());
1056 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1057 NSRect nsrect = NSMakeRect(
1058 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1059 return [NSValue valueWithRect:nsrect];
1062 // TODO(dtseng): support the following attributes.
1063 if ([attribute isEqualTo:
1064 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1065 [attribute isEqualTo:
1066 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1067 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1068 [attribute isEqualTo:
1069 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1075 // Returns an array of parameterized attributes names that this object will
1077 - (NSArray*)accessibilityParameterizedAttributeNames {
1078 if (!browserAccessibility_)
1081 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1082 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1083 return [NSArray arrayWithObjects:
1084 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1087 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1088 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1089 return [NSArray arrayWithObjects:
1090 NSAccessibilityLineForIndexParameterizedAttribute,
1091 NSAccessibilityRangeForLineParameterizedAttribute,
1092 NSAccessibilityStringForRangeParameterizedAttribute,
1093 NSAccessibilityRangeForPositionParameterizedAttribute,
1094 NSAccessibilityRangeForIndexParameterizedAttribute,
1095 NSAccessibilityBoundsForRangeParameterizedAttribute,
1096 NSAccessibilityRTFForRangeParameterizedAttribute,
1097 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1098 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1101 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1102 return [NSArray arrayWithObjects:
1103 NSAccessibilityBoundsForRangeParameterizedAttribute,
1109 // Returns an array of action names that this object will respond to.
1110 - (NSArray*)accessibilityActionNames {
1111 if (!browserAccessibility_)
1114 NSMutableArray* ret =
1115 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1116 NSString* role = [self role];
1117 // TODO(dtseng): this should only get set when there's a default action.
1118 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1119 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1120 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1121 [ret addObject:NSAccessibilityPressAction];
1127 // Returns a sub-array of values for the given attribute value, starting at
1128 // index, with up to maxCount items. If the given index is out of bounds,
1129 // or there are no values for the given attribute, it will return nil.
1130 // This method is used for querying subsets of values, without having to
1131 // return a large set of data, such as elements with a large number of
1133 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1134 index:(NSUInteger)index
1135 maxCount:(NSUInteger)maxCount {
1136 if (!browserAccessibility_)
1139 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1142 NSUInteger arrayCount = [fullArray count];
1143 if (index >= arrayCount)
1146 if ((index + maxCount) > arrayCount) {
1147 subRange = NSMakeRange(index, arrayCount - index);
1149 subRange = NSMakeRange(index, maxCount);
1151 return [fullArray subarrayWithRange:subRange];
1154 // Returns the count of the specified accessibility array attribute.
1155 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1156 if (!browserAccessibility_)
1159 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1160 return [fullArray count];
1163 // Returns the list of accessibility attributes that this object supports.
1164 - (NSArray*)accessibilityAttributeNames {
1165 if (!browserAccessibility_)
1168 // General attributes.
1169 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1170 NSAccessibilityChildrenAttribute,
1171 NSAccessibilityDescriptionAttribute,
1172 NSAccessibilityEnabledAttribute,
1173 NSAccessibilityFocusedAttribute,
1174 NSAccessibilityHelpAttribute,
1175 NSAccessibilityLinkedUIElementsAttribute,
1176 NSAccessibilityParentAttribute,
1177 NSAccessibilityPositionAttribute,
1178 NSAccessibilityRoleAttribute,
1179 NSAccessibilityRoleDescriptionAttribute,
1180 NSAccessibilitySizeAttribute,
1181 NSAccessibilitySubroleAttribute,
1182 NSAccessibilityTitleAttribute,
1183 NSAccessibilityTopLevelUIElementAttribute,
1184 NSAccessibilityValueAttribute,
1185 NSAccessibilityWindowAttribute,
1192 // Specific role attributes.
1193 NSString* role = [self role];
1194 NSString* subrole = [self subrole];
1195 if ([role isEqualToString:NSAccessibilityTableRole] ||
1196 [role isEqualToString:NSAccessibilityGridRole]) {
1197 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1198 NSAccessibilityColumnsAttribute,
1199 NSAccessibilityVisibleColumnsAttribute,
1200 NSAccessibilityRowsAttribute,
1201 NSAccessibilityVisibleRowsAttribute,
1202 NSAccessibilityVisibleCellsAttribute,
1203 NSAccessibilityHeaderAttribute,
1204 NSAccessibilityColumnHeaderUIElementsAttribute,
1205 NSAccessibilityRowHeaderUIElementsAttribute,
1207 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1208 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1209 NSAccessibilityIndexAttribute,
1210 NSAccessibilityHeaderAttribute,
1211 NSAccessibilityRowsAttribute,
1212 NSAccessibilityVisibleRowsAttribute,
1214 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1215 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1216 NSAccessibilityColumnIndexRangeAttribute,
1217 NSAccessibilityRowIndexRangeAttribute,
1219 } else if ([role isEqualToString:@"AXWebArea"]) {
1220 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1222 @"AXLoadingProgress",
1224 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1225 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1226 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1227 NSAccessibilityInsertionPointLineNumberAttribute,
1228 NSAccessibilityNumberOfCharactersAttribute,
1229 NSAccessibilitySelectedTextAttribute,
1230 NSAccessibilitySelectedTextRangeAttribute,
1231 NSAccessibilityVisibleCharacterRangeAttribute,
1233 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1234 [ret addObject:NSAccessibilityTabsAttribute];
1235 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1236 [role isEqualToString:NSAccessibilitySliderRole] ||
1237 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1238 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1239 NSAccessibilityMaxValueAttribute,
1240 NSAccessibilityMinValueAttribute,
1241 NSAccessibilityOrientationAttribute,
1242 NSAccessibilityValueDescriptionAttribute,
1244 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1245 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1246 NSAccessibilityDisclosingAttribute,
1247 NSAccessibilityDisclosedByRowAttribute,
1248 NSAccessibilityDisclosureLevelAttribute,
1249 NSAccessibilityDisclosedRowsAttribute,
1251 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1252 if (browserAccessibility_->GetParent()) {
1253 base::string16 parentRole;
1254 browserAccessibility_->GetParent()->GetHtmlAttribute(
1255 "role", &parentRole);
1256 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1257 if (parentRole == treegridRole) {
1258 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1259 NSAccessibilityDisclosingAttribute,
1260 NSAccessibilityDisclosedByRowAttribute,
1261 NSAccessibilityDisclosureLevelAttribute,
1262 NSAccessibilityDisclosedRowsAttribute,
1265 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1266 NSAccessibilityIndexAttribute,
1270 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1271 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1272 NSAccessibilityOrientationAttribute,
1273 NSAccessibilitySelectedChildrenAttribute,
1274 NSAccessibilityVisibleChildrenAttribute,
1278 // Add the url attribute only if it has a valid url.
1279 if ([self url] != nil) {
1280 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1281 NSAccessibilityURLAttribute,
1286 if (browserAccessibility_->HasStringAttribute(
1287 ui::AX_ATTR_LIVE_STATUS)) {
1288 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1293 if (browserAccessibility_->HasStringAttribute(
1294 ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1295 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1301 // Title UI Element.
1302 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1303 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1304 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1306 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1307 NSAccessibilityTitleUIElementAttribute,
1310 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1311 // for elements which are referred to by labelledby or are labels
1316 // Returns the index of the child in this objects array of children.
1317 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1318 if (!browserAccessibility_)
1321 NSUInteger index = 0;
1322 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1323 if ([child isEqual:childToCheck])
1330 // Returns whether or not the specified attribute can be set by the
1331 // accessibility API via |accessibilitySetValue:forAttribute:|.
1332 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1333 if (!browserAccessibility_)
1336 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1337 return GetState(browserAccessibility_,
1338 ui::AX_STATE_FOCUSABLE);
1339 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1340 return browserAccessibility_->GetBoolAttribute(
1341 ui::AX_ATTR_CAN_SET_VALUE);
1343 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1344 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1345 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1351 // Returns whether or not this object should be ignored in the accessibilty
1353 - (BOOL)accessibilityIsIgnored {
1354 if (!browserAccessibility_)
1357 return [self isIgnored];
1360 // Performs the given accessibilty action on the webkit accessibility object
1361 // that backs this object.
1362 - (void)accessibilityPerformAction:(NSString*)action {
1363 if (!browserAccessibility_)
1366 // TODO(dmazzoni): Support more actions.
1367 if ([action isEqualToString:NSAccessibilityPressAction]) {
1368 [self delegate]->AccessibilityDoDefaultAction(
1369 browserAccessibility_->GetId());
1370 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1371 NSPoint objOrigin = [self origin];
1372 NSSize size = [[self size] sizeValue];
1373 gfx::Point origin = [self delegate]->AccessibilityOriginInScreen(
1374 gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
1375 origin.Offset(size.width / 2, size.height / 2);
1376 [self delegate]->AccessibilityShowMenu(origin);
1380 // Returns the description of the given action.
1381 - (NSString*)accessibilityActionDescription:(NSString*)action {
1382 if (!browserAccessibility_)
1385 return NSAccessibilityActionDescription(action);
1388 // Sets an override value for a specific accessibility attribute.
1389 // This class does not support this.
1390 - (BOOL)accessibilitySetOverrideValue:(id)value
1391 forAttribute:(NSString*)attribute {
1395 // Sets the value for an accessibility attribute via the accessibility API.
1396 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1397 if (!browserAccessibility_)
1400 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1401 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1402 NSNumber* focusedNumber = value;
1403 BOOL focused = [focusedNumber intValue];
1405 manager->SetFocus(browserAccessibility_, true);
1407 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1408 NSRange range = [(NSValue*)value rangeValue];
1409 [self delegate]->AccessibilitySetTextSelection(
1410 browserAccessibility_->GetId(),
1411 range.location, range.location + range.length);
1415 // Returns the deepest accessibility child that should not be ignored.
1416 // It is assumed that the hit test has been narrowed down to this object
1417 // or one of its children, so this will never return nil unless this
1418 // object is invalid.
1419 - (id)accessibilityHitTest:(NSPoint)point {
1420 if (!browserAccessibility_)
1423 BrowserAccessibilityCocoa* hit = self;
1424 for (BrowserAccessibilityCocoa* child in [self children]) {
1425 if (!child->browserAccessibility_)
1427 NSPoint origin = [child origin];
1428 NSSize size = [[child size] sizeValue];
1430 rect.origin = origin;
1432 if (NSPointInRect(point, rect)) {
1434 id childResult = [child accessibilityHitTest:point];
1435 if (![childResult accessibilityIsIgnored]) {
1441 return NSAccessibilityUnignoredAncestor(hit);
1444 - (BOOL)isEqual:(id)object {
1445 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1447 return ([self hash] == [object hash]);
1450 - (NSUInteger)hash {
1451 // Potentially called during dealloc.
1452 if (!browserAccessibility_)
1453 return [super hash];
1454 return browserAccessibility_->GetId();
1457 - (BOOL)accessibilityShouldUseUniqueId {