Remove PlatformFile from profile_browsertest
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_cocoa.mm
blobf1565eba01336ab3769b34b25adb68ab9650aab7
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_TEXT, 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, NSAccessibilityCheckBoxRole },
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_TOGGLE_BUTTON, @"AXToggleButton" },
210     { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
211     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
212   };
214   RoleMap subrole_map;
215   for (size_t i = 0; i < arraysize(subroles); ++i)
216     subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
217   return subrole_map;
220 // A mapping of webkit roles to native subroles.
221 NSString* NativeSubroleFromAXRole(
222     const ui::AXRole& role) {
223   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
224                          (BuildSubroleMap()));
225   RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
226   if (it != web_accessibility_to_native_subrole.end())
227     return it->second;
228   else
229     return nil;
232 // A mapping from an accessibility attribute to its method name.
233 NSDictionary* attributeToMethodNameMap = nil;
235 } // namespace
237 @implementation BrowserAccessibilityCocoa
239 + (void)initialize {
240   const struct {
241     NSString* attribute;
242     NSString* methodName;
243   } attributeToMethodNameContainer[] = {
244     { NSAccessibilityChildrenAttribute, @"children" },
245     { NSAccessibilityColumnsAttribute, @"columns" },
246     { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
247     { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
248     { NSAccessibilityContentsAttribute, @"contents" },
249     { NSAccessibilityDescriptionAttribute, @"description" },
250     { NSAccessibilityDisclosingAttribute, @"disclosing" },
251     { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
252     { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
253     { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
254     { NSAccessibilityEnabledAttribute, @"enabled" },
255     { NSAccessibilityFocusedAttribute, @"focused" },
256     { NSAccessibilityHeaderAttribute, @"header" },
257     { NSAccessibilityHelpAttribute, @"help" },
258     { NSAccessibilityIndexAttribute, @"index" },
259     { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
260     { NSAccessibilityMaxValueAttribute, @"maxValue" },
261     { NSAccessibilityMinValueAttribute, @"minValue" },
262     { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
263     { NSAccessibilityOrientationAttribute, @"orientation" },
264     { NSAccessibilityParentAttribute, @"parent" },
265     { NSAccessibilityPositionAttribute, @"position" },
266     { NSAccessibilityRoleAttribute, @"role" },
267     { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
268     { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
269     { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
270     { NSAccessibilityRowsAttribute, @"rows" },
271     // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
272     { NSAccessibilitySizeAttribute, @"size" },
273     { NSAccessibilitySubroleAttribute, @"subrole" },
274     { NSAccessibilityTabsAttribute, @"tabs" },
275     { NSAccessibilityTitleAttribute, @"title" },
276     { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
277     { NSAccessibilityTopLevelUIElementAttribute, @"window" },
278     { NSAccessibilityURLAttribute, @"url" },
279     { NSAccessibilityValueAttribute, @"value" },
280     { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
281     { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
282     { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
283     { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
284     { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
285     { NSAccessibilityWindowAttribute, @"window" },
286     { @"AXAccessKey", @"accessKey" },
287     { @"AXARIAAtomic", @"ariaAtomic" },
288     { @"AXARIABusy", @"ariaBusy" },
289     { @"AXARIALive", @"ariaLive" },
290     { @"AXARIARelevant", @"ariaRelevant" },
291     { @"AXInvalid", @"invalid" },
292     { @"AXLoaded", @"loaded" },
293     { @"AXLoadingProgress", @"loadingProgress" },
294     { @"AXRequired", @"required" },
295     { @"AXVisited", @"visited" },
296   };
298   NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
299   const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
300                                sizeof(attributeToMethodNameContainer[0]);
301   for (size_t i = 0; i < numAttributes; ++i) {
302     [dict setObject:attributeToMethodNameContainer[i].methodName
303              forKey:attributeToMethodNameContainer[i].attribute];
304   }
305   attributeToMethodNameMap = dict;
306   dict = nil;
309 - (id)initWithObject:(BrowserAccessibility*)accessibility
310             delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate {
311   if ((self = [super init])) {
312     browserAccessibility_ = accessibility;
313     delegate_ = delegate;
314   }
315   return self;
318 - (void)detach {
319   if (browserAccessibility_) {
320     NSAccessibilityUnregisterUniqueIdForUIElement(self);
321     browserAccessibility_ = NULL;
322   }
325 - (NSString*)accessKey {
326   return NSStringForStringAttribute(
327       browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
330 - (NSNumber*)ariaAtomic {
331   bool boolValue = browserAccessibility_->GetBoolAttribute(
332       ui::AX_ATTR_LIVE_ATOMIC);
333   return [NSNumber numberWithBool:boolValue];
336 - (NSNumber*)ariaBusy {
337   bool boolValue = browserAccessibility_->GetBoolAttribute(
338       ui::AX_ATTR_LIVE_BUSY);
339   return [NSNumber numberWithBool:boolValue];
342 - (NSString*)ariaLive {
343   return NSStringForStringAttribute(
344       browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
347 - (NSString*)ariaRelevant {
348   return NSStringForStringAttribute(
349       browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
352 // Returns an array of BrowserAccessibilityCocoa objects, representing the
353 // accessibility children of this object.
354 - (NSArray*)children {
355   if (!children_) {
356     uint32 childCount = browserAccessibility_->PlatformChildCount();
357     children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
358     for (uint32 index = 0; index < childCount; ++index) {
359       BrowserAccessibilityCocoa* child =
360           browserAccessibility_->PlatformGetChild(index)->
361               ToBrowserAccessibilityCocoa();
362       if ([child isIgnored])
363         [children_ addObjectsFromArray:[child children]];
364       else
365         [children_ addObject:child];
366     }
368     // Also, add indirect children (if any).
369     const std::vector<int32>& indirectChildIds =
370         browserAccessibility_->GetIntListAttribute(
371             ui::AX_ATTR_INDIRECT_CHILD_IDS);
372     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
373       int32 child_id = indirectChildIds[i];
374       BrowserAccessibility* child =
375           browserAccessibility_->manager()->GetFromRendererID(child_id);
377       // This only became necessary as a result of crbug.com/93095. It should be
378       // a DCHECK in the future.
379       if (child) {
380         BrowserAccessibilityCocoa* child_cocoa =
381             child->ToBrowserAccessibilityCocoa();
382         [children_ addObject:child_cocoa];
383       }
384     }
385   }
386   return children_;
389 - (void)childrenChanged {
390   if (![self isIgnored]) {
391     children_.reset();
392   } else {
393     [browserAccessibility_->parent()->ToBrowserAccessibilityCocoa()
394        childrenChanged];
395   }
398 - (NSArray*)columnHeaders {
399   if ([self internalRole] != ui::AX_ROLE_TABLE &&
400       [self internalRole] != ui::AX_ROLE_GRID) {
401     return nil;
402   }
404   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
405   const std::vector<int32>& uniqueCellIds =
406       browserAccessibility_->GetIntListAttribute(
407           ui::AX_ATTR_UNIQUE_CELL_IDS);
408   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
409     int id = uniqueCellIds[i];
410     BrowserAccessibility* cell =
411         browserAccessibility_->manager()->GetFromRendererID(id);
412     if (cell && cell->role() == ui::AX_ROLE_COLUMN_HEADER)
413       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
414   }
415   return ret;
418 - (NSValue*)columnIndexRange {
419   if ([self internalRole] != ui::AX_ROLE_CELL)
420     return nil;
422   int column = -1;
423   int colspan = -1;
424   browserAccessibility_->GetIntAttribute(
425       ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
426   browserAccessibility_->GetIntAttribute(
427       ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
428   if (column >= 0 && colspan >= 1)
429     return [NSValue valueWithRange:NSMakeRange(column, colspan)];
430   return nil;
433 - (NSArray*)columns {
434   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
435   for (BrowserAccessibilityCocoa* child in [self children]) {
436     if ([[child role] isEqualToString:NSAccessibilityColumnRole])
437       [ret addObject:child];
438   }
439   return ret;
442 - (NSString*)description {
443   std::string description;
444   if (browserAccessibility_->GetStringAttribute(
445           ui::AX_ATTR_DESCRIPTION, &description)) {
446     return base::SysUTF8ToNSString(description);
447   }
449   // If the role is anything other than an image, or if there's
450   // a title or title UI element, just return an empty string.
451   if (![[self role] isEqualToString:NSAccessibilityImageRole])
452     return @"";
453   if (browserAccessibility_->HasStringAttribute(
454           ui::AX_ATTR_NAME)) {
455     return @"";
456   }
457   if ([self titleUIElement])
458     return @"";
460   // The remaining case is an image where there's no other title.
461   // Return the base part of the filename as the description.
462   std::string url;
463   if (browserAccessibility_->GetStringAttribute(
464           ui::AX_ATTR_URL, &url)) {
465     // Given a url like http://foo.com/bar/baz.png, just return the
466     // base name, e.g., "baz.png".
467     size_t leftIndex = url.rfind('/');
468     std::string basename =
469         leftIndex != std::string::npos ? url.substr(leftIndex) : url;
470     return base::SysUTF8ToNSString(basename);
471   }
473   return @"";
476 - (NSNumber*)disclosing {
477   if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
478     return [NSNumber numberWithBool:
479         GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
480   } else {
481     return nil;
482   }
485 - (id)disclosedByRow {
486   // The row that contains this row.
487   // It should be the same as the first parent that is a treeitem.
488   return nil;
491 - (NSNumber*)disclosureLevel {
492   ui::AXRole role = [self internalRole];
493   if (role == ui::AX_ROLE_ROW ||
494       role == ui::AX_ROLE_TREE_ITEM) {
495     int level = browserAccessibility_->GetIntAttribute(
496         ui::AX_ATTR_HIERARCHICAL_LEVEL);
497     // Mac disclosureLevel is 0-based, but web levels are 1-based.
498     if (level > 0)
499       level--;
500     return [NSNumber numberWithInt:level];
501   } else {
502     return nil;
503   }
506 - (id)disclosedRows {
507   // The rows that are considered inside this row.
508   return nil;
511 - (NSNumber*)enabled {
512   return [NSNumber numberWithBool:
513       GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
516 - (NSNumber*)focused {
517   BrowserAccessibilityManager* manager = browserAccessibility_->manager();
518   NSNumber* ret = [NSNumber numberWithBool:
519       manager->GetFocus(NULL) == browserAccessibility_];
520   return ret;
523 - (id)header {
524   int headerElementId = -1;
525   if ([self internalRole] == ui::AX_ROLE_TABLE ||
526       [self internalRole] == ui::AX_ROLE_GRID) {
527     browserAccessibility_->GetIntAttribute(
528         ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
529   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
530     browserAccessibility_->GetIntAttribute(
531         ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
532   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
533     browserAccessibility_->GetIntAttribute(
534         ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
535   }
537   if (headerElementId > 0) {
538     BrowserAccessibility* headerObject =
539         browserAccessibility_->manager()->GetFromRendererID(headerElementId);
540     if (headerObject)
541       return headerObject->ToBrowserAccessibilityCocoa();
542   }
543   return nil;
546 - (NSString*)help {
547   return NSStringForStringAttribute(
548       browserAccessibility_, ui::AX_ATTR_HELP);
551 - (NSNumber*)index {
552   if ([self internalRole] == ui::AX_ROLE_COLUMN) {
553     int columnIndex = browserAccessibility_->GetIntAttribute(
554           ui::AX_ATTR_TABLE_COLUMN_INDEX);
555     return [NSNumber numberWithInt:columnIndex];
556   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
557     int rowIndex = browserAccessibility_->GetIntAttribute(
558         ui::AX_ATTR_TABLE_ROW_INDEX);
559     return [NSNumber numberWithInt:rowIndex];
560   }
562   return nil;
565 // Returns whether or not this node should be ignored in the
566 // accessibility tree.
567 - (BOOL)isIgnored {
568   return [[self role] isEqualToString:NSAccessibilityUnknownRole];
571 - (NSString*)invalid {
572   base::string16 invalidUTF;
573   if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
574     return NULL;
575   NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
576   if ([invalid isEqualToString:@"false"] ||
577       [invalid isEqualToString:@""]) {
578     return @"false";
579   }
580   return invalid;
583 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
584                                    addTo:(NSMutableArray*)outArray {
585   const std::vector<int32>& attributeValues =
586       browserAccessibility_->GetIntListAttribute(attribute);
587   for (size_t i = 0; i < attributeValues.size(); ++i) {
588     BrowserAccessibility* element =
589         browserAccessibility_->manager()->GetFromRendererID(attributeValues[i]);
590     if (element)
591       [outArray addObject:element->ToBrowserAccessibilityCocoa()];
592   }
595 - (NSArray*)linkedUIElements {
596   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
597   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
598   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
599   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
600   if ([ret count] == 0)
601     return nil;
602   return ret;
605 - (NSNumber*)loaded {
606   return [NSNumber numberWithBool:YES];
609 - (NSNumber*)loadingProgress {
610   float floatValue = browserAccessibility_->GetFloatAttribute(
611       ui::AX_ATTR_DOC_LOADING_PROGRESS);
612   return [NSNumber numberWithFloat:floatValue];
615 - (NSNumber*)maxValue {
616   float floatValue = browserAccessibility_->GetFloatAttribute(
617       ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
618   return [NSNumber numberWithFloat:floatValue];
621 - (NSNumber*)minValue {
622   float floatValue = browserAccessibility_->GetFloatAttribute(
623       ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
624   return [NSNumber numberWithFloat:floatValue];
627 - (NSString*)orientation {
628   // We present a spin button as a vertical slider, with a role description
629   // of "spin button".
630   if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
631     return NSAccessibilityVerticalOrientationValue;
633   if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
634     return NSAccessibilityVerticalOrientationValue;
635   else
636     return NSAccessibilityHorizontalOrientationValue;
639 - (NSNumber*)numberOfCharacters {
640   return [NSNumber numberWithInt:browserAccessibility_->value().length()];
643 // The origin of this accessibility object in the page's document.
644 // This is relative to webkit's top-left origin, not Cocoa's
645 // bottom-left origin.
646 - (NSPoint)origin {
647   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
648   return NSMakePoint(bounds.x(), bounds.y());
651 - (id)parent {
652   // A nil parent means we're the root.
653   if (browserAccessibility_->parent()) {
654     return NSAccessibilityUnignoredAncestor(
655         browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
656   } else {
657     // Hook back up to RenderWidgetHostViewCocoa.
658     BrowserAccessibilityManagerMac* manager =
659         static_cast<BrowserAccessibilityManagerMac*>(
660             browserAccessibility_->manager());
661     return manager->parent_view();
662   }
665 - (NSValue*)position {
666   NSPoint origin = [self origin];
667   NSSize size = [[self size] sizeValue];
668   NSPoint pointInScreen =
669       [delegate_ accessibilityPointInScreen:origin size:size];
670   return [NSValue valueWithPoint:pointInScreen];
673 - (NSNumber*)required {
674   return [NSNumber numberWithBool:
675       GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
678 // Returns an enum indicating the role from browserAccessibility_.
679 - (ui::AXRole)internalRole {
680   return static_cast<ui::AXRole>(browserAccessibility_->role());
683 // Returns a string indicating the NSAccessibility role of this object.
684 - (NSString*)role {
685   ui::AXRole role = [self internalRole];
686   if (role == ui::AX_ROLE_CANVAS &&
687       browserAccessibility_->GetBoolAttribute(
688           ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
689     return NSAccessibilityGroupRole;
690   }
691   if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
692     bool isAriaPressedDefined;
693     bool isMixed;
694     browserAccessibility_->GetAriaTristate("aria-pressed",
695                                            &isAriaPressedDefined,
696                                            &isMixed);
697     if (isAriaPressedDefined)
698       return NSAccessibilityCheckBoxRole;
699     else
700       return NSAccessibilityButtonRole;
701   }
702   return NativeRoleFromAXRole(role);
705 // Returns a string indicating the role description of this object.
706 - (NSString*)roleDescription {
707   NSString* role = [self role];
709   ContentClient* content_client = content::GetContentClient();
711   // The following descriptions are specific to webkit.
712   if ([role isEqualToString:@"AXWebArea"]) {
713     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
714         IDS_AX_ROLE_WEB_AREA));
715   }
717   if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
718     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
719         IDS_AX_ROLE_LINK));
720   }
722   if ([role isEqualToString:@"AXHeading"]) {
723     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
724         IDS_AX_ROLE_HEADING));
725   }
727   if ([role isEqualToString:NSAccessibilityGroupRole] ||
728       [role isEqualToString:NSAccessibilityRadioButtonRole]) {
729     std::string role;
730     if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
731       ui::AXRole internalRole = [self internalRole];
732       if ((internalRole != ui::AX_ROLE_GROUP &&
733            internalRole != ui::AX_ROLE_LIST_ITEM) ||
734           internalRole == ui::AX_ROLE_TAB) {
735         // TODO(dtseng): This is not localized; see crbug/84814.
736         return base::SysUTF8ToNSString(role);
737       }
738     }
739   }
741   switch([self internalRole]) {
742   case ui::AX_ROLE_FOOTER:
743     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
744         IDS_AX_ROLE_FOOTER));
745   case ui::AX_ROLE_SPIN_BUTTON:
746     // This control is similar to what VoiceOver calls a "stepper".
747     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
748         IDS_AX_ROLE_STEPPER));
749   case ui::AX_ROLE_TOGGLE_BUTTON:
750     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
751         IDS_AX_ROLE_TOGGLE_BUTTON));
752   default:
753     break;
754   }
756   return NSAccessibilityRoleDescription(role, nil);
759 - (NSArray*)rowHeaders {
760   if ([self internalRole] != ui::AX_ROLE_TABLE &&
761       [self internalRole] != ui::AX_ROLE_GRID) {
762     return nil;
763   }
765   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
766   const std::vector<int32>& uniqueCellIds =
767       browserAccessibility_->GetIntListAttribute(
768           ui::AX_ATTR_UNIQUE_CELL_IDS);
769   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
770     int id = uniqueCellIds[i];
771     BrowserAccessibility* cell =
772         browserAccessibility_->manager()->GetFromRendererID(id);
773     if (cell && cell->role() == ui::AX_ROLE_ROW_HEADER)
774       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
775   }
776   return ret;
779 - (NSValue*)rowIndexRange {
780   if ([self internalRole] != ui::AX_ROLE_CELL)
781     return nil;
783   int row = -1;
784   int rowspan = -1;
785   browserAccessibility_->GetIntAttribute(
786       ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
787   browserAccessibility_->GetIntAttribute(
788       ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
789   if (row >= 0 && rowspan >= 1)
790     return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
791   return nil;
794 - (NSArray*)rows {
795   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
797   if ([self internalRole] == ui::AX_ROLE_TABLE||
798       [self internalRole] == ui::AX_ROLE_GRID) {
799     for (BrowserAccessibilityCocoa* child in [self children]) {
800       if ([[child role] isEqualToString:NSAccessibilityRowRole])
801         [ret addObject:child];
802     }
803   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
804     const std::vector<int32>& indirectChildIds =
805         browserAccessibility_->GetIntListAttribute(
806             ui::AX_ATTR_INDIRECT_CHILD_IDS);
807     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
808       int id = indirectChildIds[i];
809       BrowserAccessibility* rowElement =
810           browserAccessibility_->manager()->GetFromRendererID(id);
811       if (rowElement)
812         [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
813     }
814   }
816   return ret;
819 // Returns the size of this object.
820 - (NSValue*)size {
821   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
822   return  [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
825 // Returns a subrole based upon the role.
826 - (NSString*) subrole {
827   ui::AXRole browserAccessibilityRole = [self internalRole];
828   if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
829       GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
830     return @"AXSecureTextField";
831   }
833   NSString* htmlTag = NSStringForStringAttribute(
834       browserAccessibility_, ui::AX_ATTR_HTML_TAG);
836   if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
837     if ([htmlTag isEqualToString:@"ul"] ||
838         [htmlTag isEqualToString:@"ol"]) {
839       return @"AXContentList";
840     } else if ([htmlTag isEqualToString:@"dl"]) {
841       return @"AXDescriptionList";
842     }
843   }
845   return NativeSubroleFromAXRole(browserAccessibilityRole);
848 // Returns all tabs in this subtree.
849 - (NSArray*)tabs {
850   NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
852   if ([self internalRole] == ui::AX_ROLE_TAB)
853     [tabSubtree addObject:self];
855   for (uint i=0; i < [[self children] count]; ++i) {
856     NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
857     if ([tabChildren count] > 0)
858       [tabSubtree addObjectsFromArray:tabChildren];
859   }
861   return tabSubtree;
864 - (NSString*)title {
865   return NSStringForStringAttribute(
866       browserAccessibility_, ui::AX_ATTR_NAME);
869 - (id)titleUIElement {
870   int titleElementId;
871   if (browserAccessibility_->GetIntAttribute(
872           ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
873     BrowserAccessibility* titleElement =
874         browserAccessibility_->manager()->GetFromRendererID(titleElementId);
875     if (titleElement)
876       return titleElement->ToBrowserAccessibilityCocoa();
877   }
878   std::vector<int32> labelledby_ids =
879       browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
880   if (labelledby_ids.size() == 1) {
881     BrowserAccessibility* titleElement =
882         browserAccessibility_->manager()->GetFromRendererID(labelledby_ids[0]);
883     if (titleElement)
884       return titleElement->ToBrowserAccessibilityCocoa();
885   }
887   return nil;
890 - (NSString*)url {
891   StringAttribute urlAttribute =
892       [[self role] isEqualToString:@"AXWebArea"] ?
893           ui::AX_ATTR_DOC_URL :
894           ui::AX_ATTR_URL;
895   return NSStringForStringAttribute(browserAccessibility_, urlAttribute);
898 - (id)value {
899   // WebCore uses an attachmentView to get the below behavior.
900   // We do not have any native views backing this object, so need
901   // to approximate Cocoa ax behavior best as we can.
902   NSString* role = [self role];
903   if ([role isEqualToString:@"AXHeading"]) {
904     int level = 0;
905     if (browserAccessibility_->GetIntAttribute(
906             ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
907       return [NSNumber numberWithInt:level];
908     }
909   } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
910     // AXValue does not make sense for pure buttons.
911     return @"";
912   } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
913     int value = 0;
914     bool isAriaPressedDefined;
915     bool isMixed;
916     value = browserAccessibility_->GetAriaTristate(
917         "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
919     if (isMixed)
920       value = 2;
922     return [NSNumber numberWithInt:value];
924   } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
925              [role isEqualToString:NSAccessibilityRadioButtonRole]) {
926     int value = 0;
927     value = GetState(
928         browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
929     value = GetState(
930         browserAccessibility_, ui::AX_STATE_SELECTED) ?
931             1 :
932             value;
934     if (browserAccessibility_->GetBoolAttribute(
935         ui::AX_ATTR_BUTTON_MIXED)) {
936       value = 2;
937     }
938     return [NSNumber numberWithInt:value];
939   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
940              [role isEqualToString:NSAccessibilitySliderRole] ||
941              [role isEqualToString:NSAccessibilityScrollBarRole]) {
942     float floatValue;
943     if (browserAccessibility_->GetFloatAttribute(
944             ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
945       return [NSNumber numberWithFloat:floatValue];
946     }
947   } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
948     int r = browserAccessibility_->GetIntAttribute(
949         ui::AX_ATTR_COLOR_VALUE_RED);
950     int g = browserAccessibility_->GetIntAttribute(
951         ui::AX_ATTR_COLOR_VALUE_GREEN);
952     int b = browserAccessibility_->GetIntAttribute(
953         ui::AX_ATTR_COLOR_VALUE_BLUE);
954     // This string matches the one returned by a native Mac color well.
955     return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
956                 r / 255., g / 255., b / 255.];
957   }
959   return NSStringForStringAttribute(
960       browserAccessibility_, ui::AX_ATTR_VALUE);
963 - (NSString*)valueDescription {
964   return NSStringForStringAttribute(
965       browserAccessibility_, ui::AX_ATTR_VALUE);
968 - (NSValue*)visibleCharacterRange {
969   return [NSValue valueWithRange:
970       NSMakeRange(0, browserAccessibility_->value().length())];
973 - (NSArray*)visibleCells {
974   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
975   const std::vector<int32>& uniqueCellIds =
976       browserAccessibility_->GetIntListAttribute(
977           ui::AX_ATTR_UNIQUE_CELL_IDS);
978   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
979     int id = uniqueCellIds[i];
980     BrowserAccessibility* cell =
981         browserAccessibility_->manager()->GetFromRendererID(id);
982     if (cell)
983       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
984   }
985   return ret;
988 - (NSArray*)visibleColumns {
989   return [self columns];
992 - (NSArray*)visibleRows {
993   return [self rows];
996 - (NSNumber*)visited {
997   return [NSNumber numberWithBool:
998       GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1001 - (id)window {
1002   return [delegate_ window];
1005 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1006   return [attributeToMethodNameMap objectForKey:attribute];
1009 // Returns the accessibility value for the given attribute.  If the value isn't
1010 // supported this will return nil.
1011 - (id)accessibilityAttributeValue:(NSString*)attribute {
1012   if (!browserAccessibility_)
1013     return nil;
1015   SEL selector =
1016       NSSelectorFromString([self methodNameForAttribute:attribute]);
1017   if (selector)
1018     return [self performSelector:selector];
1020   // TODO(dtseng): refactor remaining attributes.
1021   int selStart, selEnd;
1022   if (browserAccessibility_->GetIntAttribute(
1023           ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1024       browserAccessibility_->
1025           GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1026     if (selStart > selEnd)
1027       std::swap(selStart, selEnd);
1028     int selLength = selEnd - selStart;
1029     if ([attribute isEqualToString:
1030         NSAccessibilityInsertionPointLineNumberAttribute]) {
1031       const std::vector<int32>& line_breaks =
1032           browserAccessibility_->GetIntListAttribute(
1033               ui::AX_ATTR_LINE_BREAKS);
1034       for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1035         if (line_breaks[i] > selStart)
1036           return [NSNumber numberWithInt:i];
1037       }
1038       return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1039     }
1040     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1041       std::string value = browserAccessibility_->GetStringAttribute(
1042           ui::AX_ATTR_VALUE);
1043       return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1044     }
1045     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1046       return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1047     }
1048   }
1049   return nil;
1052 // Returns the accessibility value for the given attribute and parameter. If the
1053 // value isn't supported this will return nil.
1054 - (id)accessibilityAttributeValue:(NSString*)attribute
1055                      forParameter:(id)parameter {
1056   if (!browserAccessibility_)
1057     return nil;
1059   const std::vector<int32>& line_breaks =
1060       browserAccessibility_->GetIntListAttribute(
1061           ui::AX_ATTR_LINE_BREAKS);
1062   int len = static_cast<int>(browserAccessibility_->value().size());
1064   if ([attribute isEqualToString:
1065       NSAccessibilityStringForRangeParameterizedAttribute]) {
1066     NSRange range = [(NSValue*)parameter rangeValue];
1067     std::string value = browserAccessibility_->GetStringAttribute(
1068         ui::AX_ATTR_VALUE);
1069     return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1070   }
1072   if ([attribute isEqualToString:
1073       NSAccessibilityLineForIndexParameterizedAttribute]) {
1074     int index = [(NSNumber*)parameter intValue];
1075     for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1076       if (line_breaks[i] > index)
1077         return [NSNumber numberWithInt:i];
1078     }
1079     return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1080   }
1082   if ([attribute isEqualToString:
1083       NSAccessibilityRangeForLineParameterizedAttribute]) {
1084     int line_index = [(NSNumber*)parameter intValue];
1085     int line_count = static_cast<int>(line_breaks.size()) + 1;
1086     if (line_index < 0 || line_index >= line_count)
1087       return nil;
1088     int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1089     int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1090     return [NSValue valueWithRange:
1091         NSMakeRange(start, end - start)];
1092   }
1094   if ([attribute isEqualToString:
1095       NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1096     if ([self internalRole] != ui::AX_ROLE_TABLE &&
1097         [self internalRole] != ui::AX_ROLE_GRID) {
1098       return nil;
1099     }
1100     if (![parameter isKindOfClass:[NSArray self]])
1101       return nil;
1102     NSArray* array = parameter;
1103     int column = [[array objectAtIndex:0] intValue];
1104     int row = [[array objectAtIndex:1] intValue];
1105     int num_columns = browserAccessibility_->GetIntAttribute(
1106         ui::AX_ATTR_TABLE_COLUMN_COUNT);
1107     int num_rows = browserAccessibility_->GetIntAttribute(
1108         ui::AX_ATTR_TABLE_ROW_COUNT);
1109     if (column < 0 || column >= num_columns ||
1110         row < 0 || row >= num_rows) {
1111       return nil;
1112     }
1113     for (size_t i = 0;
1114          i < browserAccessibility_->PlatformChildCount();
1115          ++i) {
1116       BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1117       if (child->role() != ui::AX_ROLE_ROW)
1118         continue;
1119       int rowIndex;
1120       if (!child->GetIntAttribute(
1121               ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1122         continue;
1123       }
1124       if (rowIndex < row)
1125         continue;
1126       if (rowIndex > row)
1127         break;
1128       for (size_t j = 0;
1129            j < child->PlatformChildCount();
1130            ++j) {
1131         BrowserAccessibility* cell = child->PlatformGetChild(j);
1132         if (cell->role() != ui::AX_ROLE_CELL)
1133           continue;
1134         int colIndex;
1135         if (!cell->GetIntAttribute(
1136                 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1137                 &colIndex)) {
1138           continue;
1139         }
1140         if (colIndex == column)
1141           return cell->ToBrowserAccessibilityCocoa();
1142         if (colIndex > column)
1143           break;
1144       }
1145     }
1146     return nil;
1147   }
1149   if ([attribute isEqualToString:
1150       NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1151     if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1152       return nil;
1153     NSRange range = [(NSValue*)parameter rangeValue];
1154     gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1155         range.location, range.length);
1156     NSPoint origin = NSMakePoint(rect.x(), rect.y());
1157     NSSize size = NSMakeSize(rect.width(), rect.height());
1158     NSPoint pointInScreen =
1159         [delegate_ accessibilityPointInScreen:origin size:size];
1160     NSRect nsrect = NSMakeRect(
1161         pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1162     return [NSValue valueWithRect:nsrect];
1163   }
1165   // TODO(dtseng): support the following attributes.
1166   if ([attribute isEqualTo:
1167           NSAccessibilityRangeForPositionParameterizedAttribute] ||
1168       [attribute isEqualTo:
1169           NSAccessibilityRangeForIndexParameterizedAttribute] ||
1170       [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1171       [attribute isEqualTo:
1172           NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1173     return nil;
1174   }
1175   return nil;
1178 // Returns an array of parameterized attributes names that this object will
1179 // respond to.
1180 - (NSArray*)accessibilityParameterizedAttributeNames {
1181   if (!browserAccessibility_)
1182     return nil;
1184   if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1185       [[self role] isEqualToString:NSAccessibilityGridRole]) {
1186     return [NSArray arrayWithObjects:
1187         NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1188         nil];
1189   }
1190   if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1191       [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1192     return [NSArray arrayWithObjects:
1193         NSAccessibilityLineForIndexParameterizedAttribute,
1194         NSAccessibilityRangeForLineParameterizedAttribute,
1195         NSAccessibilityStringForRangeParameterizedAttribute,
1196         NSAccessibilityRangeForPositionParameterizedAttribute,
1197         NSAccessibilityRangeForIndexParameterizedAttribute,
1198         NSAccessibilityBoundsForRangeParameterizedAttribute,
1199         NSAccessibilityRTFForRangeParameterizedAttribute,
1200         NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1201         NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1202         nil];
1203   }
1204   if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1205     return [NSArray arrayWithObjects:
1206         NSAccessibilityBoundsForRangeParameterizedAttribute,
1207         nil];
1208   }
1209   return nil;
1212 // Returns an array of action names that this object will respond to.
1213 - (NSArray*)accessibilityActionNames {
1214   if (!browserAccessibility_)
1215     return nil;
1217   NSMutableArray* ret =
1218       [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1219   NSString* role = [self role];
1220   // TODO(dtseng): this should only get set when there's a default action.
1221   if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1222       ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1223       ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1224     [ret addObject:NSAccessibilityPressAction];
1225   }
1227   return ret;
1230 // Returns a sub-array of values for the given attribute value, starting at
1231 // index, with up to maxCount items.  If the given index is out of bounds,
1232 // or there are no values for the given attribute, it will return nil.
1233 // This method is used for querying subsets of values, without having to
1234 // return a large set of data, such as elements with a large number of
1235 // children.
1236 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1237                                         index:(NSUInteger)index
1238                                      maxCount:(NSUInteger)maxCount {
1239   if (!browserAccessibility_)
1240     return nil;
1242   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1243   if (!fullArray)
1244     return nil;
1245   NSUInteger arrayCount = [fullArray count];
1246   if (index >= arrayCount)
1247     return nil;
1248   NSRange subRange;
1249   if ((index + maxCount) > arrayCount) {
1250     subRange = NSMakeRange(index, arrayCount - index);
1251   } else {
1252     subRange = NSMakeRange(index, maxCount);
1253   }
1254   return [fullArray subarrayWithRange:subRange];
1257 // Returns the count of the specified accessibility array attribute.
1258 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1259   if (!browserAccessibility_)
1260     return nil;
1262   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1263   return [fullArray count];
1266 // Returns the list of accessibility attributes that this object supports.
1267 - (NSArray*)accessibilityAttributeNames {
1268   if (!browserAccessibility_)
1269     return nil;
1271   // General attributes.
1272   NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1273       NSAccessibilityChildrenAttribute,
1274       NSAccessibilityDescriptionAttribute,
1275       NSAccessibilityEnabledAttribute,
1276       NSAccessibilityFocusedAttribute,
1277       NSAccessibilityHelpAttribute,
1278       NSAccessibilityLinkedUIElementsAttribute,
1279       NSAccessibilityParentAttribute,
1280       NSAccessibilityPositionAttribute,
1281       NSAccessibilityRoleAttribute,
1282       NSAccessibilityRoleDescriptionAttribute,
1283       NSAccessibilitySizeAttribute,
1284       NSAccessibilitySubroleAttribute,
1285       NSAccessibilityTitleAttribute,
1286       NSAccessibilityTopLevelUIElementAttribute,
1287       NSAccessibilityValueAttribute,
1288       NSAccessibilityWindowAttribute,
1289       NSAccessibilityURLAttribute,
1290       @"AXAccessKey",
1291       @"AXInvalid",
1292       @"AXRequired",
1293       @"AXVisited",
1294       nil];
1296   // Specific role attributes.
1297   NSString* role = [self role];
1298   NSString* subrole = [self subrole];
1299   if ([role isEqualToString:NSAccessibilityTableRole] ||
1300       [role isEqualToString:NSAccessibilityGridRole]) {
1301     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1302         NSAccessibilityColumnsAttribute,
1303         NSAccessibilityVisibleColumnsAttribute,
1304         NSAccessibilityRowsAttribute,
1305         NSAccessibilityVisibleRowsAttribute,
1306         NSAccessibilityVisibleCellsAttribute,
1307         NSAccessibilityHeaderAttribute,
1308         NSAccessibilityColumnHeaderUIElementsAttribute,
1309         NSAccessibilityRowHeaderUIElementsAttribute,
1310         nil]];
1311   } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1312     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1313         NSAccessibilityIndexAttribute,
1314         NSAccessibilityHeaderAttribute,
1315         NSAccessibilityRowsAttribute,
1316         NSAccessibilityVisibleRowsAttribute,
1317         nil]];
1318   } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1319     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1320         NSAccessibilityColumnIndexRangeAttribute,
1321         NSAccessibilityRowIndexRangeAttribute,
1322         nil]];
1323   } else if ([role isEqualToString:@"AXWebArea"]) {
1324     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1325         @"AXLoaded",
1326         @"AXLoadingProgress",
1327         nil]];
1328   } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1329              [role isEqualToString:NSAccessibilityTextAreaRole]) {
1330     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1331         NSAccessibilityInsertionPointLineNumberAttribute,
1332         NSAccessibilityNumberOfCharactersAttribute,
1333         NSAccessibilitySelectedTextAttribute,
1334         NSAccessibilitySelectedTextRangeAttribute,
1335         NSAccessibilityVisibleCharacterRangeAttribute,
1336         nil]];
1337   } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1338     [ret addObject:NSAccessibilityTabsAttribute];
1339   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1340              [role isEqualToString:NSAccessibilitySliderRole] ||
1341              [role isEqualToString:NSAccessibilityScrollBarRole]) {
1342     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1343         NSAccessibilityMaxValueAttribute,
1344         NSAccessibilityMinValueAttribute,
1345         NSAccessibilityOrientationAttribute,
1346         NSAccessibilityValueDescriptionAttribute,
1347         nil]];
1348   } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1349     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1350         NSAccessibilityDisclosingAttribute,
1351         NSAccessibilityDisclosedByRowAttribute,
1352         NSAccessibilityDisclosureLevelAttribute,
1353         NSAccessibilityDisclosedRowsAttribute,
1354         nil]];
1355   } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1356     if (browserAccessibility_->parent()) {
1357       base::string16 parentRole;
1358       browserAccessibility_->parent()->GetHtmlAttribute(
1359           "role", &parentRole);
1360       const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1361       if (parentRole == treegridRole) {
1362         [ret addObjectsFromArray:[NSArray arrayWithObjects:
1363             NSAccessibilityDisclosingAttribute,
1364             NSAccessibilityDisclosedByRowAttribute,
1365             NSAccessibilityDisclosureLevelAttribute,
1366             NSAccessibilityDisclosedRowsAttribute,
1367             nil]];
1368       } else {
1369         [ret addObjectsFromArray:[NSArray arrayWithObjects:
1370             NSAccessibilityIndexAttribute,
1371             nil]];
1372       }
1373     }
1374   }
1376   // Live regions.
1377   if (browserAccessibility_->HasStringAttribute(
1378           ui::AX_ATTR_LIVE_STATUS)) {
1379     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1380         @"AXARIALive",
1381         @"AXARIARelevant",
1382         nil]];
1383   }
1384   if (browserAccessibility_->HasStringAttribute(
1385           ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1386     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1387         @"AXARIAAtomic",
1388         @"AXARIABusy",
1389         nil]];
1390   }
1392   // Title UI Element.
1393   if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1394       (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1395        browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1396                             .size() == 1)) {
1397     [ret addObjectsFromArray:[NSArray arrayWithObjects:
1398          NSAccessibilityTitleUIElementAttribute,
1399          nil]];
1400   }
1401   // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1402   // for elements which are referred to by labelledby or are labels
1404   return ret;
1407 // Returns the index of the child in this objects array of children.
1408 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1409   if (!browserAccessibility_)
1410     return nil;
1412   NSUInteger index = 0;
1413   for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1414     if ([child isEqual:childToCheck])
1415       return index;
1416     ++index;
1417   }
1418   return NSNotFound;
1421 // Returns whether or not the specified attribute can be set by the
1422 // accessibility API via |accessibilitySetValue:forAttribute:|.
1423 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1424   if (!browserAccessibility_)
1425     return nil;
1427   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1428     return GetState(browserAccessibility_,
1429         ui::AX_STATE_FOCUSABLE);
1430   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1431     return browserAccessibility_->GetBoolAttribute(
1432         ui::AX_ATTR_CAN_SET_VALUE);
1433   }
1434   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1435       ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1436        [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1437     return YES;
1439   return NO;
1442 // Returns whether or not this object should be ignored in the accessibilty
1443 // tree.
1444 - (BOOL)accessibilityIsIgnored {
1445   if (!browserAccessibility_)
1446     return true;
1448   return [self isIgnored];
1451 // Performs the given accessibilty action on the webkit accessibility object
1452 // that backs this object.
1453 - (void)accessibilityPerformAction:(NSString*)action {
1454   if (!browserAccessibility_)
1455     return;
1457   // TODO(feldstein): Support more actions.
1458   if ([action isEqualToString:NSAccessibilityPressAction])
1459     [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
1460   else if ([action isEqualToString:NSAccessibilityShowMenuAction])
1461     [delegate_ performShowMenuAction:self];
1464 // Returns the description of the given action.
1465 - (NSString*)accessibilityActionDescription:(NSString*)action {
1466   if (!browserAccessibility_)
1467     return nil;
1469   return NSAccessibilityActionDescription(action);
1472 // Sets an override value for a specific accessibility attribute.
1473 // This class does not support this.
1474 - (BOOL)accessibilitySetOverrideValue:(id)value
1475                          forAttribute:(NSString*)attribute {
1476   return NO;
1479 // Sets the value for an accessibility attribute via the accessibility API.
1480 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1481   if (!browserAccessibility_)
1482     return;
1484   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1485     NSNumber* focusedNumber = value;
1486     BOOL focused = [focusedNumber intValue];
1487     [delegate_ setAccessibilityFocus:focused
1488                      accessibilityId:browserAccessibility_->renderer_id()];
1489   }
1490   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1491     NSRange range = [(NSValue*)value rangeValue];
1492     [delegate_
1493         accessibilitySetTextSelection:browserAccessibility_->renderer_id()
1494         startOffset:range.location
1495         endOffset:range.location + range.length];
1496   }
1499 // Returns the deepest accessibility child that should not be ignored.
1500 // It is assumed that the hit test has been narrowed down to this object
1501 // or one of its children, so this will never return nil unless this
1502 // object is invalid.
1503 - (id)accessibilityHitTest:(NSPoint)point {
1504   if (!browserAccessibility_)
1505     return nil;
1507   BrowserAccessibilityCocoa* hit = self;
1508   for (BrowserAccessibilityCocoa* child in [self children]) {
1509     if (!child->browserAccessibility_)
1510       continue;
1511     NSPoint origin = [child origin];
1512     NSSize size = [[child size] sizeValue];
1513     NSRect rect;
1514     rect.origin = origin;
1515     rect.size = size;
1516     if (NSPointInRect(point, rect)) {
1517       hit = child;
1518       id childResult = [child accessibilityHitTest:point];
1519       if (![childResult accessibilityIsIgnored]) {
1520         hit = childResult;
1521         break;
1522       }
1523     }
1524   }
1525   return NSAccessibilityUnignoredAncestor(hit);
1528 - (BOOL)isEqual:(id)object {
1529   if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1530     return NO;
1531   return ([self hash] == [object hash]);
1534 - (NSUInteger)hash {
1535   // Potentially called during dealloc.
1536   if (!browserAccessibility_)
1537     return [super hash];
1538   return browserAccessibility_->renderer_id();
1541 - (BOOL)accessibilityShouldUseUniqueId {
1542   return YES;
1545 @end