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