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