Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_cocoa.mm
blobb5a42ef04e4f5e33acf1cac82cb5b022016b1518
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->GetState() >> 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_IFRAME, NSAccessibilityGroupRole },
94     { ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole },
95     { ui::AX_ROLE_IMAGE, NSAccessibilityImageRole },
96     { ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
97     { ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
98     { ui::AX_ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
99     { ui::AX_ROLE_LABEL_TEXT, NSAccessibilityGroupRole },
100     { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
101     { ui::AX_ROLE_LIST, NSAccessibilityListRole },
102     { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
103     { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
104     { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
105     { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
106     { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
107     { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
108     { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
109     { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
110     { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
111     { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
112     { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
113     { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
114     { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
115     { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
116     { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
117     { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
118     { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
119     { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
120     { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
121     { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
122     { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
123     { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
124     { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
125     { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
126     { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
127     { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
128     { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
129     { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
130     { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
131     { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
132     { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
133     { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
134     { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
135     { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
136     { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
137     { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
138     { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
139     { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
140     { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
141     { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
142     { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
143     { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
144     { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
145     { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
146     { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
147     { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
148     { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
149     { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
150     { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
151     { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
152     { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityCheckBoxRole },
153     { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
154     { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
155     { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
156     { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
157     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
158     { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
159     { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
160     { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
162     // TODO(dtseng): we don't correctly support the attributes for these roles.
163     // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
164   };
166   RoleMap role_map;
167   for (size_t i = 0; i < arraysize(roles); ++i)
168     role_map[roles[i].webKitValue] = roles[i].nativeValue;
169   return role_map;
172 // A mapping of webkit roles to native roles.
173 NSString* NativeRoleFromAXRole(
174     const ui::AXRole& role) {
175   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
176                          (BuildRoleMap()));
177   RoleMap::iterator it = web_accessibility_to_native_role.find(role);
178   if (it != web_accessibility_to_native_role.end())
179     return it->second;
180   else
181     return NSAccessibilityUnknownRole;
184 RoleMap BuildSubroleMap() {
185   const MapEntry subroles[] = {
186     { ui::AX_ROLE_ALERT, @"AXApplicationAlert" },
187     { ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
188     { ui::AX_ROLE_ARTICLE, @"AXDocumentArticle" },
189     { ui::AX_ROLE_DEFINITION, @"AXDefinition" },
190     { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
191     { ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
192     { ui::AX_ROLE_DIALOG, @"AXApplicationDialog" },
193     { ui::AX_ROLE_DOCUMENT, @"AXDocument" },
194     { ui::AX_ROLE_FOOTER, @"AXLandmarkContentInfo" },
195     { ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication" },
196     { ui::AX_ROLE_BANNER, @"AXLandmarkBanner" },
197     { ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary" },
198     { ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo" },
199     { ui::AX_ROLE_MAIN, @"AXLandmarkMain" },
200     { ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation" },
201     { ui::AX_ROLE_SEARCH, @"AXLandmarkSearch" },
202     { ui::AX_ROLE_LOG, @"AXApplicationLog" },
203     { ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee" },
204     { ui::AX_ROLE_MATH, @"AXDocumentMath" },
205     { ui::AX_ROLE_NOTE, @"AXDocumentNote" },
206     { ui::AX_ROLE_REGION, @"AXDocumentRegion" },
207     { ui::AX_ROLE_STATUS, @"AXApplicationStatus" },
208     { ui::AX_ROLE_TAB_PANEL, @"AXTabPanel" },
209     { ui::AX_ROLE_TIMER, @"AXApplicationTimer" },
210     { ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton" },
211     { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
212     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
213   };
215   RoleMap subrole_map;
216   for (size_t i = 0; i < arraysize(subroles); ++i)
217     subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
218   return subrole_map;
221 // A mapping of webkit roles to native subroles.
222 NSString* NativeSubroleFromAXRole(
223     const ui::AXRole& role) {
224   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
225                          (BuildSubroleMap()));
226   RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
227   if (it != web_accessibility_to_native_subrole.end())
228     return it->second;
229   else
230     return nil;
233 // A mapping from an accessibility attribute to its method name.
234 NSDictionary* attributeToMethodNameMap = nil;
236 } // namespace
238 @implementation BrowserAccessibilityCocoa
240 + (void)initialize {
241   const struct {
242     NSString* attribute;
243     NSString* methodName;
244   } attributeToMethodNameContainer[] = {
245     { NSAccessibilityChildrenAttribute, @"children" },
246     { NSAccessibilityColumnsAttribute, @"columns" },
247     { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
248     { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
249     { NSAccessibilityContentsAttribute, @"contents" },
250     { NSAccessibilityDescriptionAttribute, @"description" },
251     { NSAccessibilityDisclosingAttribute, @"disclosing" },
252     { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
253     { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
254     { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
255     { NSAccessibilityEnabledAttribute, @"enabled" },
256     { NSAccessibilityFocusedAttribute, @"focused" },
257     { NSAccessibilityHeaderAttribute, @"header" },
258     { NSAccessibilityHelpAttribute, @"help" },
259     { NSAccessibilityIndexAttribute, @"index" },
260     { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
261     { NSAccessibilityMaxValueAttribute, @"maxValue" },
262     { NSAccessibilityMinValueAttribute, @"minValue" },
263     { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
264     { NSAccessibilityOrientationAttribute, @"orientation" },
265     { NSAccessibilityParentAttribute, @"parent" },
266     { NSAccessibilityPositionAttribute, @"position" },
267     { NSAccessibilityRoleAttribute, @"role" },
268     { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
269     { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
270     { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
271     { NSAccessibilityRowsAttribute, @"rows" },
272     // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
273     { NSAccessibilitySizeAttribute, @"size" },
274     { NSAccessibilitySubroleAttribute, @"subrole" },
275     { NSAccessibilityTabsAttribute, @"tabs" },
276     { NSAccessibilityTitleAttribute, @"title" },
277     { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
278     { NSAccessibilityTopLevelUIElementAttribute, @"window" },
279     { NSAccessibilityURLAttribute, @"url" },
280     { NSAccessibilityValueAttribute, @"value" },
281     { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
282     { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
283     { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
284     { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
285     { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
286     { NSAccessibilityWindowAttribute, @"window" },
287     { @"AXAccessKey", @"accessKey" },
288     { @"AXARIAAtomic", @"ariaAtomic" },
289     { @"AXARIABusy", @"ariaBusy" },
290     { @"AXARIALive", @"ariaLive" },
291     { @"AXARIARelevant", @"ariaRelevant" },
292     { @"AXInvalid", @"invalid" },
293     { @"AXLoaded", @"loaded" },
294     { @"AXLoadingProgress", @"loadingProgress" },
295     { @"AXRequired", @"required" },
296     { @"AXVisited", @"visited" },
297   };
299   NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
300   const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
301                                sizeof(attributeToMethodNameContainer[0]);
302   for (size_t i = 0; i < numAttributes; ++i) {
303     [dict setObject:attributeToMethodNameContainer[i].methodName
304              forKey:attributeToMethodNameContainer[i].attribute];
305   }
306   attributeToMethodNameMap = dict;
307   dict = nil;
310 - (id)initWithObject:(BrowserAccessibility*)accessibility {
311   if ((self = [super init]))
312     browserAccessibility_ = accessibility;
313   return self;
316 - (void)detach {
317   if (browserAccessibility_) {
318     NSAccessibilityUnregisterUniqueIdForUIElement(self);
319     browserAccessibility_ = NULL;
320   }
323 - (NSString*)accessKey {
324   return NSStringForStringAttribute(
325       browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
328 - (NSNumber*)ariaAtomic {
329   bool boolValue = browserAccessibility_->GetBoolAttribute(
330       ui::AX_ATTR_LIVE_ATOMIC);
331   return [NSNumber numberWithBool:boolValue];
334 - (NSNumber*)ariaBusy {
335   bool boolValue = browserAccessibility_->GetBoolAttribute(
336       ui::AX_ATTR_LIVE_BUSY);
337   return [NSNumber numberWithBool:boolValue];
340 - (NSString*)ariaLive {
341   return NSStringForStringAttribute(
342       browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
345 - (NSString*)ariaRelevant {
346   return NSStringForStringAttribute(
347       browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
350 // Returns an array of BrowserAccessibilityCocoa objects, representing the
351 // accessibility children of this object.
352 - (NSArray*)children {
353   if (!children_) {
354     uint32 childCount = browserAccessibility_->PlatformChildCount();
355     children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
356     for (uint32 index = 0; index < childCount; ++index) {
357       BrowserAccessibilityCocoa* child =
358           browserAccessibility_->PlatformGetChild(index)->
359               ToBrowserAccessibilityCocoa();
360       if ([child isIgnored])
361         [children_ addObjectsFromArray:[child children]];
362       else
363         [children_ addObject:child];
364     }
366     // Also, add indirect children (if any).
367     const std::vector<int32>& indirectChildIds =
368         browserAccessibility_->GetIntListAttribute(
369             ui::AX_ATTR_INDIRECT_CHILD_IDS);
370     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
371       int32 child_id = indirectChildIds[i];
372       BrowserAccessibility* child =
373           browserAccessibility_->manager()->GetFromID(child_id);
375       // This only became necessary as a result of crbug.com/93095. It should be
376       // a DCHECK in the future.
377       if (child) {
378         BrowserAccessibilityCocoa* child_cocoa =
379             child->ToBrowserAccessibilityCocoa();
380         [children_ addObject:child_cocoa];
381       }
382     }
383   }
384   return children_;
387 - (void)childrenChanged {
388   if (![self isIgnored]) {
389     children_.reset();
390   } else {
391     [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
392        childrenChanged];
393   }
396 - (NSArray*)columnHeaders {
397   if ([self internalRole] != ui::AX_ROLE_TABLE &&
398       [self internalRole] != ui::AX_ROLE_GRID) {
399     return nil;
400   }
402   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
403   const std::vector<int32>& uniqueCellIds =
404       browserAccessibility_->GetIntListAttribute(
405           ui::AX_ATTR_UNIQUE_CELL_IDS);
406   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
407     int id = uniqueCellIds[i];
408     BrowserAccessibility* cell =
409         browserAccessibility_->manager()->GetFromID(id);
410     if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
411       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
412   }
413   return ret;
416 - (NSValue*)columnIndexRange {
417   if ([self internalRole] != ui::AX_ROLE_CELL)
418     return nil;
420   int column = -1;
421   int colspan = -1;
422   browserAccessibility_->GetIntAttribute(
423       ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
424   browserAccessibility_->GetIntAttribute(
425       ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
426   if (column >= 0 && colspan >= 1)
427     return [NSValue valueWithRange:NSMakeRange(column, colspan)];
428   return nil;
431 - (NSArray*)columns {
432   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
433   for (BrowserAccessibilityCocoa* child in [self children]) {
434     if ([[child role] isEqualToString:NSAccessibilityColumnRole])
435       [ret addObject:child];
436   }
437   return ret;
440 - (NSString*)description {
441   std::string description;
442   if (browserAccessibility_->GetStringAttribute(
443           ui::AX_ATTR_DESCRIPTION, &description)) {
444     return base::SysUTF8ToNSString(description);
445   }
447   // If the role is anything other than an image, or if there's
448   // a title or title UI element, just return an empty string.
449   if (![[self role] isEqualToString:NSAccessibilityImageRole])
450     return @"";
451   if (browserAccessibility_->HasStringAttribute(
452           ui::AX_ATTR_NAME)) {
453     return @"";
454   }
455   if ([self titleUIElement])
456     return @"";
458   // The remaining case is an image where there's no other title.
459   // Return the base part of the filename as the description.
460   std::string url;
461   if (browserAccessibility_->GetStringAttribute(
462           ui::AX_ATTR_URL, &url)) {
463     // Given a url like http://foo.com/bar/baz.png, just return the
464     // base name, e.g., "baz.png".
465     size_t leftIndex = url.rfind('/');
466     std::string basename =
467         leftIndex != std::string::npos ? url.substr(leftIndex) : url;
468     return base::SysUTF8ToNSString(basename);
469   }
471   return @"";
474 - (NSNumber*)disclosing {
475   if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
476     return [NSNumber numberWithBool:
477         GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
478   } else {
479     return nil;
480   }
483 - (id)disclosedByRow {
484   // The row that contains this row.
485   // It should be the same as the first parent that is a treeitem.
486   return nil;
489 - (NSNumber*)disclosureLevel {
490   ui::AXRole role = [self internalRole];
491   if (role == ui::AX_ROLE_ROW ||
492       role == ui::AX_ROLE_TREE_ITEM) {
493     int level = browserAccessibility_->GetIntAttribute(
494         ui::AX_ATTR_HIERARCHICAL_LEVEL);
495     // Mac disclosureLevel is 0-based, but web levels are 1-based.
496     if (level > 0)
497       level--;
498     return [NSNumber numberWithInt:level];
499   } else {
500     return nil;
501   }
504 - (id)disclosedRows {
505   // The rows that are considered inside this row.
506   return nil;
509 - (NSNumber*)enabled {
510   return [NSNumber numberWithBool:
511       GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
514 - (NSNumber*)focused {
515   BrowserAccessibilityManager* manager = browserAccessibility_->manager();
516   NSNumber* ret = [NSNumber numberWithBool:
517       manager->GetFocus(NULL) == browserAccessibility_];
518   return ret;
521 - (id)header {
522   int headerElementId = -1;
523   if ([self internalRole] == ui::AX_ROLE_TABLE ||
524       [self internalRole] == ui::AX_ROLE_GRID) {
525     browserAccessibility_->GetIntAttribute(
526         ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
527   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
528     browserAccessibility_->GetIntAttribute(
529         ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
530   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
531     browserAccessibility_->GetIntAttribute(
532         ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
533   }
535   if (headerElementId > 0) {
536     BrowserAccessibility* headerObject =
537         browserAccessibility_->manager()->GetFromID(headerElementId);
538     if (headerObject)
539       return headerObject->ToBrowserAccessibilityCocoa();
540   }
541   return nil;
544 - (NSString*)help {
545   return NSStringForStringAttribute(
546       browserAccessibility_, ui::AX_ATTR_HELP);
549 - (NSNumber*)index {
550   if ([self internalRole] == ui::AX_ROLE_COLUMN) {
551     int columnIndex = browserAccessibility_->GetIntAttribute(
552           ui::AX_ATTR_TABLE_COLUMN_INDEX);
553     return [NSNumber numberWithInt:columnIndex];
554   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
555     int rowIndex = browserAccessibility_->GetIntAttribute(
556         ui::AX_ATTR_TABLE_ROW_INDEX);
557     return [NSNumber numberWithInt:rowIndex];
558   }
560   return nil;
563 // Returns whether or not this node should be ignored in the
564 // accessibility tree.
565 - (BOOL)isIgnored {
566   return [[self role] isEqualToString:NSAccessibilityUnknownRole];
569 - (NSString*)invalid {
570   base::string16 invalidUTF;
571   if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
572     return NULL;
573   NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
574   if ([invalid isEqualToString:@"false"] ||
575       [invalid isEqualToString:@""]) {
576     return @"false";
577   }
578   return invalid;
581 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
582                                    addTo:(NSMutableArray*)outArray {
583   const std::vector<int32>& attributeValues =
584       browserAccessibility_->GetIntListAttribute(attribute);
585   for (size_t i = 0; i < attributeValues.size(); ++i) {
586     BrowserAccessibility* element =
587         browserAccessibility_->manager()->GetFromID(attributeValues[i]);
588     if (element)
589       [outArray addObject:element->ToBrowserAccessibilityCocoa()];
590   }
593 - (NSArray*)linkedUIElements {
594   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
595   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
596   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
597   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
598   if ([ret count] == 0)
599     return nil;
600   return ret;
603 - (NSNumber*)loaded {
604   return [NSNumber numberWithBool:YES];
607 - (NSNumber*)loadingProgress {
608   float floatValue = browserAccessibility_->GetFloatAttribute(
609       ui::AX_ATTR_DOC_LOADING_PROGRESS);
610   return [NSNumber numberWithFloat:floatValue];
613 - (NSNumber*)maxValue {
614   float floatValue = browserAccessibility_->GetFloatAttribute(
615       ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
616   return [NSNumber numberWithFloat:floatValue];
619 - (NSNumber*)minValue {
620   float floatValue = browserAccessibility_->GetFloatAttribute(
621       ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
622   return [NSNumber numberWithFloat:floatValue];
625 - (NSString*)orientation {
626   // We present a spin button as a vertical slider, with a role description
627   // of "spin button".
628   if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
629     return NSAccessibilityVerticalOrientationValue;
631   if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
632     return NSAccessibilityVerticalOrientationValue;
633   else
634     return NSAccessibilityHorizontalOrientationValue;
637 - (NSNumber*)numberOfCharacters {
638   return [NSNumber numberWithInt:browserAccessibility_->value().length()];
641 // The origin of this accessibility object in the page's document.
642 // This is relative to webkit's top-left origin, not Cocoa's
643 // bottom-left origin.
644 - (NSPoint)origin {
645   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
646   return NSMakePoint(bounds.x(), bounds.y());
649 - (id)parent {
650   // A nil parent means we're the root.
651   if (browserAccessibility_->GetParent()) {
652     return NSAccessibilityUnignoredAncestor(
653         browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
654   } else {
655     // Hook back up to RenderWidgetHostViewCocoa.
656     BrowserAccessibilityManagerMac* manager =
657         static_cast<BrowserAccessibilityManagerMac*>(
658             browserAccessibility_->manager());
659     return manager->parent_view();
660   }
663 - (NSValue*)position {
664   NSPoint origin = [self origin];
665   NSSize size = [[self size] sizeValue];
666   NSPoint pointInScreen = [self pointInScreen:origin size:size];
667   return [NSValue valueWithPoint:pointInScreen];
670 - (NSNumber*)required {
671   return [NSNumber numberWithBool:
672       GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
675 // Returns an enum indicating the role from browserAccessibility_.
676 - (ui::AXRole)internalRole {
677   return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
680 - (content::BrowserAccessibilityDelegate*)delegate {
681   return browserAccessibility_->manager() ?
682       browserAccessibility_->manager()->delegate() :
683       nil;
686 - (NSPoint)pointInScreen:(NSPoint)origin
687                     size:(NSSize)size {
688   if (!browserAccessibility_)
689     return NSZeroPoint;
691   gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
692   gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
693   return NSMakePoint(point.x(), point.y());
696 // Returns a string indicating the NSAccessibility role of this object.
697 - (NSString*)role {
698   ui::AXRole role = [self internalRole];
699   if (role == ui::AX_ROLE_CANVAS &&
700       browserAccessibility_->GetBoolAttribute(
701           ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
702     return NSAccessibilityGroupRole;
703   }
704   if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
705     bool isAriaPressedDefined;
706     bool isMixed;
707     browserAccessibility_->GetAriaTristate("aria-pressed",
708                                            &isAriaPressedDefined,
709                                            &isMixed);
710     if (isAriaPressedDefined)
711       return NSAccessibilityCheckBoxRole;
712     else
713       return NSAccessibilityButtonRole;
714   }
715   return NativeRoleFromAXRole(role);
718 // Returns a string indicating the role description of this object.
719 - (NSString*)roleDescription {
720   NSString* role = [self role];
722   ContentClient* content_client = content::GetContentClient();
724   // The following descriptions are specific to webkit.
725   if ([role isEqualToString:@"AXWebArea"]) {
726     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
727         IDS_AX_ROLE_WEB_AREA));
728   }
730   if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
731     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
732         IDS_AX_ROLE_LINK));
733   }
735   if ([role isEqualToString:@"AXHeading"]) {
736     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
737         IDS_AX_ROLE_HEADING));
738   }
740   if ([role isEqualToString:NSAccessibilityGroupRole] ||
741       [role isEqualToString:NSAccessibilityRadioButtonRole]) {
742     std::string role;
743     if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
744       ui::AXRole internalRole = [self internalRole];
745       if ((internalRole != ui::AX_ROLE_GROUP &&
746            internalRole != ui::AX_ROLE_LIST_ITEM) ||
747           internalRole == ui::AX_ROLE_TAB) {
748         // TODO(dtseng): This is not localized; see crbug/84814.
749         return base::SysUTF8ToNSString(role);
750       }
751     }
752   }
754   switch([self internalRole]) {
755   case ui::AX_ROLE_FOOTER:
756     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
757         IDS_AX_ROLE_FOOTER));
758   case ui::AX_ROLE_SPIN_BUTTON:
759     // This control is similar to what VoiceOver calls a "stepper".
760     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
761         IDS_AX_ROLE_STEPPER));
762   case ui::AX_ROLE_TOGGLE_BUTTON:
763     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
764         IDS_AX_ROLE_TOGGLE_BUTTON));
765   default:
766     break;
767   }
769   return NSAccessibilityRoleDescription(role, nil);
772 - (NSArray*)rowHeaders {
773   if ([self internalRole] != ui::AX_ROLE_TABLE &&
774       [self internalRole] != ui::AX_ROLE_GRID) {
775     return nil;
776   }
778   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
779   const std::vector<int32>& uniqueCellIds =
780       browserAccessibility_->GetIntListAttribute(
781           ui::AX_ATTR_UNIQUE_CELL_IDS);
782   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
783     int id = uniqueCellIds[i];
784     BrowserAccessibility* cell =
785         browserAccessibility_->manager()->GetFromID(id);
786     if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
787       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
788   }
789   return ret;
792 - (NSValue*)rowIndexRange {
793   if ([self internalRole] != ui::AX_ROLE_CELL)
794     return nil;
796   int row = -1;
797   int rowspan = -1;
798   browserAccessibility_->GetIntAttribute(
799       ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
800   browserAccessibility_->GetIntAttribute(
801       ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
802   if (row >= 0 && rowspan >= 1)
803     return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
804   return nil;
807 - (NSArray*)rows {
808   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
810   if ([self internalRole] == ui::AX_ROLE_TABLE||
811       [self internalRole] == ui::AX_ROLE_GRID) {
812     for (BrowserAccessibilityCocoa* child in [self children]) {
813       if ([[child role] isEqualToString:NSAccessibilityRowRole])
814         [ret addObject:child];
815     }
816   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
817     const std::vector<int32>& indirectChildIds =
818         browserAccessibility_->GetIntListAttribute(
819             ui::AX_ATTR_INDIRECT_CHILD_IDS);
820     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
821       int id = indirectChildIds[i];
822       BrowserAccessibility* rowElement =
823           browserAccessibility_->manager()->GetFromID(id);
824       if (rowElement)
825         [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
826     }
827   }
829   return ret;
832 // Returns the size of this object.
833 - (NSValue*)size {
834   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
835   return  [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
838 // Returns a subrole based upon the role.
839 - (NSString*) subrole {
840   ui::AXRole browserAccessibilityRole = [self internalRole];
841   if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
842       GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
843     return @"AXSecureTextField";
844   }
846   NSString* htmlTag = NSStringForStringAttribute(
847       browserAccessibility_, ui::AX_ATTR_HTML_TAG);
849   if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
850     if ([htmlTag isEqualToString:@"ul"] ||
851         [htmlTag isEqualToString:@"ol"]) {
852       return @"AXContentList";
853     } else if ([htmlTag isEqualToString:@"dl"]) {
854       return @"AXDescriptionList";
855     }
856   }
858   return NativeSubroleFromAXRole(browserAccessibilityRole);
861 // Returns all tabs in this subtree.
862 - (NSArray*)tabs {
863   NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
865   if ([self internalRole] == ui::AX_ROLE_TAB)
866     [tabSubtree addObject:self];
868   for (uint i=0; i < [[self children] count]; ++i) {
869     NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
870     if ([tabChildren count] > 0)
871       [tabSubtree addObjectsFromArray:tabChildren];
872   }
874   return tabSubtree;
877 - (NSString*)title {
878   return NSStringForStringAttribute(
879       browserAccessibility_, ui::AX_ATTR_NAME);
882 - (id)titleUIElement {
883   int titleElementId;
884   if (browserAccessibility_->GetIntAttribute(
885           ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
886     BrowserAccessibility* titleElement =
887         browserAccessibility_->manager()->GetFromID(titleElementId);
888     if (titleElement)
889       return titleElement->ToBrowserAccessibilityCocoa();
890   }
891   std::vector<int32> labelledby_ids =
892       browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
893   if (labelledby_ids.size() == 1) {
894     BrowserAccessibility* titleElement =
895         browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
896     if (titleElement)
897       return titleElement->ToBrowserAccessibilityCocoa();
898   }
900   return nil;
903 - (NSURL*)url {
904   StringAttribute urlAttribute =
905       [[self role] isEqualToString:@"AXWebArea"] ?
906           ui::AX_ATTR_DOC_URL :
907           ui::AX_ATTR_URL;
909   std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
910   if (urlStr.empty())
911     return nil;
913   return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
916 - (id)value {
917   // WebCore uses an attachmentView to get the below behavior.
918   // We do not have any native views backing this object, so need
919   // to approximate Cocoa ax behavior best as we can.
920   NSString* role = [self role];
921   if ([role isEqualToString:@"AXHeading"]) {
922     int level = 0;
923     if (browserAccessibility_->GetIntAttribute(
924             ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
925       return [NSNumber numberWithInt:level];
926     }
927   } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
928     // AXValue does not make sense for pure buttons.
929     return @"";
930   } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
931     int value = 0;
932     bool isAriaPressedDefined;
933     bool isMixed;
934     value = browserAccessibility_->GetAriaTristate(
935         "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
937     if (isMixed)
938       value = 2;
940     return [NSNumber numberWithInt:value];
942   } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
943              [role isEqualToString:NSAccessibilityRadioButtonRole]) {
944     int value = 0;
945     value = GetState(
946         browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
947     value = GetState(
948         browserAccessibility_, ui::AX_STATE_SELECTED) ?
949             1 :
950             value;
952     if (browserAccessibility_->GetBoolAttribute(
953         ui::AX_ATTR_BUTTON_MIXED)) {
954       value = 2;
955     }
956     return [NSNumber numberWithInt:value];
957   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
958              [role isEqualToString:NSAccessibilitySliderRole] ||
959              [role isEqualToString:NSAccessibilityScrollBarRole]) {
960     float floatValue;
961     if (browserAccessibility_->GetFloatAttribute(
962             ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
963       return [NSNumber numberWithFloat:floatValue];
964     }
965   } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
966     int r = browserAccessibility_->GetIntAttribute(
967         ui::AX_ATTR_COLOR_VALUE_RED);
968     int g = browserAccessibility_->GetIntAttribute(
969         ui::AX_ATTR_COLOR_VALUE_GREEN);
970     int b = browserAccessibility_->GetIntAttribute(
971         ui::AX_ATTR_COLOR_VALUE_BLUE);
972     // This string matches the one returned by a native Mac color well.
973     return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
974                 r / 255., g / 255., b / 255.];
975   }
977   return NSStringForStringAttribute(
978       browserAccessibility_, ui::AX_ATTR_VALUE);
981 - (NSString*)valueDescription {
982   return NSStringForStringAttribute(
983       browserAccessibility_, ui::AX_ATTR_VALUE);
986 - (NSValue*)visibleCharacterRange {
987   return [NSValue valueWithRange:
988       NSMakeRange(0, browserAccessibility_->value().length())];
991 - (NSArray*)visibleCells {
992   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
993   const std::vector<int32>& uniqueCellIds =
994       browserAccessibility_->GetIntListAttribute(
995           ui::AX_ATTR_UNIQUE_CELL_IDS);
996   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
997     int id = uniqueCellIds[i];
998     BrowserAccessibility* cell =
999         browserAccessibility_->manager()->GetFromID(id);
1000     if (cell)
1001       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1002   }
1003   return ret;
1006 - (NSArray*)visibleColumns {
1007   return [self columns];
1010 - (NSArray*)visibleRows {
1011   return [self rows];
1014 - (NSNumber*)visited {
1015   return [NSNumber numberWithBool:
1016       GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1019 - (id)window {
1020   if (!browserAccessibility_)
1021     return nil;
1023   BrowserAccessibilityManagerMac* manager =
1024       static_cast<BrowserAccessibilityManagerMac*>(
1025           browserAccessibility_->manager());
1026   return [manager->parent_view() window];
1029 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1030   return [attributeToMethodNameMap objectForKey:attribute];
1033 // Returns the accessibility value for the given attribute.  If the value isn't
1034 // supported this will return nil.
1035 - (id)accessibilityAttributeValue:(NSString*)attribute {
1036   if (!browserAccessibility_)
1037     return nil;
1039   SEL selector =
1040       NSSelectorFromString([self methodNameForAttribute:attribute]);
1041   if (selector)
1042     return [self performSelector:selector];
1044   // TODO(dtseng): refactor remaining attributes.
1045   int selStart, selEnd;
1046   if (browserAccessibility_->GetIntAttribute(
1047           ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1048       browserAccessibility_->
1049           GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1050     if (selStart > selEnd)
1051       std::swap(selStart, selEnd);
1052     int selLength = selEnd - selStart;
1053     if ([attribute isEqualToString:
1054         NSAccessibilityInsertionPointLineNumberAttribute]) {
1055       const std::vector<int32>& line_breaks =
1056           browserAccessibility_->GetIntListAttribute(
1057               ui::AX_ATTR_LINE_BREAKS);
1058       for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1059         if (line_breaks[i] > selStart)
1060           return [NSNumber numberWithInt:i];
1061       }
1062       return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1063     }
1064     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1065       std::string value = browserAccessibility_->GetStringAttribute(
1066           ui::AX_ATTR_VALUE);
1067       return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1068     }
1069     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1070       return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1071     }
1072   }
1073   return nil;
1076 // Returns the accessibility value for the given attribute and parameter. If the
1077 // value isn't supported this will return nil.
1078 - (id)accessibilityAttributeValue:(NSString*)attribute
1079                      forParameter:(id)parameter {
1080   if (!browserAccessibility_)
1081     return nil;
1083   const std::vector<int32>& line_breaks =
1084       browserAccessibility_->GetIntListAttribute(
1085           ui::AX_ATTR_LINE_BREAKS);
1086   int len = static_cast<int>(browserAccessibility_->value().size());
1088   if ([attribute isEqualToString:
1089       NSAccessibilityStringForRangeParameterizedAttribute]) {
1090     NSRange range = [(NSValue*)parameter rangeValue];
1091     std::string value = browserAccessibility_->GetStringAttribute(
1092         ui::AX_ATTR_VALUE);
1093     return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1094   }
1096   if ([attribute isEqualToString:
1097       NSAccessibilityLineForIndexParameterizedAttribute]) {
1098     int index = [(NSNumber*)parameter intValue];
1099     for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1100       if (line_breaks[i] > index)
1101         return [NSNumber numberWithInt:i];
1102     }
1103     return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1104   }
1106   if ([attribute isEqualToString:
1107       NSAccessibilityRangeForLineParameterizedAttribute]) {
1108     int line_index = [(NSNumber*)parameter intValue];
1109     int line_count = static_cast<int>(line_breaks.size()) + 1;
1110     if (line_index < 0 || line_index >= line_count)
1111       return nil;
1112     int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1113     int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1114     return [NSValue valueWithRange:
1115         NSMakeRange(start, end - start)];
1116   }
1118   if ([attribute isEqualToString:
1119       NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1120     if ([self internalRole] != ui::AX_ROLE_TABLE &&
1121         [self internalRole] != ui::AX_ROLE_GRID) {
1122       return nil;
1123     }
1124     if (![parameter isKindOfClass:[NSArray self]])
1125       return nil;
1126     NSArray* array = parameter;
1127     int column = [[array objectAtIndex:0] intValue];
1128     int row = [[array objectAtIndex:1] intValue];
1129     int num_columns = browserAccessibility_->GetIntAttribute(
1130         ui::AX_ATTR_TABLE_COLUMN_COUNT);
1131     int num_rows = browserAccessibility_->GetIntAttribute(
1132         ui::AX_ATTR_TABLE_ROW_COUNT);
1133     if (column < 0 || column >= num_columns ||
1134         row < 0 || row >= num_rows) {
1135       return nil;
1136     }
1137     for (size_t i = 0;
1138          i < browserAccessibility_->PlatformChildCount();
1139          ++i) {
1140       BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1141       if (child->GetRole() != ui::AX_ROLE_ROW)
1142         continue;
1143       int rowIndex;
1144       if (!child->GetIntAttribute(
1145               ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1146         continue;
1147       }
1148       if (rowIndex < row)
1149         continue;
1150       if (rowIndex > row)
1151         break;
1152       for (size_t j = 0;
1153            j < child->PlatformChildCount();
1154            ++j) {
1155         BrowserAccessibility* cell = child->PlatformGetChild(j);
1156         if (cell->GetRole() != ui::AX_ROLE_CELL)
1157           continue;
1158         int colIndex;
1159         if (!cell->GetIntAttribute(
1160                 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1161                 &colIndex)) {
1162           continue;
1163         }
1164         if (colIndex == column)
1165           return cell->ToBrowserAccessibilityCocoa();
1166         if (colIndex > column)
1167           break;
1168       }
1169     }
1170     return nil;
1171   }
1173   if ([attribute isEqualToString:
1174       NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1175     if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1176       return nil;
1177     NSRange range = [(NSValue*)parameter rangeValue];
1178     gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1179         range.location, range.length);
1180     NSPoint origin = NSMakePoint(rect.x(), rect.y());
1181     NSSize size = NSMakeSize(rect.width(), rect.height());
1182     NSPoint pointInScreen = [self pointInScreen:origin size:size];
1183     NSRect nsrect = NSMakeRect(
1184         pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1185     return [NSValue valueWithRect:nsrect];
1186   }
1188   // TODO(dtseng): support the following attributes.
1189   if ([attribute isEqualTo:
1190           NSAccessibilityRangeForPositionParameterizedAttribute] ||
1191       [attribute isEqualTo:
1192           NSAccessibilityRangeForIndexParameterizedAttribute] ||
1193       [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1194       [attribute isEqualTo:
1195           NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1196     return nil;
1197   }
1198   return nil;
1201 // Returns an array of parameterized attributes names that this object will
1202 // respond to.
1203 - (NSArray*)accessibilityParameterizedAttributeNames {
1204   if (!browserAccessibility_)
1205     return nil;
1207   if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1208       [[self role] isEqualToString:NSAccessibilityGridRole]) {
1209     return [NSArray arrayWithObjects:
1210         NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1211         nil];
1212   }
1213   if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1214       [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1215     return [NSArray arrayWithObjects:
1216         NSAccessibilityLineForIndexParameterizedAttribute,
1217         NSAccessibilityRangeForLineParameterizedAttribute,
1218         NSAccessibilityStringForRangeParameterizedAttribute,
1219         NSAccessibilityRangeForPositionParameterizedAttribute,
1220         NSAccessibilityRangeForIndexParameterizedAttribute,
1221         NSAccessibilityBoundsForRangeParameterizedAttribute,
1222         NSAccessibilityRTFForRangeParameterizedAttribute,
1223         NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1224         NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1225         nil];
1226   }
1227   if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1228     return [NSArray arrayWithObjects:
1229         NSAccessibilityBoundsForRangeParameterizedAttribute,
1230         nil];
1231   }
1232   return nil;
1235 // Returns an array of action names that this object will respond to.
1236 - (NSArray*)accessibilityActionNames {
1237   if (!browserAccessibility_)
1238     return nil;
1240   NSMutableArray* ret =
1241       [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1242   NSString* role = [self role];
1243   // TODO(dtseng): this should only get set when there's a default action.
1244   if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1245       ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1246       ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1247     [ret addObject:NSAccessibilityPressAction];
1248   }
1250   return ret;
1253 // Returns a sub-array of values for the given attribute value, starting at
1254 // index, with up to maxCount items.  If the given index is out of bounds,
1255 // or there are no values for the given attribute, it will return nil.
1256 // This method is used for querying subsets of values, without having to
1257 // return a large set of data, such as elements with a large number of
1258 // children.
1259 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1260                                         index:(NSUInteger)index
1261                                      maxCount:(NSUInteger)maxCount {
1262   if (!browserAccessibility_)
1263     return nil;
1265   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1266   if (!fullArray)
1267     return nil;
1268   NSUInteger arrayCount = [fullArray count];
1269   if (index >= arrayCount)
1270     return nil;
1271   NSRange subRange;
1272   if ((index + maxCount) > arrayCount) {
1273     subRange = NSMakeRange(index, arrayCount - index);
1274   } else {
1275     subRange = NSMakeRange(index, maxCount);
1276   }
1277   return [fullArray subarrayWithRange:subRange];
1280 // Returns the count of the specified accessibility array attribute.
1281 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1282   if (!browserAccessibility_)
1283     return nil;
1285   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1286   return [fullArray count];
1289 // Returns the list of accessibility attributes that this object supports.
1290 - (NSArray*)accessibilityAttributeNames {
1291   if (!browserAccessibility_)
1292     return nil;
1294   // General attributes.
1295   NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1296       NSAccessibilityChildrenAttribute,
1297       NSAccessibilityDescriptionAttribute,
1298       NSAccessibilityEnabledAttribute,
1299       NSAccessibilityFocusedAttribute,
1300       NSAccessibilityHelpAttribute,
1301       NSAccessibilityLinkedUIElementsAttribute,
1302       NSAccessibilityParentAttribute,
1303       NSAccessibilityPositionAttribute,
1304       NSAccessibilityRoleAttribute,
1305       NSAccessibilityRoleDescriptionAttribute,
1306       NSAccessibilitySizeAttribute,
1307       NSAccessibilitySubroleAttribute,
1308       NSAccessibilityTitleAttribute,
1309       NSAccessibilityTopLevelUIElementAttribute,
1310       NSAccessibilityValueAttribute,
1311       NSAccessibilityWindowAttribute,
1312       @"AXAccessKey",
1313       @"AXInvalid",
1314       @"AXRequired",
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         nil]];
1345   } else if ([role isEqualToString:@"AXWebArea"]) {
1346     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1347         @"AXLoaded",
1348         @"AXLoadingProgress",
1349         nil]];
1350   } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1351              [role isEqualToString:NSAccessibilityTextAreaRole]) {
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:NSAccessibilityScrollBarRole]) {
1364     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1365         NSAccessibilityMaxValueAttribute,
1366         NSAccessibilityMinValueAttribute,
1367         NSAccessibilityOrientationAttribute,
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   }
1398   // Add the url attribute only if it has a valid url.
1399   if ([self url] != nil) {
1400     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1401         NSAccessibilityURLAttribute,
1402         nil]];
1403   }
1405   // Live regions.
1406   if (browserAccessibility_->HasStringAttribute(
1407           ui::AX_ATTR_LIVE_STATUS)) {
1408     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1409         @"AXARIALive",
1410         @"AXARIARelevant",
1411         nil]];
1412   }
1413   if (browserAccessibility_->HasStringAttribute(
1414           ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1415     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1416         @"AXARIAAtomic",
1417         @"AXARIABusy",
1418         nil]];
1419   }
1421   // Title UI Element.
1422   if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1423       (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1424        browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1425                             .size() == 1)) {
1426     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1427          NSAccessibilityTitleUIElementAttribute,
1428          nil]];
1429   }
1430   // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1431   // for elements which are referred to by labelledby or are labels
1433   return ret;
1436 // Returns the index of the child in this objects array of children.
1437 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1438   if (!browserAccessibility_)
1439     return nil;
1441   NSUInteger index = 0;
1442   for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1443     if ([child isEqual:childToCheck])
1444       return index;
1445     ++index;
1446   }
1447   return NSNotFound;
1450 // Returns whether or not the specified attribute can be set by the
1451 // accessibility API via |accessibilitySetValue:forAttribute:|.
1452 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1453   if (!browserAccessibility_)
1454     return nil;
1456   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1457     return GetState(browserAccessibility_,
1458         ui::AX_STATE_FOCUSABLE);
1459   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1460     return browserAccessibility_->GetBoolAttribute(
1461         ui::AX_ATTR_CAN_SET_VALUE);
1462   }
1463   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1464       ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1465        [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1466     return YES;
1468   return NO;
1471 // Returns whether or not this object should be ignored in the accessibilty
1472 // tree.
1473 - (BOOL)accessibilityIsIgnored {
1474   if (!browserAccessibility_)
1475     return true;
1477   return [self isIgnored];
1480 // Performs the given accessibilty action on the webkit accessibility object
1481 // that backs this object.
1482 - (void)accessibilityPerformAction:(NSString*)action {
1483   if (!browserAccessibility_)
1484     return;
1486   // TODO(dmazzoni): Support more actions.
1487   if ([action isEqualToString:NSAccessibilityPressAction]) {
1488     [self delegate]->AccessibilityDoDefaultAction(
1489         browserAccessibility_->GetId());
1490   } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1491     [self delegate]->AccessibilityShowMenu(browserAccessibility_->GetId());
1492   }
1495 // Returns the description of the given action.
1496 - (NSString*)accessibilityActionDescription:(NSString*)action {
1497   if (!browserAccessibility_)
1498     return nil;
1500   return NSAccessibilityActionDescription(action);
1503 // Sets an override value for a specific accessibility attribute.
1504 // This class does not support this.
1505 - (BOOL)accessibilitySetOverrideValue:(id)value
1506                          forAttribute:(NSString*)attribute {
1507   return NO;
1510 // Sets the value for an accessibility attribute via the accessibility API.
1511 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1512   if (!browserAccessibility_)
1513     return;
1515   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1516     NSNumber* focusedNumber = value;
1517     BOOL focused = [focusedNumber intValue];
1518     if (focused)
1519       [self delegate]->AccessibilitySetFocus(browserAccessibility_->GetId());
1520   }
1521   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1522     NSRange range = [(NSValue*)value rangeValue];
1523     [self delegate]->AccessibilitySetTextSelection(
1524         browserAccessibility_->GetId(),
1525         range.location, range.location + range.length);
1526   }
1529 // Returns the deepest accessibility child that should not be ignored.
1530 // It is assumed that the hit test has been narrowed down to this object
1531 // or one of its children, so this will never return nil unless this
1532 // object is invalid.
1533 - (id)accessibilityHitTest:(NSPoint)point {
1534   if (!browserAccessibility_)
1535     return nil;
1537   BrowserAccessibilityCocoa* hit = self;
1538   for (BrowserAccessibilityCocoa* child in [self children]) {
1539     if (!child->browserAccessibility_)
1540       continue;
1541     NSPoint origin = [child origin];
1542     NSSize size = [[child size] sizeValue];
1543     NSRect rect;
1544     rect.origin = origin;
1545     rect.size = size;
1546     if (NSPointInRect(point, rect)) {
1547       hit = child;
1548       id childResult = [child accessibilityHitTest:point];
1549       if (![childResult accessibilityIsIgnored]) {
1550         hit = childResult;
1551         break;
1552       }
1553     }
1554   }
1555   return NSAccessibilityUnignoredAncestor(hit);
1558 - (BOOL)isEqual:(id)object {
1559   if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1560     return NO;
1561   return ([self hash] == [object hash]);
1564 - (NSUInteger)hash {
1565   // Potentially called during dealloc.
1566   if (!browserAccessibility_)
1567     return [super hash];
1568   return browserAccessibility_->GetId();
1571 - (BOOL)accessibilityShouldUseUniqueId {
1572   return YES;
1575 @end