cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_cocoa.mm
blobaa11b5fdc330554c319f83482ef62c0fb06f073e
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.
5 #include <execinfo.h>
7 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
9 #include <map>
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
23 // this object.
24 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
26 using ui::AXNodeData;
27 using content::BrowserAccessibility;
28 using content::BrowserAccessibilityManager;
29 using content::BrowserAccessibilityManagerMac;
30 using content::ContentClient;
31 typedef ui::AXStringAttribute StringAttribute;
33 namespace {
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;
52 } // namespace
54 @implementation BrowserAccessibilityCocoa
56 + (void)initialize {
57   const struct {
58     NSString* attribute;
59     NSString* methodName;
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" },
115   };
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];
123   }
124   attributeToMethodNameMap = dict;
125   dict = nil;
128 - (id)initWithObject:(BrowserAccessibility*)accessibility {
129   if ((self = [super init]))
130     browserAccessibility_ = accessibility;
131   return self;
134 - (void)detach {
135   if (browserAccessibility_) {
136     NSAccessibilityUnregisterUniqueIdForUIElement(self);
137     browserAccessibility_ = NULL;
138   }
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 {
171   if (!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]];
180       else
181         [children_ addObject:child];
182     }
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.
195       if (child) {
196         BrowserAccessibilityCocoa* child_cocoa =
197             child->ToBrowserAccessibilityCocoa();
198         [children_ addObject:child_cocoa];
199       }
200     }
201   }
202   return children_;
205 - (void)childrenChanged {
206   if (![self isIgnored]) {
207     children_.reset();
208   } else {
209     [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
210        childrenChanged];
211   }
214 - (NSArray*)columnHeaders {
215   if ([self internalRole] != ui::AX_ROLE_TABLE &&
216       [self internalRole] != ui::AX_ROLE_GRID) {
217     return nil;
218   }
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()];
230   }
231   return ret;
234 - (NSValue*)columnIndexRange {
235   if ([self internalRole] != ui::AX_ROLE_CELL)
236     return nil;
238   int column = -1;
239   int colspan = -1;
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)];
246   return nil;
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];
254   }
255   return ret;
258 - (NSString*)description {
259   std::string description;
260   if (browserAccessibility_->GetStringAttribute(
261           ui::AX_ATTR_DESCRIPTION, &description)) {
262     return base::SysUTF8ToNSString(description);
263   }
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])
268     return @"";
269   if (browserAccessibility_->HasStringAttribute(
270           ui::AX_ATTR_NAME)) {
271     return @"";
272   }
273   if ([self titleUIElement])
274     return @"";
276   // The remaining case is an image where there's no other title.
277   // Return the base part of the filename as the description.
278   std::string url;
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);
287   }
289   return @"";
292 - (NSNumber*)disclosing {
293   if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
294     return [NSNumber numberWithBool:
295         GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
296   } else {
297     return nil;
298   }
301 - (id)disclosedByRow {
302   // The row that contains this row.
303   // It should be the same as the first parent that is a treeitem.
304   return nil;
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.
314     if (level > 0)
315       level--;
316     return [NSNumber numberWithInt:level];
317   } else {
318     return nil;
319   }
322 - (id)disclosedRows {
323   // The rows that are considered inside this row.
324   return nil;
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_];
336   return ret;
339 - (id)header {
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);
351   }
353   if (headerElementId > 0) {
354     BrowserAccessibility* headerObject =
355         browserAccessibility_->manager()->GetFromID(headerElementId);
356     if (headerObject)
357       return headerObject->ToBrowserAccessibilityCocoa();
358   }
359   return nil;
362 - (NSString*)help {
363   return NSStringForStringAttribute(
364       browserAccessibility_, ui::AX_ATTR_HELP);
367 - (NSNumber*)index {
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];
376   }
378   return nil;
381 // Returns whether or not this node should be ignored in the
382 // accessibility tree.
383 - (BOOL)isIgnored {
384   return [[self role] isEqualToString:NSAccessibilityUnknownRole];
387 - (NSString*)invalid {
388   base::string16 invalidUTF;
389   if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
390     return NULL;
391   NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
392   if ([invalid isEqualToString:@"false"] ||
393       [invalid isEqualToString:@""]) {
394     return @"false";
395   }
396   return invalid;
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]);
406     if (element)
407       [outArray addObject:element->ToBrowserAccessibilityCocoa()];
408   }
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)
417     return nil;
418   return ret;
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
445   // of "spin button".
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;
452   }
454   if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
455     return NSAccessibilityVerticalOrientationValue;
456   else
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.
467 - (NSPoint)origin {
468   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
469   return NSMakePoint(bounds.x(), bounds.y());
472 - (id)parent {
473   // A nil parent means we're the root.
474   if (browserAccessibility_->GetParent()) {
475     return NSAccessibilityUnignoredAncestor(
476         browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
477   } else {
478     // Hook back up to RenderWidgetHostViewCocoa.
479     BrowserAccessibilityManagerMac* manager =
480         static_cast<BrowserAccessibilityManagerMac*>(
481             browserAccessibility_->manager());
482     return manager->parent_view();
483   }
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() :
506       nil;
509 - (NSPoint)pointInScreen:(NSPoint)origin
510                     size:(NSSize)size {
511   if (!browserAccessibility_)
512     return NSZeroPoint;
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.
520 - (NSString*)role {
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;
526   }
527   if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
528     bool isAriaPressedDefined;
529     bool isMixed;
530     browserAccessibility_->GetAriaTristate("aria-pressed",
531                                            &isAriaPressedDefined,
532                                            &isMixed);
533     if (isAriaPressedDefined)
534       return NSAccessibilityCheckBoxRole;
535     else
536       return NSAccessibilityButtonRole;
537   }
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));
551   }
553   if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
554     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
555         IDS_AX_ROLE_LINK));
556   }
558   if ([role isEqualToString:@"AXHeading"]) {
559     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
560         IDS_AX_ROLE_HEADING));
561   }
563   if ([role isEqualToString:NSAccessibilityGroupRole] ||
564       [role isEqualToString:NSAccessibilityRadioButtonRole]) {
565     std::string role;
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);
573       }
574     }
575   }
577   switch([self internalRole]) {
578   case ui::AX_ROLE_BANNER:
579     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
580         IDS_AX_ROLE_BANNER));
581     break;
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));
595   default:
596     break;
597   }
599   return NSAccessibilityRoleDescription(role, nil);
602 - (NSArray*)rowHeaders {
603   if ([self internalRole] != ui::AX_ROLE_TABLE &&
604       [self internalRole] != ui::AX_ROLE_GRID) {
605     return nil;
606   }
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()];
618   }
619   return ret;
622 - (NSValue*)rowIndexRange {
623   if ([self internalRole] != ui::AX_ROLE_CELL)
624     return nil;
626   int row = -1;
627   int rowspan = -1;
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)];
634   return nil;
637 - (NSArray*)rows {
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];
645     }
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);
654       if (rowElement)
655         [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
656     }
657   }
659   return ret;
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()];
671   } else {
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()];
680     } else {
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()];
689       }
690     }
691   }
693   return ret;
696 // Returns the size of this object.
697 - (NSValue*)size {
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";
708   }
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.
720 - (NSArray*)tabs {
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];
730   }
732   return tabSubtree;
735 - (NSString*)title {
736   return NSStringForStringAttribute(
737       browserAccessibility_, ui::AX_ATTR_NAME);
740 - (id)titleUIElement {
741   int titleElementId;
742   if (browserAccessibility_->GetIntAttribute(
743           ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
744     BrowserAccessibility* titleElement =
745         browserAccessibility_->manager()->GetFromID(titleElementId);
746     if (titleElement)
747       return titleElement->ToBrowserAccessibilityCocoa();
748   }
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]);
754     if (titleElement)
755       return titleElement->ToBrowserAccessibilityCocoa();
756   }
758   return nil;
761 - (NSURL*)url {
762   StringAttribute urlAttribute =
763       [[self role] isEqualToString:@"AXWebArea"] ?
764           ui::AX_ATTR_DOC_URL :
765           ui::AX_ATTR_URL;
767   std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
768   if (urlStr.empty())
769     return nil;
771   return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
774 - (id)value {
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"]) {
780     int level = 0;
781     if (browserAccessibility_->GetIntAttribute(
782             ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
783       return [NSNumber numberWithInt:level];
784     }
785   } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
786     // AXValue does not make sense for pure buttons.
787     return @"";
788   } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
789     int value = 0;
790     bool isAriaPressedDefined;
791     bool isMixed;
792     value = browserAccessibility_->GetAriaTristate(
793         "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
795     if (isMixed)
796       value = 2;
798     return [NSNumber numberWithInt:value];
800   } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
801              [role isEqualToString:NSAccessibilityRadioButtonRole]) {
802     int value = 0;
803     value = GetState(
804         browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
805     value = GetState(
806         browserAccessibility_, ui::AX_STATE_SELECTED) ?
807             1 :
808             value;
810     if (browserAccessibility_->GetBoolAttribute(
811         ui::AX_ATTR_BUTTON_MIXED)) {
812       value = 2;
813     }
814     return [NSNumber numberWithInt:value];
815   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
816              [role isEqualToString:NSAccessibilitySliderRole] ||
817              [role isEqualToString:NSAccessibilityScrollBarRole]) {
818     float floatValue;
819     if (browserAccessibility_->GetFloatAttribute(
820             ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
821       return [NSNumber numberWithFloat:floatValue];
822     }
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.];
833   }
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);
858     if (cell)
859       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
860   }
861   return ret;
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];
872   }
873   return ret;
876 - (NSArray*)visibleColumns {
877   return [self columns];
880 - (NSArray*)visibleRows {
881   return [self rows];
884 - (NSNumber*)visited {
885   return [NSNumber numberWithBool:
886       GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
889 - (id)window {
890   if (!browserAccessibility_)
891     return nil;
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_)
911     return nil;
913   SEL selector =
914       NSSelectorFromString([self methodNameForAttribute:attribute]);
915   if (selector)
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];
935       }
936       return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
937     }
938     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
939       std::string value = browserAccessibility_->GetStringAttribute(
940           ui::AX_ATTR_VALUE);
941       return base::SysUTF8ToNSString(value.substr(selStart, selLength));
942     }
943     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
944       return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
945     }
946   }
947   return nil;
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_)
955     return nil;
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(
966         ui::AX_ATTR_VALUE);
967     return base::SysUTF8ToNSString(value.substr(range.location, range.length));
968   }
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];
976     }
977     return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
978   }
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)
985       return nil;
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)];
990   }
992   if ([attribute isEqualToString:
993       NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
994     if ([self internalRole] != ui::AX_ROLE_TABLE &&
995         [self internalRole] != ui::AX_ROLE_GRID) {
996       return nil;
997     }
998     if (![parameter isKindOfClass:[NSArray self]])
999       return nil;
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) {
1009       return nil;
1010     }
1011     for (size_t i = 0;
1012          i < browserAccessibility_->PlatformChildCount();
1013          ++i) {
1014       BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1015       if (child->GetRole() != ui::AX_ROLE_ROW)
1016         continue;
1017       int rowIndex;
1018       if (!child->GetIntAttribute(
1019               ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1020         continue;
1021       }
1022       if (rowIndex < row)
1023         continue;
1024       if (rowIndex > row)
1025         break;
1026       for (size_t j = 0;
1027            j < child->PlatformChildCount();
1028            ++j) {
1029         BrowserAccessibility* cell = child->PlatformGetChild(j);
1030         if (cell->GetRole() != ui::AX_ROLE_CELL)
1031           continue;
1032         int colIndex;
1033         if (!cell->GetIntAttribute(
1034                 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1035                 &colIndex)) {
1036           continue;
1037         }
1038         if (colIndex == column)
1039           return cell->ToBrowserAccessibilityCocoa();
1040         if (colIndex > column)
1041           break;
1042       }
1043     }
1044     return nil;
1045   }
1047   if ([attribute isEqualToString:
1048       NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1049     if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1050       return nil;
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];
1060   }
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]) {
1070     return nil;
1071   }
1072   return nil;
1075 // Returns an array of parameterized attributes names that this object will
1076 // respond to.
1077 - (NSArray*)accessibilityParameterizedAttributeNames {
1078   if (!browserAccessibility_)
1079     return nil;
1081   if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1082       [[self role] isEqualToString:NSAccessibilityGridRole]) {
1083     return [NSArray arrayWithObjects:
1084         NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1085         nil];
1086   }
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,
1099         nil];
1100   }
1101   if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1102     return [NSArray arrayWithObjects:
1103         NSAccessibilityBoundsForRangeParameterizedAttribute,
1104         nil];
1105   }
1106   return nil;
1109 // Returns an array of action names that this object will respond to.
1110 - (NSArray*)accessibilityActionNames {
1111   if (!browserAccessibility_)
1112     return nil;
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];
1122   }
1124   return ret;
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
1132 // children.
1133 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1134                                         index:(NSUInteger)index
1135                                      maxCount:(NSUInteger)maxCount {
1136   if (!browserAccessibility_)
1137     return nil;
1139   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1140   if (!fullArray)
1141     return nil;
1142   NSUInteger arrayCount = [fullArray count];
1143   if (index >= arrayCount)
1144     return nil;
1145   NSRange subRange;
1146   if ((index + maxCount) > arrayCount) {
1147     subRange = NSMakeRange(index, arrayCount - index);
1148   } else {
1149     subRange = NSMakeRange(index, maxCount);
1150   }
1151   return [fullArray subarrayWithRange:subRange];
1154 // Returns the count of the specified accessibility array attribute.
1155 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1156   if (!browserAccessibility_)
1157     return 0;
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_)
1166     return nil;
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,
1186       @"AXAccessKey",
1187       @"AXInvalid",
1188       @"AXRequired",
1189       @"AXVisited",
1190       nil];
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,
1206         nil]];
1207   } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1208     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1209         NSAccessibilityIndexAttribute,
1210         NSAccessibilityHeaderAttribute,
1211         NSAccessibilityRowsAttribute,
1212         NSAccessibilityVisibleRowsAttribute,
1213         nil]];
1214   } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1215     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1216         NSAccessibilityColumnIndexRangeAttribute,
1217         NSAccessibilityRowIndexRangeAttribute,
1218         nil]];
1219   } else if ([role isEqualToString:@"AXWebArea"]) {
1220     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1221         @"AXLoaded",
1222         @"AXLoadingProgress",
1223         nil]];
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,
1232         nil]];
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,
1243         nil]];
1244   } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1245     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1246         NSAccessibilityDisclosingAttribute,
1247         NSAccessibilityDisclosedByRowAttribute,
1248         NSAccessibilityDisclosureLevelAttribute,
1249         NSAccessibilityDisclosedRowsAttribute,
1250         nil]];
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,
1263             nil]];
1264       } else {
1265         [ret addObjectsFromArray:[NSArray arrayWithObjects:
1266             NSAccessibilityIndexAttribute,
1267             nil]];
1268       }
1269     }
1270   } else if ([role isEqualToString:NSAccessibilityListRole]) {
1271     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1272         NSAccessibilityOrientationAttribute,
1273         NSAccessibilitySelectedChildrenAttribute,
1274         NSAccessibilityVisibleChildrenAttribute,
1275         nil]];
1276   }
1278   // Add the url attribute only if it has a valid url.
1279   if ([self url] != nil) {
1280     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1281         NSAccessibilityURLAttribute,
1282         nil]];
1283   }
1285   // Live regions.
1286   if (browserAccessibility_->HasStringAttribute(
1287           ui::AX_ATTR_LIVE_STATUS)) {
1288     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1289         @"AXARIALive",
1290         @"AXARIARelevant",
1291         nil]];
1292   }
1293   if (browserAccessibility_->HasStringAttribute(
1294           ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1295     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1296         @"AXARIAAtomic",
1297         @"AXARIABusy",
1298         nil]];
1299   }
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)
1305                             .size() == 1)) {
1306     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1307          NSAccessibilityTitleUIElementAttribute,
1308          nil]];
1309   }
1310   // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1311   // for elements which are referred to by labelledby or are labels
1313   return ret;
1316 // Returns the index of the child in this objects array of children.
1317 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1318   if (!browserAccessibility_)
1319     return 0;
1321   NSUInteger index = 0;
1322   for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1323     if ([child isEqual:childToCheck])
1324       return index;
1325     ++index;
1326   }
1327   return NSNotFound;
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_)
1334     return NO;
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);
1342   }
1343   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1344       ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1345        [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1346     return YES;
1348   return NO;
1351 // Returns whether or not this object should be ignored in the accessibilty
1352 // tree.
1353 - (BOOL)accessibilityIsIgnored {
1354   if (!browserAccessibility_)
1355     return true;
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_)
1364     return;
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);
1377   }
1380 // Returns the description of the given action.
1381 - (NSString*)accessibilityActionDescription:(NSString*)action {
1382   if (!browserAccessibility_)
1383     return nil;
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 {
1392   return NO;
1395 // Sets the value for an accessibility attribute via the accessibility API.
1396 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1397   if (!browserAccessibility_)
1398     return;
1400   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1401     BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1402     NSNumber* focusedNumber = value;
1403     BOOL focused = [focusedNumber intValue];
1404     if (focused)
1405       manager->SetFocus(browserAccessibility_, true);
1406   }
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);
1412   }
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_)
1421     return nil;
1423   BrowserAccessibilityCocoa* hit = self;
1424   for (BrowserAccessibilityCocoa* child in [self children]) {
1425     if (!child->browserAccessibility_)
1426       continue;
1427     NSPoint origin = [child origin];
1428     NSSize size = [[child size] sizeValue];
1429     NSRect rect;
1430     rect.origin = origin;
1431     rect.size = size;
1432     if (NSPointInRect(point, rect)) {
1433       hit = child;
1434       id childResult = [child accessibilityHitTest:point];
1435       if (![childResult accessibilityIsIgnored]) {
1436         hit = childResult;
1437         break;
1438       }
1439     }
1440   }
1441   return NSAccessibilityUnignoredAncestor(hit);
1444 - (BOOL)isEqual:(id)object {
1445   if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1446     return NO;
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 {
1458   return YES;
1461 @end