IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_cocoa.mm
blob9e3e771133ef7559e2f975175e4f40ee9632cad6
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/browser/accessibility/browser_accessibility_manager.h"
16 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
17 #include "content/public/common/content_client.h"
18 #include "grit/webkit_strings.h"
20 // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
21 // 10.6, and 10.7. It allows accessibility clients to observe events posted on
22 // this object.
23 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
25 using ui::AXNodeData;
26 using content::BrowserAccessibility;
27 using content::BrowserAccessibilityManager;
28 using content::BrowserAccessibilityManagerMac;
29 using content::ContentClient;
30 typedef ui::AXStringAttribute StringAttribute;
32 namespace {
34 // Returns an autoreleased copy of the AXNodeData's attribute.
35 NSString* NSStringForStringAttribute(
36     BrowserAccessibility* browserAccessibility,
37     StringAttribute attribute) {
38   return base::SysUTF8ToNSString(
39       browserAccessibility->GetStringAttribute(attribute));
42 struct MapEntry {
43   ui::AXRole webKitValue;
44   NSString* nativeValue;
47 typedef std::map<ui::AXRole, NSString*> RoleMap;
49 // GetState checks the bitmask used in AXNodeData to check
50 // if the given state was set on the accessibility object.
51 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
52   return ((accessibility->state() >> state) & 1);
55 RoleMap BuildRoleMap() {
56   const MapEntry roles[] = {
57     { ui::AX_ROLE_ALERT, NSAccessibilityGroupRole },
58     { ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole },
59     { ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole },
60     { ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole },
61     { ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole },
62     { ui::AX_ROLE_BANNER, NSAccessibilityGroupRole },
63     { ui::AX_ROLE_BROWSER, NSAccessibilityBrowserRole },
64     { ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole },
65     { ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole },
66     { ui::AX_ROLE_CANVAS, NSAccessibilityImageRole },
67     { ui::AX_ROLE_CELL, @"AXCell" },
68     { ui::AX_ROLE_CHECK_BOX, NSAccessibilityCheckBoxRole },
69     { ui::AX_ROLE_COLOR_WELL, NSAccessibilityColorWellRole },
70     { ui::AX_ROLE_COLUMN, NSAccessibilityColumnRole },
71     { ui::AX_ROLE_COLUMN_HEADER, @"AXCell" },
72     { ui::AX_ROLE_COMBO_BOX, NSAccessibilityComboBoxRole },
73     { ui::AX_ROLE_COMPLEMENTARY, NSAccessibilityGroupRole },
74     { ui::AX_ROLE_CONTENT_INFO, NSAccessibilityGroupRole },
75     { ui::AX_ROLE_DEFINITION, NSAccessibilityGroupRole },
76     { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, NSAccessibilityGroupRole },
77     { ui::AX_ROLE_DESCRIPTION_LIST_TERM, NSAccessibilityGroupRole },
78     { ui::AX_ROLE_DIALOG, NSAccessibilityGroupRole },
79     { ui::AX_ROLE_DIRECTORY, NSAccessibilityListRole },
80     { ui::AX_ROLE_DISCLOSURE_TRIANGLE, NSAccessibilityDisclosureTriangleRole },
81     { ui::AX_ROLE_DIV, NSAccessibilityGroupRole },
82     { ui::AX_ROLE_DOCUMENT, NSAccessibilityGroupRole },
83     { ui::AX_ROLE_DRAWER, NSAccessibilityDrawerRole },
84     { ui::AX_ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole },
85     { ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole },
86     { ui::AX_ROLE_FORM, NSAccessibilityGroupRole },
87     { ui::AX_ROLE_GRID, NSAccessibilityGridRole },
88     { ui::AX_ROLE_GROUP, NSAccessibilityGroupRole },
89     { ui::AX_ROLE_GROW_AREA, NSAccessibilityGrowAreaRole },
90     { ui::AX_ROLE_HEADING, @"AXHeading" },
91     { ui::AX_ROLE_HELP_TAG, NSAccessibilityHelpTagRole },
92     { ui::AX_ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole },
93     { ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole },
94     { ui::AX_ROLE_IMAGE, NSAccessibilityImageRole },
95     { ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
96     { ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
97     { ui::AX_ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
98     { ui::AX_ROLE_LABEL, NSAccessibilityGroupRole },
99     { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
100     { ui::AX_ROLE_LIST, NSAccessibilityListRole },
101     { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
102     { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
103     { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
104     { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
105     { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
106     { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
107     { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
108     { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
109     { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
110     { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
111     { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
112     { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
113     { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
114     { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
115     { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
116     { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
117     { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
118     { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
119     { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
120     { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
121     { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
122     { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
123     { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
124     { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
125     { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
126     { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
127     { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
128     { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
129     { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
130     { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
131     { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
132     { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
133     { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
134     { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
135     { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
136     { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
137     { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
138     { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
139     { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
140     { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
141     { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
142     { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
143     { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
144     { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
145     { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
146     { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
147     { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
148     { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
149     { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
150     { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
151     { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityButtonRole },
152     { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
153     { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
154     { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
155     { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
156     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
157     { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
158     { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
159     { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
161     // TODO(dtseng): we don't correctly support the attributes for these roles.
162     // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
163   };
165   RoleMap role_map;
166   for (size_t i = 0; i < arraysize(roles); ++i)
167     role_map[roles[i].webKitValue] = roles[i].nativeValue;
168   return role_map;
171 // A mapping of webkit roles to native roles.
172 NSString* NativeRoleFromAXRole(
173     const ui::AXRole& role) {
174   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
175                          (BuildRoleMap()));
176   RoleMap::iterator it = web_accessibility_to_native_role.find(role);
177   if (it != web_accessibility_to_native_role.end())
178     return it->second;
179   else
180     return NSAccessibilityUnknownRole;
183 RoleMap BuildSubroleMap() {
184   const MapEntry subroles[] = {
185     { ui::AX_ROLE_ALERT, @"AXApplicationAlert" },
186     { ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
187     { ui::AX_ROLE_ARTICLE, @"AXDocumentArticle" },
188     { ui::AX_ROLE_DEFINITION, @"AXDefinition" },
189     { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
190     { ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
191     { ui::AX_ROLE_DIALOG, @"AXApplicationDialog" },
192     { ui::AX_ROLE_DOCUMENT, @"AXDocument" },
193     { ui::AX_ROLE_FOOTER, @"AXLandmarkContentInfo" },
194     { ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication" },
195     { ui::AX_ROLE_BANNER, @"AXLandmarkBanner" },
196     { ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary" },
197     { ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo" },
198     { ui::AX_ROLE_MAIN, @"AXLandmarkMain" },
199     { ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation" },
200     { ui::AX_ROLE_SEARCH, @"AXLandmarkSearch" },
201     { ui::AX_ROLE_LOG, @"AXApplicationLog" },
202     { ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee" },
203     { ui::AX_ROLE_MATH, @"AXDocumentMath" },
204     { ui::AX_ROLE_NOTE, @"AXDocumentNote" },
205     { ui::AX_ROLE_REGION, @"AXDocumentRegion" },
206     { ui::AX_ROLE_STATUS, @"AXApplicationStatus" },
207     { ui::AX_ROLE_TAB_PANEL, @"AXTabPanel" },
208     { ui::AX_ROLE_TIMER, @"AXApplicationTimer" },
209     { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
210     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
211   };
213   RoleMap subrole_map;
214   for (size_t i = 0; i < arraysize(subroles); ++i)
215     subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
216   return subrole_map;
219 // A mapping of webkit roles to native subroles.
220 NSString* NativeSubroleFromAXRole(
221     const ui::AXRole& role) {
222   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
223                          (BuildSubroleMap()));
224   RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
225   if (it != web_accessibility_to_native_subrole.end())
226     return it->second;
227   else
228     return nil;
231 // A mapping from an accessibility attribute to its method name.
232 NSDictionary* attributeToMethodNameMap = nil;
234 } // namespace
236 @implementation BrowserAccessibilityCocoa
238 + (void)initialize {
239   const struct {
240     NSString* attribute;
241     NSString* methodName;
242   } attributeToMethodNameContainer[] = {
243     { NSAccessibilityChildrenAttribute, @"children" },
244     { NSAccessibilityColumnsAttribute, @"columns" },
245     { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
246     { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
247     { NSAccessibilityContentsAttribute, @"contents" },
248     { NSAccessibilityDescriptionAttribute, @"description" },
249     { NSAccessibilityDisclosingAttribute, @"disclosing" },
250     { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
251     { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
252     { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
253     { NSAccessibilityEnabledAttribute, @"enabled" },
254     { NSAccessibilityFocusedAttribute, @"focused" },
255     { NSAccessibilityHeaderAttribute, @"header" },
256     { NSAccessibilityHelpAttribute, @"help" },
257     { NSAccessibilityIndexAttribute, @"index" },
258     { NSAccessibilityMaxValueAttribute, @"maxValue" },
259     { NSAccessibilityMinValueAttribute, @"minValue" },
260     { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
261     { NSAccessibilityOrientationAttribute, @"orientation" },
262     { NSAccessibilityParentAttribute, @"parent" },
263     { NSAccessibilityPositionAttribute, @"position" },
264     { NSAccessibilityRoleAttribute, @"role" },
265     { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
266     { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
267     { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
268     { NSAccessibilityRowsAttribute, @"rows" },
269     { NSAccessibilitySizeAttribute, @"size" },
270     { NSAccessibilitySubroleAttribute, @"subrole" },
271     { NSAccessibilityTabsAttribute, @"tabs" },
272     { NSAccessibilityTitleAttribute, @"title" },
273     { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
274     { NSAccessibilityTopLevelUIElementAttribute, @"window" },
275     { NSAccessibilityURLAttribute, @"url" },
276     { NSAccessibilityValueAttribute, @"value" },
277     { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
278     { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
279     { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
280     { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
281     { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
282     { NSAccessibilityWindowAttribute, @"window" },
283     { @"AXAccessKey", @"accessKey" },
284     { @"AXARIAAtomic", @"ariaAtomic" },
285     { @"AXARIABusy", @"ariaBusy" },
286     { @"AXARIALive", @"ariaLive" },
287     { @"AXARIARelevant", @"ariaRelevant" },
288     { @"AXInvalid", @"invalid" },
289     { @"AXLoaded", @"loaded" },
290     { @"AXLoadingProgress", @"loadingProgress" },
291     { @"AXRequired", @"required" },
292     { @"AXVisited", @"visited" },
293   };
295   NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
296   const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
297                                sizeof(attributeToMethodNameContainer[0]);
298   for (size_t i = 0; i < numAttributes; ++i) {
299     [dict setObject:attributeToMethodNameContainer[i].methodName
300              forKey:attributeToMethodNameContainer[i].attribute];
301   }
302   attributeToMethodNameMap = dict;
303   dict = nil;
306 - (id)initWithObject:(BrowserAccessibility*)accessibility
307             delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate {
308   if ((self = [super init])) {
309     browserAccessibility_ = accessibility;
310     delegate_ = delegate;
311   }
312   return self;
315 - (void)detach {
316   if (browserAccessibility_) {
317     NSAccessibilityUnregisterUniqueIdForUIElement(self);
318     browserAccessibility_ = NULL;
319   }
322 - (NSString*)accessKey {
323   return NSStringForStringAttribute(
324       browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
327 - (NSNumber*)ariaAtomic {
328   bool boolValue = browserAccessibility_->GetBoolAttribute(
329       ui::AX_ATTR_LIVE_ATOMIC);
330   return [NSNumber numberWithBool:boolValue];
333 - (NSNumber*)ariaBusy {
334   bool boolValue = browserAccessibility_->GetBoolAttribute(
335       ui::AX_ATTR_LIVE_BUSY);
336   return [NSNumber numberWithBool:boolValue];
339 - (NSString*)ariaLive {
340   return NSStringForStringAttribute(
341       browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
344 - (NSString*)ariaRelevant {
345   return NSStringForStringAttribute(
346       browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
349 // Returns an array of BrowserAccessibilityCocoa objects, representing the
350 // accessibility children of this object.
351 - (NSArray*)children {
352   if (!children_) {
353     uint32 childCount = browserAccessibility_->PlatformChildCount();
354     children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
355     for (uint32 index = 0; index < childCount; ++index) {
356       BrowserAccessibilityCocoa* child =
357           browserAccessibility_->PlatformGetChild(index)->
358               ToBrowserAccessibilityCocoa();
359       if ([child isIgnored])
360         [children_ addObjectsFromArray:[child children]];
361       else
362         [children_ addObject:child];
363     }
365     // Also, add indirect children (if any).
366     const std::vector<int32>& indirectChildIds =
367         browserAccessibility_->GetIntListAttribute(
368             ui::AX_ATTR_INDIRECT_CHILD_IDS);
369     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
370       int32 child_id = indirectChildIds[i];
371       BrowserAccessibility* child =
372           browserAccessibility_->manager()->GetFromRendererID(child_id);
374       // This only became necessary as a result of crbug.com/93095. It should be
375       // a DCHECK in the future.
376       if (child) {
377         BrowserAccessibilityCocoa* child_cocoa =
378             child->ToBrowserAccessibilityCocoa();
379         [children_ addObject:child_cocoa];
380       }
381     }
382   }
383   return children_;
386 - (void)childrenChanged {
387   if (![self isIgnored]) {
388     children_.reset();
389   } else {
390     [browserAccessibility_->parent()->ToBrowserAccessibilityCocoa()
391        childrenChanged];
392   }
395 - (NSArray*)columnHeaders {
396   if ([self internalRole] != ui::AX_ROLE_TABLE &&
397       [self internalRole] != ui::AX_ROLE_GRID) {
398     return nil;
399   }
401   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
402   const std::vector<int32>& uniqueCellIds =
403       browserAccessibility_->GetIntListAttribute(
404           ui::AX_ATTR_UNIQUE_CELL_IDS);
405   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
406     int id = uniqueCellIds[i];
407     BrowserAccessibility* cell =
408         browserAccessibility_->manager()->GetFromRendererID(id);
409     if (cell && cell->role() == ui::AX_ROLE_COLUMN_HEADER)
410       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
411   }
412   return ret;
415 - (NSValue*)columnIndexRange {
416   if ([self internalRole] != ui::AX_ROLE_CELL)
417     return nil;
419   int column = -1;
420   int colspan = -1;
421   browserAccessibility_->GetIntAttribute(
422       ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
423   browserAccessibility_->GetIntAttribute(
424       ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
425   if (column >= 0 && colspan >= 1)
426     return [NSValue valueWithRange:NSMakeRange(column, colspan)];
427   return nil;
430 - (NSArray*)columns {
431   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
432   for (BrowserAccessibilityCocoa* child in [self children]) {
433     if ([[child role] isEqualToString:NSAccessibilityColumnRole])
434       [ret addObject:child];
435   }
436   return ret;
439 - (NSString*)description {
440   std::string description;
441   if (browserAccessibility_->GetStringAttribute(
442           ui::AX_ATTR_DESCRIPTION, &description)) {
443     return base::SysUTF8ToNSString(description);
444   }
446   // If the role is anything other than an image, or if there's
447   // a title or title UI element, just return an empty string.
448   if (![[self role] isEqualToString:NSAccessibilityImageRole])
449     return @"";
450   if (browserAccessibility_->HasStringAttribute(
451           ui::AX_ATTR_NAME)) {
452     return @"";
453   }
454   if ([self titleUIElement])
455     return @"";
457   // The remaining case is an image where there's no other title.
458   // Return the base part of the filename as the description.
459   std::string url;
460   if (browserAccessibility_->GetStringAttribute(
461           ui::AX_ATTR_URL, &url)) {
462     // Given a url like http://foo.com/bar/baz.png, just return the
463     // base name, e.g., "baz.png".
464     size_t leftIndex = url.rfind('/');
465     std::string basename =
466         leftIndex != std::string::npos ? url.substr(leftIndex) : url;
467     return base::SysUTF8ToNSString(basename);
468   }
470   return @"";
473 - (NSNumber*)disclosing {
474   if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
475     return [NSNumber numberWithBool:
476         GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
477   } else {
478     return nil;
479   }
482 - (id)disclosedByRow {
483   // The row that contains this row.
484   // It should be the same as the first parent that is a treeitem.
485   return nil;
488 - (NSNumber*)disclosureLevel {
489   ui::AXRole role = [self internalRole];
490   if (role == ui::AX_ROLE_ROW ||
491       role == ui::AX_ROLE_TREE_ITEM) {
492     int level = browserAccessibility_->GetIntAttribute(
493         ui::AX_ATTR_HIERARCHICAL_LEVEL);
494     // Mac disclosureLevel is 0-based, but web levels are 1-based.
495     if (level > 0)
496       level--;
497     return [NSNumber numberWithInt:level];
498   } else {
499     return nil;
500   }
503 - (id)disclosedRows {
504   // The rows that are considered inside this row.
505   return nil;
508 - (NSNumber*)enabled {
509   return [NSNumber numberWithBool:
510       GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
513 - (NSNumber*)focused {
514   BrowserAccessibilityManager* manager = browserAccessibility_->manager();
515   NSNumber* ret = [NSNumber numberWithBool:
516       manager->GetFocus(NULL) == browserAccessibility_];
517   return ret;
520 - (id)header {
521   int headerElementId = -1;
522   if ([self internalRole] == ui::AX_ROLE_TABLE ||
523       [self internalRole] == ui::AX_ROLE_GRID) {
524     browserAccessibility_->GetIntAttribute(
525         ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
526   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
527     browserAccessibility_->GetIntAttribute(
528         ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
529   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
530     browserAccessibility_->GetIntAttribute(
531         ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
532   }
534   if (headerElementId > 0) {
535     BrowserAccessibility* headerObject =
536         browserAccessibility_->manager()->GetFromRendererID(headerElementId);
537     if (headerObject)
538       return headerObject->ToBrowserAccessibilityCocoa();
539   }
540   return nil;
543 - (NSString*)help {
544   return NSStringForStringAttribute(
545       browserAccessibility_, ui::AX_ATTR_HELP);
548 - (NSNumber*)index {
549   if ([self internalRole] == ui::AX_ROLE_COLUMN) {
550     int columnIndex = browserAccessibility_->GetIntAttribute(
551           ui::AX_ATTR_TABLE_COLUMN_INDEX);
552     return [NSNumber numberWithInt:columnIndex];
553   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
554     int rowIndex = browserAccessibility_->GetIntAttribute(
555         ui::AX_ATTR_TABLE_ROW_INDEX);
556     return [NSNumber numberWithInt:rowIndex];
557   }
559   return nil;
562 // Returns whether or not this node should be ignored in the
563 // accessibility tree.
564 - (BOOL)isIgnored {
565   return [[self role] isEqualToString:NSAccessibilityUnknownRole];
568 - (NSString*)invalid {
569   base::string16 invalidUTF;
570   if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
571     return NULL;
572   NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
573   if ([invalid isEqualToString:@"false"] ||
574       [invalid isEqualToString:@""]) {
575     return @"false";
576   }
577   return invalid;
580 - (NSNumber*)loaded {
581   return [NSNumber numberWithBool:YES];
584 - (NSNumber*)loadingProgress {
585   float floatValue = browserAccessibility_->GetFloatAttribute(
586       ui::AX_ATTR_DOC_LOADING_PROGRESS);
587   return [NSNumber numberWithFloat:floatValue];
590 - (NSNumber*)maxValue {
591   float floatValue = browserAccessibility_->GetFloatAttribute(
592       ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
593   return [NSNumber numberWithFloat:floatValue];
596 - (NSNumber*)minValue {
597   float floatValue = browserAccessibility_->GetFloatAttribute(
598       ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
599   return [NSNumber numberWithFloat:floatValue];
602 - (NSString*)orientation {
603   // We present a spin button as a vertical slider, with a role description
604   // of "spin button".
605   if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
606     return NSAccessibilityVerticalOrientationValue;
608   if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
609     return NSAccessibilityVerticalOrientationValue;
610   else
611     return NSAccessibilityHorizontalOrientationValue;
614 - (NSNumber*)numberOfCharacters {
615   return [NSNumber numberWithInt:browserAccessibility_->value().length()];
618 // The origin of this accessibility object in the page's document.
619 // This is relative to webkit's top-left origin, not Cocoa's
620 // bottom-left origin.
621 - (NSPoint)origin {
622   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
623   return NSMakePoint(bounds.x(), bounds.y());
626 - (id)parent {
627   // A nil parent means we're the root.
628   if (browserAccessibility_->parent()) {
629     return NSAccessibilityUnignoredAncestor(
630         browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
631   } else {
632     // Hook back up to RenderWidgetHostViewCocoa.
633     BrowserAccessibilityManagerMac* manager =
634         static_cast<BrowserAccessibilityManagerMac*>(
635             browserAccessibility_->manager());
636     return manager->parent_view();
637   }
640 - (NSValue*)position {
641   NSPoint origin = [self origin];
642   NSSize size = [[self size] sizeValue];
643   NSPoint pointInScreen =
644       [delegate_ accessibilityPointInScreen:origin size:size];
645   return [NSValue valueWithPoint:pointInScreen];
648 - (NSNumber*)required {
649   return [NSNumber numberWithBool:
650       GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
653 // Returns an enum indicating the role from browserAccessibility_.
654 - (ui::AXRole)internalRole {
655   return static_cast<ui::AXRole>(browserAccessibility_->role());
658 // Returns a string indicating the NSAccessibility role of this object.
659 - (NSString*)role {
660   ui::AXRole role = [self internalRole];
661   if (role == ui::AX_ROLE_CANVAS &&
662       browserAccessibility_->GetBoolAttribute(
663           ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
664     return NSAccessibilityGroupRole;
665   }
666   return NativeRoleFromAXRole(role);
669 // Returns a string indicating the role description of this object.
670 - (NSString*)roleDescription {
671   NSString* role = [self role];
673   ContentClient* content_client = content::GetContentClient();
675   // The following descriptions are specific to webkit.
676   if ([role isEqualToString:@"AXWebArea"]) {
677     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
678         IDS_AX_ROLE_WEB_AREA));
679   }
681   if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
682     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
683         IDS_AX_ROLE_LINK));
684   }
686   if ([role isEqualToString:@"AXHeading"]) {
687     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
688         IDS_AX_ROLE_HEADING));
689   }
691   if ([role isEqualToString:NSAccessibilityGroupRole] ||
692       [role isEqualToString:NSAccessibilityRadioButtonRole]) {
693     std::string role;
694     if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
695       ui::AXRole internalRole = [self internalRole];
696       if ((internalRole != ui::AX_ROLE_GROUP &&
697            internalRole != ui::AX_ROLE_LIST_ITEM) ||
698           internalRole == ui::AX_ROLE_TAB) {
699         // TODO(dtseng): This is not localized; see crbug/84814.
700         return base::SysUTF8ToNSString(role);
701       }
702     }
703   }
705   switch([self internalRole]) {
706   case ui::AX_ROLE_FOOTER:
707     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
708         IDS_AX_ROLE_FOOTER));
709   case ui::AX_ROLE_SPIN_BUTTON:
710     // This control is similar to what VoiceOver calls a "stepper".
711     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
712         IDS_AX_ROLE_STEPPER));
713   default:
714     break;
715   }
717   return NSAccessibilityRoleDescription(role, nil);
720 - (NSArray*)rowHeaders {
721   if ([self internalRole] != ui::AX_ROLE_TABLE &&
722       [self internalRole] != ui::AX_ROLE_GRID) {
723     return nil;
724   }
726   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
727   const std::vector<int32>& uniqueCellIds =
728       browserAccessibility_->GetIntListAttribute(
729           ui::AX_ATTR_UNIQUE_CELL_IDS);
730   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
731     int id = uniqueCellIds[i];
732     BrowserAccessibility* cell =
733         browserAccessibility_->manager()->GetFromRendererID(id);
734     if (cell && cell->role() == ui::AX_ROLE_ROW_HEADER)
735       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
736   }
737   return ret;
740 - (NSValue*)rowIndexRange {
741   if ([self internalRole] != ui::AX_ROLE_CELL)
742     return nil;
744   int row = -1;
745   int rowspan = -1;
746   browserAccessibility_->GetIntAttribute(
747       ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
748   browserAccessibility_->GetIntAttribute(
749       ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
750   if (row >= 0 && rowspan >= 1)
751     return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
752   return nil;
755 - (NSArray*)rows {
756   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
758   if ([self internalRole] == ui::AX_ROLE_TABLE||
759       [self internalRole] == ui::AX_ROLE_GRID) {
760     for (BrowserAccessibilityCocoa* child in [self children]) {
761       if ([[child role] isEqualToString:NSAccessibilityRowRole])
762         [ret addObject:child];
763     }
764   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
765     const std::vector<int32>& indirectChildIds =
766         browserAccessibility_->GetIntListAttribute(
767             ui::AX_ATTR_INDIRECT_CHILD_IDS);
768     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
769       int id = indirectChildIds[i];
770       BrowserAccessibility* rowElement =
771           browserAccessibility_->manager()->GetFromRendererID(id);
772       if (rowElement)
773         [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
774     }
775   }
777   return ret;
780 // Returns the size of this object.
781 - (NSValue*)size {
782   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
783   return  [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
786 // Returns a subrole based upon the role.
787 - (NSString*) subrole {
788   ui::AXRole browserAccessibilityRole = [self internalRole];
789   if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
790       GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
791     return @"AXSecureTextField";
792   }
794   NSString* htmlTag = NSStringForStringAttribute(
795       browserAccessibility_, ui::AX_ATTR_HTML_TAG);
797   if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
798     if ([htmlTag isEqualToString:@"ul"] ||
799         [htmlTag isEqualToString:@"ol"]) {
800       return @"AXContentList";
801     } else if ([htmlTag isEqualToString:@"dl"]) {
802       return @"AXDescriptionList";
803     }
804   }
806   return NativeSubroleFromAXRole(browserAccessibilityRole);
809 // Returns all tabs in this subtree.
810 - (NSArray*)tabs {
811   NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
813   if ([self internalRole] == ui::AX_ROLE_TAB)
814     [tabSubtree addObject:self];
816   for (uint i=0; i < [[self children] count]; ++i) {
817     NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
818     if ([tabChildren count] > 0)
819       [tabSubtree addObjectsFromArray:tabChildren];
820   }
822   return tabSubtree;
825 - (NSString*)title {
826   return NSStringForStringAttribute(
827       browserAccessibility_, ui::AX_ATTR_NAME);
830 - (id)titleUIElement {
831   int titleElementId;
832   if (browserAccessibility_->GetIntAttribute(
833           ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
834     BrowserAccessibility* titleElement =
835         browserAccessibility_->manager()->GetFromRendererID(titleElementId);
836     if (titleElement)
837       return titleElement->ToBrowserAccessibilityCocoa();
838   }
839   return nil;
842 - (NSString*)url {
843   StringAttribute urlAttribute =
844       [[self role] isEqualToString:@"AXWebArea"] ?
845           ui::AX_ATTR_DOC_URL :
846           ui::AX_ATTR_URL;
847   return NSStringForStringAttribute(browserAccessibility_, urlAttribute);
850 - (id)value {
851   // WebCore uses an attachmentView to get the below behavior.
852   // We do not have any native views backing this object, so need
853   // to approximate Cocoa ax behavior best as we can.
854   NSString* role = [self role];
855   if ([role isEqualToString:@"AXHeading"]) {
856     int level = 0;
857     if (browserAccessibility_->GetIntAttribute(
858             ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
859       return [NSNumber numberWithInt:level];
860     }
861   } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
862     // AXValue does not make sense for pure buttons.
863     return @"";
864   } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
865              [role isEqualToString:NSAccessibilityRadioButtonRole]) {
866     int value = 0;
867     value = GetState(
868         browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
869     value = GetState(
870         browserAccessibility_, ui::AX_STATE_SELECTED) ?
871             1 :
872             value;
874     if (browserAccessibility_->GetBoolAttribute(
875         ui::AX_ATTR_BUTTON_MIXED)) {
876       value = 2;
877     }
878     return [NSNumber numberWithInt:value];
879   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
880              [role isEqualToString:NSAccessibilitySliderRole] ||
881              [role isEqualToString:NSAccessibilityScrollBarRole]) {
882     float floatValue;
883     if (browserAccessibility_->GetFloatAttribute(
884             ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
885       return [NSNumber numberWithFloat:floatValue];
886     }
887   } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
888     int r = browserAccessibility_->GetIntAttribute(
889         ui::AX_ATTR_COLOR_VALUE_RED);
890     int g = browserAccessibility_->GetIntAttribute(
891         ui::AX_ATTR_COLOR_VALUE_GREEN);
892     int b = browserAccessibility_->GetIntAttribute(
893         ui::AX_ATTR_COLOR_VALUE_BLUE);
894     // This string matches the one returned by a native Mac color well.
895     return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
896                 r / 255., g / 255., b / 255.];
897   }
899   return NSStringForStringAttribute(
900       browserAccessibility_, ui::AX_ATTR_VALUE);
903 - (NSString*)valueDescription {
904   return NSStringForStringAttribute(
905       browserAccessibility_, ui::AX_ATTR_VALUE);
908 - (NSValue*)visibleCharacterRange {
909   return [NSValue valueWithRange:
910       NSMakeRange(0, browserAccessibility_->value().length())];
913 - (NSArray*)visibleCells {
914   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
915   const std::vector<int32>& uniqueCellIds =
916       browserAccessibility_->GetIntListAttribute(
917           ui::AX_ATTR_UNIQUE_CELL_IDS);
918   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
919     int id = uniqueCellIds[i];
920     BrowserAccessibility* cell =
921         browserAccessibility_->manager()->GetFromRendererID(id);
922     if (cell)
923       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
924   }
925   return ret;
928 - (NSArray*)visibleColumns {
929   return [self columns];
932 - (NSArray*)visibleRows {
933   return [self rows];
936 - (NSNumber*)visited {
937   return [NSNumber numberWithBool:
938       GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
941 - (id)window {
942   return [delegate_ window];
945 - (NSString*)methodNameForAttribute:(NSString*)attribute {
946   return [attributeToMethodNameMap objectForKey:attribute];
949 // Returns the accessibility value for the given attribute.  If the value isn't
950 // supported this will return nil.
951 - (id)accessibilityAttributeValue:(NSString*)attribute {
952   if (!browserAccessibility_)
953     return nil;
955   SEL selector =
956       NSSelectorFromString([self methodNameForAttribute:attribute]);
957   if (selector)
958     return [self performSelector:selector];
960   // TODO(dtseng): refactor remaining attributes.
961   int selStart, selEnd;
962   if (browserAccessibility_->GetIntAttribute(
963           ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
964       browserAccessibility_->
965           GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
966     if (selStart > selEnd)
967       std::swap(selStart, selEnd);
968     int selLength = selEnd - selStart;
969     if ([attribute isEqualToString:
970         NSAccessibilityInsertionPointLineNumberAttribute]) {
971       const std::vector<int32>& line_breaks =
972           browserAccessibility_->GetIntListAttribute(
973               ui::AX_ATTR_LINE_BREAKS);
974       for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
975         if (line_breaks[i] > selStart)
976           return [NSNumber numberWithInt:i];
977       }
978       return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
979     }
980     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
981       std::string value = browserAccessibility_->GetStringAttribute(
982           ui::AX_ATTR_VALUE);
983       return base::SysUTF8ToNSString(value.substr(selStart, selLength));
984     }
985     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
986       return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
987     }
988   }
989   return nil;
992 // Returns the accessibility value for the given attribute and parameter. If the
993 // value isn't supported this will return nil.
994 - (id)accessibilityAttributeValue:(NSString*)attribute
995                      forParameter:(id)parameter {
996   if (!browserAccessibility_)
997     return nil;
999   const std::vector<int32>& line_breaks =
1000       browserAccessibility_->GetIntListAttribute(
1001           ui::AX_ATTR_LINE_BREAKS);
1002   int len = static_cast<int>(browserAccessibility_->value().size());
1004   if ([attribute isEqualToString:
1005       NSAccessibilityStringForRangeParameterizedAttribute]) {
1006     NSRange range = [(NSValue*)parameter rangeValue];
1007     std::string value = browserAccessibility_->GetStringAttribute(
1008         ui::AX_ATTR_VALUE);
1009     return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1010   }
1012   if ([attribute isEqualToString:
1013       NSAccessibilityLineForIndexParameterizedAttribute]) {
1014     int index = [(NSNumber*)parameter intValue];
1015     for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1016       if (line_breaks[i] > index)
1017         return [NSNumber numberWithInt:i];
1018     }
1019     return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1020   }
1022   if ([attribute isEqualToString:
1023       NSAccessibilityRangeForLineParameterizedAttribute]) {
1024     int line_index = [(NSNumber*)parameter intValue];
1025     int line_count = static_cast<int>(line_breaks.size()) + 1;
1026     if (line_index < 0 || line_index >= line_count)
1027       return nil;
1028     int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1029     int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1030     return [NSValue valueWithRange:
1031         NSMakeRange(start, end - start)];
1032   }
1034   if ([attribute isEqualToString:
1035       NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1036     if ([self internalRole] != ui::AX_ROLE_TABLE &&
1037         [self internalRole] != ui::AX_ROLE_GRID) {
1038       return nil;
1039     }
1040     if (![parameter isKindOfClass:[NSArray self]])
1041       return nil;
1042     NSArray* array = parameter;
1043     int column = [[array objectAtIndex:0] intValue];
1044     int row = [[array objectAtIndex:1] intValue];
1045     int num_columns = browserAccessibility_->GetIntAttribute(
1046         ui::AX_ATTR_TABLE_COLUMN_COUNT);
1047     int num_rows = browserAccessibility_->GetIntAttribute(
1048         ui::AX_ATTR_TABLE_ROW_COUNT);
1049     if (column < 0 || column >= num_columns ||
1050         row < 0 || row >= num_rows) {
1051       return nil;
1052     }
1053     for (size_t i = 0;
1054          i < browserAccessibility_->PlatformChildCount();
1055          ++i) {
1056       BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1057       if (child->role() != ui::AX_ROLE_ROW)
1058         continue;
1059       int rowIndex;
1060       if (!child->GetIntAttribute(
1061               ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1062         continue;
1063       }
1064       if (rowIndex < row)
1065         continue;
1066       if (rowIndex > row)
1067         break;
1068       for (size_t j = 0;
1069            j < child->PlatformChildCount();
1070            ++j) {
1071         BrowserAccessibility* cell = child->PlatformGetChild(j);
1072         if (cell->role() != ui::AX_ROLE_CELL)
1073           continue;
1074         int colIndex;
1075         if (!cell->GetIntAttribute(
1076                 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1077                 &colIndex)) {
1078           continue;
1079         }
1080         if (colIndex == column)
1081           return cell->ToBrowserAccessibilityCocoa();
1082         if (colIndex > column)
1083           break;
1084       }
1085     }
1086     return nil;
1087   }
1089   if ([attribute isEqualToString:
1090       NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1091     if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1092       return nil;
1093     NSRange range = [(NSValue*)parameter rangeValue];
1094     gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1095         range.location, range.length);
1096     NSPoint origin = NSMakePoint(rect.x(), rect.y());
1097     NSSize size = NSMakeSize(rect.width(), rect.height());
1098     NSPoint pointInScreen =
1099         [delegate_ accessibilityPointInScreen:origin size:size];
1100     NSRect nsrect = NSMakeRect(
1101         pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1102     return [NSValue valueWithRect:nsrect];
1103   }
1105   // TODO(dtseng): support the following attributes.
1106   if ([attribute isEqualTo:
1107           NSAccessibilityRangeForPositionParameterizedAttribute] ||
1108       [attribute isEqualTo:
1109           NSAccessibilityRangeForIndexParameterizedAttribute] ||
1110       [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1111       [attribute isEqualTo:
1112           NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1113     return nil;
1114   }
1115   return nil;
1118 // Returns an array of parameterized attributes names that this object will
1119 // respond to.
1120 - (NSArray*)accessibilityParameterizedAttributeNames {
1121   if (!browserAccessibility_)
1122     return nil;
1124   if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1125       [[self role] isEqualToString:NSAccessibilityGridRole]) {
1126     return [NSArray arrayWithObjects:
1127         NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1128         nil];
1129   }
1130   if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1131       [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1132     return [NSArray arrayWithObjects:
1133         NSAccessibilityLineForIndexParameterizedAttribute,
1134         NSAccessibilityRangeForLineParameterizedAttribute,
1135         NSAccessibilityStringForRangeParameterizedAttribute,
1136         NSAccessibilityRangeForPositionParameterizedAttribute,
1137         NSAccessibilityRangeForIndexParameterizedAttribute,
1138         NSAccessibilityBoundsForRangeParameterizedAttribute,
1139         NSAccessibilityRTFForRangeParameterizedAttribute,
1140         NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1141         NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1142         nil];
1143   }
1144   if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1145     return [NSArray arrayWithObjects:
1146         NSAccessibilityBoundsForRangeParameterizedAttribute,
1147         nil];
1148   }
1149   return nil;
1152 // Returns an array of action names that this object will respond to.
1153 - (NSArray*)accessibilityActionNames {
1154   if (!browserAccessibility_)
1155     return nil;
1157   NSMutableArray* ret =
1158       [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1159   NSString* role = [self role];
1160   // TODO(dtseng): this should only get set when there's a default action.
1161   if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1162       ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1163       ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1164     [ret addObject:NSAccessibilityPressAction];
1165   }
1167   return ret;
1170 // Returns a sub-array of values for the given attribute value, starting at
1171 // index, with up to maxCount items.  If the given index is out of bounds,
1172 // or there are no values for the given attribute, it will return nil.
1173 // This method is used for querying subsets of values, without having to
1174 // return a large set of data, such as elements with a large number of
1175 // children.
1176 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1177                                         index:(NSUInteger)index
1178                                      maxCount:(NSUInteger)maxCount {
1179   if (!browserAccessibility_)
1180     return nil;
1182   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1183   if (!fullArray)
1184     return nil;
1185   NSUInteger arrayCount = [fullArray count];
1186   if (index >= arrayCount)
1187     return nil;
1188   NSRange subRange;
1189   if ((index + maxCount) > arrayCount) {
1190     subRange = NSMakeRange(index, arrayCount - index);
1191   } else {
1192     subRange = NSMakeRange(index, maxCount);
1193   }
1194   return [fullArray subarrayWithRange:subRange];
1197 // Returns the count of the specified accessibility array attribute.
1198 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1199   if (!browserAccessibility_)
1200     return nil;
1202   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1203   return [fullArray count];
1206 // Returns the list of accessibility attributes that this object supports.
1207 - (NSArray*)accessibilityAttributeNames {
1208   if (!browserAccessibility_)
1209     return nil;
1211   // General attributes.
1212   NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1213       NSAccessibilityChildrenAttribute,
1214       NSAccessibilityDescriptionAttribute,
1215       NSAccessibilityEnabledAttribute,
1216       NSAccessibilityFocusedAttribute,
1217       NSAccessibilityHelpAttribute,
1218       NSAccessibilityParentAttribute,
1219       NSAccessibilityPositionAttribute,
1220       NSAccessibilityRoleAttribute,
1221       NSAccessibilityRoleDescriptionAttribute,
1222       NSAccessibilitySizeAttribute,
1223       NSAccessibilitySubroleAttribute,
1224       NSAccessibilityTitleAttribute,
1225       NSAccessibilityTopLevelUIElementAttribute,
1226       NSAccessibilityValueAttribute,
1227       NSAccessibilityWindowAttribute,
1228       NSAccessibilityURLAttribute,
1229       @"AXAccessKey",
1230       @"AXInvalid",
1231       @"AXRequired",
1232       @"AXVisited",
1233       nil];
1235   // Specific role attributes.
1236   NSString* role = [self role];
1237   NSString* subrole = [self subrole];
1238   if ([role isEqualToString:NSAccessibilityTableRole] ||
1239       [role isEqualToString:NSAccessibilityGridRole]) {
1240     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1241         NSAccessibilityColumnsAttribute,
1242         NSAccessibilityVisibleColumnsAttribute,
1243         NSAccessibilityRowsAttribute,
1244         NSAccessibilityVisibleRowsAttribute,
1245         NSAccessibilityVisibleCellsAttribute,
1246         NSAccessibilityHeaderAttribute,
1247         NSAccessibilityColumnHeaderUIElementsAttribute,
1248         NSAccessibilityRowHeaderUIElementsAttribute,
1249         nil]];
1250   } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1251     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1252         NSAccessibilityIndexAttribute,
1253         NSAccessibilityHeaderAttribute,
1254         NSAccessibilityRowsAttribute,
1255         NSAccessibilityVisibleRowsAttribute,
1256         nil]];
1257   } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1258     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1259         NSAccessibilityColumnIndexRangeAttribute,
1260         NSAccessibilityRowIndexRangeAttribute,
1261         nil]];
1262   } else if ([role isEqualToString:@"AXWebArea"]) {
1263     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1264         @"AXLoaded",
1265         @"AXLoadingProgress",
1266         nil]];
1267   } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1268              [role isEqualToString:NSAccessibilityTextAreaRole]) {
1269     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1270         NSAccessibilityInsertionPointLineNumberAttribute,
1271         NSAccessibilityNumberOfCharactersAttribute,
1272         NSAccessibilitySelectedTextAttribute,
1273         NSAccessibilitySelectedTextRangeAttribute,
1274         NSAccessibilityVisibleCharacterRangeAttribute,
1275         nil]];
1276   } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1277     [ret addObject:NSAccessibilityTabsAttribute];
1278   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1279              [role isEqualToString:NSAccessibilitySliderRole] ||
1280              [role isEqualToString:NSAccessibilityScrollBarRole]) {
1281     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1282         NSAccessibilityMaxValueAttribute,
1283         NSAccessibilityMinValueAttribute,
1284         NSAccessibilityOrientationAttribute,
1285         NSAccessibilityValueDescriptionAttribute,
1286         nil]];
1287   } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1288     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1289         NSAccessibilityDisclosingAttribute,
1290         NSAccessibilityDisclosedByRowAttribute,
1291         NSAccessibilityDisclosureLevelAttribute,
1292         NSAccessibilityDisclosedRowsAttribute,
1293         nil]];
1294   } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1295     if (browserAccessibility_->parent()) {
1296       base::string16 parentRole;
1297       browserAccessibility_->parent()->GetHtmlAttribute(
1298           "role", &parentRole);
1299       const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1300       if (parentRole == treegridRole) {
1301         [ret addObjectsFromArray:[NSArray arrayWithObjects:
1302             NSAccessibilityDisclosingAttribute,
1303             NSAccessibilityDisclosedByRowAttribute,
1304             NSAccessibilityDisclosureLevelAttribute,
1305             NSAccessibilityDisclosedRowsAttribute,
1306             nil]];
1307       } else {
1308         [ret addObjectsFromArray:[NSArray arrayWithObjects:
1309             NSAccessibilityIndexAttribute,
1310             nil]];
1311       }
1312     }
1313   }
1315   // Live regions.
1316   if (browserAccessibility_->HasStringAttribute(
1317           ui::AX_ATTR_LIVE_STATUS)) {
1318     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1319         @"AXARIALive",
1320         @"AXARIARelevant",
1321         nil]];
1322   }
1323   if (browserAccessibility_->HasStringAttribute(
1324           ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1325     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1326         @"AXARIAAtomic",
1327         @"AXARIABusy",
1328         nil]];
1329   }
1331   // Title UI Element.
1332   if (browserAccessibility_->HasIntAttribute(
1333           ui::AX_ATTR_TITLE_UI_ELEMENT)) {
1334     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1335          NSAccessibilityTitleUIElementAttribute,
1336          nil]];
1337   }
1339   return ret;
1342 // Returns the index of the child in this objects array of children.
1343 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1344   if (!browserAccessibility_)
1345     return nil;
1347   NSUInteger index = 0;
1348   for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1349     if ([child isEqual:childToCheck])
1350       return index;
1351     ++index;
1352   }
1353   return NSNotFound;
1356 // Returns whether or not the specified attribute can be set by the
1357 // accessibility API via |accessibilitySetValue:forAttribute:|.
1358 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1359   if (!browserAccessibility_)
1360     return nil;
1362   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1363     return GetState(browserAccessibility_,
1364         ui::AX_STATE_FOCUSABLE);
1365   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1366     return browserAccessibility_->GetBoolAttribute(
1367         ui::AX_ATTR_CAN_SET_VALUE);
1368   }
1369   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1370       ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1371        [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1372     return YES;
1374   return NO;
1377 // Returns whether or not this object should be ignored in the accessibilty
1378 // tree.
1379 - (BOOL)accessibilityIsIgnored {
1380   if (!browserAccessibility_)
1381     return true;
1383   return [self isIgnored];
1386 // Performs the given accessibilty action on the webkit accessibility object
1387 // that backs this object.
1388 - (void)accessibilityPerformAction:(NSString*)action {
1389   if (!browserAccessibility_)
1390     return;
1392   // TODO(feldstein): Support more actions.
1393   if ([action isEqualToString:NSAccessibilityPressAction])
1394     [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
1395   else if ([action isEqualToString:NSAccessibilityShowMenuAction])
1396     [delegate_ performShowMenuAction:self];
1399 // Returns the description of the given action.
1400 - (NSString*)accessibilityActionDescription:(NSString*)action {
1401   if (!browserAccessibility_)
1402     return nil;
1404   return NSAccessibilityActionDescription(action);
1407 // Sets an override value for a specific accessibility attribute.
1408 // This class does not support this.
1409 - (BOOL)accessibilitySetOverrideValue:(id)value
1410                          forAttribute:(NSString*)attribute {
1411   return NO;
1414 // Sets the value for an accessibility attribute via the accessibility API.
1415 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1416   if (!browserAccessibility_)
1417     return;
1419   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1420     NSNumber* focusedNumber = value;
1421     BOOL focused = [focusedNumber intValue];
1422     [delegate_ setAccessibilityFocus:focused
1423                      accessibilityId:browserAccessibility_->renderer_id()];
1424   }
1425   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1426     NSRange range = [(NSValue*)value rangeValue];
1427     [delegate_
1428         accessibilitySetTextSelection:browserAccessibility_->renderer_id()
1429         startOffset:range.location
1430         endOffset:range.location + range.length];
1431   }
1434 // Returns the deepest accessibility child that should not be ignored.
1435 // It is assumed that the hit test has been narrowed down to this object
1436 // or one of its children, so this will never return nil unless this
1437 // object is invalid.
1438 - (id)accessibilityHitTest:(NSPoint)point {
1439   if (!browserAccessibility_)
1440     return nil;
1442   BrowserAccessibilityCocoa* hit = self;
1443   for (BrowserAccessibilityCocoa* child in [self children]) {
1444     if (!child->browserAccessibility_)
1445       continue;
1446     NSPoint origin = [child origin];
1447     NSSize size = [[child size] sizeValue];
1448     NSRect rect;
1449     rect.origin = origin;
1450     rect.size = size;
1451     if (NSPointInRect(point, rect)) {
1452       hit = child;
1453       id childResult = [child accessibilityHitTest:point];
1454       if (![childResult accessibilityIsIgnored]) {
1455         hit = childResult;
1456         break;
1457       }
1458     }
1459   }
1460   return NSAccessibilityUnignoredAncestor(hit);
1463 - (BOOL)isEqual:(id)object {
1464   if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1465     return NO;
1466   return ([self hash] == [object hash]);
1469 - (NSUInteger)hash {
1470   // Potentially called during dealloc.
1471   if (!browserAccessibility_)
1472     return [super hash];
1473   return browserAccessibility_->renderer_id();
1476 - (BOOL)accessibilityShouldUseUniqueId {
1477   return YES;
1480 @end