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->GetState() >> state) & 1);
55 RoleMap BuildRoleMap() {
56 const MapEntry roles[] = {
57 { ui::AX_ROLE_ALERT, NSAccessibilityGroupRole },
58 { ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole },
59 { ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole },
60 { ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole },
61 { ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole },
62 { ui::AX_ROLE_BANNER, NSAccessibilityGroupRole },
63 { ui::AX_ROLE_BROWSER, NSAccessibilityBrowserRole },
64 { ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole },
65 { ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole },
66 { ui::AX_ROLE_CANVAS, NSAccessibilityImageRole },
67 { ui::AX_ROLE_CELL, @"AXCell" },
68 { ui::AX_ROLE_CHECK_BOX, NSAccessibilityCheckBoxRole },
69 { ui::AX_ROLE_COLOR_WELL, NSAccessibilityColorWellRole },
70 { ui::AX_ROLE_COLUMN, NSAccessibilityColumnRole },
71 { ui::AX_ROLE_COLUMN_HEADER, @"AXCell" },
72 { ui::AX_ROLE_COMBO_BOX, NSAccessibilityComboBoxRole },
73 { ui::AX_ROLE_COMPLEMENTARY, NSAccessibilityGroupRole },
74 { ui::AX_ROLE_CONTENT_INFO, NSAccessibilityGroupRole },
75 { ui::AX_ROLE_DEFINITION, NSAccessibilityGroupRole },
76 { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, NSAccessibilityGroupRole },
77 { ui::AX_ROLE_DESCRIPTION_LIST_TERM, NSAccessibilityGroupRole },
78 { ui::AX_ROLE_DIALOG, NSAccessibilityGroupRole },
79 { ui::AX_ROLE_DIRECTORY, NSAccessibilityListRole },
80 { ui::AX_ROLE_DISCLOSURE_TRIANGLE, NSAccessibilityDisclosureTriangleRole },
81 { ui::AX_ROLE_DIV, NSAccessibilityGroupRole },
82 { ui::AX_ROLE_DOCUMENT, NSAccessibilityGroupRole },
83 { ui::AX_ROLE_DRAWER, NSAccessibilityDrawerRole },
84 { ui::AX_ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole },
85 { ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole },
86 { ui::AX_ROLE_FORM, NSAccessibilityGroupRole },
87 { ui::AX_ROLE_GRID, NSAccessibilityGridRole },
88 { ui::AX_ROLE_GROUP, NSAccessibilityGroupRole },
89 { ui::AX_ROLE_GROW_AREA, NSAccessibilityGrowAreaRole },
90 { ui::AX_ROLE_HEADING, @"AXHeading" },
91 { ui::AX_ROLE_HELP_TAG, NSAccessibilityHelpTagRole },
92 { ui::AX_ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole },
93 { ui::AX_ROLE_IFRAME, NSAccessibilityGroupRole },
94 { ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole },
95 { ui::AX_ROLE_IMAGE, NSAccessibilityImageRole },
96 { ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
97 { ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
98 { ui::AX_ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
99 { ui::AX_ROLE_LABEL_TEXT, NSAccessibilityGroupRole },
100 { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
101 { ui::AX_ROLE_LIST, NSAccessibilityListRole },
102 { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
103 { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
104 { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
105 { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
106 { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
107 { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
108 { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
109 { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
110 { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
111 { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
112 { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
113 { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
114 { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
115 { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
116 { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
117 { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
118 { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
119 { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
120 { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
121 { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
122 { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
123 { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
124 { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
125 { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
126 { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
127 { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
128 { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
129 { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
130 { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
131 { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
132 { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
133 { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
134 { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
135 { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
136 { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
137 { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
138 { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
139 { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
140 { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
141 { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
142 { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
143 { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
144 { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
145 { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
146 { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
147 { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
148 { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
149 { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
150 { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
151 { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
152 { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityCheckBoxRole },
153 { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
154 { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
155 { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
156 { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
157 { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
158 { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
159 { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
160 { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
162 // TODO(dtseng): we don't correctly support the attributes for these roles.
163 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
167 for (size_t i = 0; i < arraysize(roles); ++i)
168 role_map[roles[i].webKitValue] = roles[i].nativeValue;
172 // A mapping of webkit roles to native roles.
173 NSString* NativeRoleFromAXRole(
174 const ui::AXRole& role) {
175 CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
177 RoleMap::iterator it = web_accessibility_to_native_role.find(role);
178 if (it != web_accessibility_to_native_role.end())
181 return NSAccessibilityUnknownRole;
184 RoleMap BuildSubroleMap() {
185 const MapEntry subroles[] = {
186 { ui::AX_ROLE_ALERT, @"AXApplicationAlert" },
187 { ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
188 { ui::AX_ROLE_ARTICLE, @"AXDocumentArticle" },
189 { ui::AX_ROLE_DEFINITION, @"AXDefinition" },
190 { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
191 { ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
192 { ui::AX_ROLE_DIALOG, @"AXApplicationDialog" },
193 { ui::AX_ROLE_DOCUMENT, @"AXDocument" },
194 { ui::AX_ROLE_FOOTER, @"AXLandmarkContentInfo" },
195 { ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication" },
196 { ui::AX_ROLE_BANNER, @"AXLandmarkBanner" },
197 { ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary" },
198 { ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo" },
199 { ui::AX_ROLE_MAIN, @"AXLandmarkMain" },
200 { ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation" },
201 { ui::AX_ROLE_SEARCH, @"AXLandmarkSearch" },
202 { ui::AX_ROLE_LOG, @"AXApplicationLog" },
203 { ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee" },
204 { ui::AX_ROLE_MATH, @"AXDocumentMath" },
205 { ui::AX_ROLE_NOTE, @"AXDocumentNote" },
206 { ui::AX_ROLE_REGION, @"AXDocumentRegion" },
207 { ui::AX_ROLE_STATUS, @"AXApplicationStatus" },
208 { ui::AX_ROLE_TAB_PANEL, @"AXTabPanel" },
209 { ui::AX_ROLE_TIMER, @"AXApplicationTimer" },
210 { ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton" },
211 { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
212 { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
216 for (size_t i = 0; i < arraysize(subroles); ++i)
217 subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
221 // A mapping of webkit roles to native subroles.
222 NSString* NativeSubroleFromAXRole(
223 const ui::AXRole& role) {
224 CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
225 (BuildSubroleMap()));
226 RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
227 if (it != web_accessibility_to_native_subrole.end())
233 // A mapping from an accessibility attribute to its method name.
234 NSDictionary* attributeToMethodNameMap = nil;
238 @implementation BrowserAccessibilityCocoa
243 NSString* methodName;
244 } attributeToMethodNameContainer[] = {
245 { NSAccessibilityChildrenAttribute, @"children" },
246 { NSAccessibilityColumnsAttribute, @"columns" },
247 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
248 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
249 { NSAccessibilityContentsAttribute, @"contents" },
250 { NSAccessibilityDescriptionAttribute, @"description" },
251 { NSAccessibilityDisclosingAttribute, @"disclosing" },
252 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
253 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
254 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
255 { NSAccessibilityEnabledAttribute, @"enabled" },
256 { NSAccessibilityFocusedAttribute, @"focused" },
257 { NSAccessibilityHeaderAttribute, @"header" },
258 { NSAccessibilityHelpAttribute, @"help" },
259 { NSAccessibilityIndexAttribute, @"index" },
260 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
261 { NSAccessibilityMaxValueAttribute, @"maxValue" },
262 { NSAccessibilityMinValueAttribute, @"minValue" },
263 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
264 { NSAccessibilityOrientationAttribute, @"orientation" },
265 { NSAccessibilityParentAttribute, @"parent" },
266 { NSAccessibilityPositionAttribute, @"position" },
267 { NSAccessibilityRoleAttribute, @"role" },
268 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
269 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
270 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
271 { NSAccessibilityRowsAttribute, @"rows" },
272 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
273 { NSAccessibilitySizeAttribute, @"size" },
274 { NSAccessibilitySubroleAttribute, @"subrole" },
275 { NSAccessibilityTabsAttribute, @"tabs" },
276 { NSAccessibilityTitleAttribute, @"title" },
277 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
278 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
279 { NSAccessibilityURLAttribute, @"url" },
280 { NSAccessibilityValueAttribute, @"value" },
281 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
282 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
283 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
284 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
285 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
286 { NSAccessibilityWindowAttribute, @"window" },
287 { @"AXAccessKey", @"accessKey" },
288 { @"AXARIAAtomic", @"ariaAtomic" },
289 { @"AXARIABusy", @"ariaBusy" },
290 { @"AXARIALive", @"ariaLive" },
291 { @"AXARIARelevant", @"ariaRelevant" },
292 { @"AXInvalid", @"invalid" },
293 { @"AXLoaded", @"loaded" },
294 { @"AXLoadingProgress", @"loadingProgress" },
295 { @"AXRequired", @"required" },
296 { @"AXVisited", @"visited" },
299 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
300 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
301 sizeof(attributeToMethodNameContainer[0]);
302 for (size_t i = 0; i < numAttributes; ++i) {
303 [dict setObject:attributeToMethodNameContainer[i].methodName
304 forKey:attributeToMethodNameContainer[i].attribute];
306 attributeToMethodNameMap = dict;
310 - (id)initWithObject:(BrowserAccessibility*)accessibility {
311 if ((self = [super init]))
312 browserAccessibility_ = accessibility;
317 if (browserAccessibility_) {
318 NSAccessibilityUnregisterUniqueIdForUIElement(self);
319 browserAccessibility_ = NULL;
323 - (NSString*)accessKey {
324 return NSStringForStringAttribute(
325 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
328 - (NSNumber*)ariaAtomic {
329 bool boolValue = browserAccessibility_->GetBoolAttribute(
330 ui::AX_ATTR_LIVE_ATOMIC);
331 return [NSNumber numberWithBool:boolValue];
334 - (NSNumber*)ariaBusy {
335 bool boolValue = browserAccessibility_->GetBoolAttribute(
336 ui::AX_ATTR_LIVE_BUSY);
337 return [NSNumber numberWithBool:boolValue];
340 - (NSString*)ariaLive {
341 return NSStringForStringAttribute(
342 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
345 - (NSString*)ariaRelevant {
346 return NSStringForStringAttribute(
347 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
350 // Returns an array of BrowserAccessibilityCocoa objects, representing the
351 // accessibility children of this object.
352 - (NSArray*)children {
354 uint32 childCount = browserAccessibility_->PlatformChildCount();
355 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
356 for (uint32 index = 0; index < childCount; ++index) {
357 BrowserAccessibilityCocoa* child =
358 browserAccessibility_->PlatformGetChild(index)->
359 ToBrowserAccessibilityCocoa();
360 if ([child isIgnored])
361 [children_ addObjectsFromArray:[child children]];
363 [children_ addObject:child];
366 // Also, add indirect children (if any).
367 const std::vector<int32>& indirectChildIds =
368 browserAccessibility_->GetIntListAttribute(
369 ui::AX_ATTR_INDIRECT_CHILD_IDS);
370 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
371 int32 child_id = indirectChildIds[i];
372 BrowserAccessibility* child =
373 browserAccessibility_->manager()->GetFromID(child_id);
375 // This only became necessary as a result of crbug.com/93095. It should be
376 // a DCHECK in the future.
378 BrowserAccessibilityCocoa* child_cocoa =
379 child->ToBrowserAccessibilityCocoa();
380 [children_ addObject:child_cocoa];
387 - (void)childrenChanged {
388 if (![self isIgnored]) {
391 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
396 - (NSArray*)columnHeaders {
397 if ([self internalRole] != ui::AX_ROLE_TABLE &&
398 [self internalRole] != ui::AX_ROLE_GRID) {
402 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
403 const std::vector<int32>& uniqueCellIds =
404 browserAccessibility_->GetIntListAttribute(
405 ui::AX_ATTR_UNIQUE_CELL_IDS);
406 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
407 int id = uniqueCellIds[i];
408 BrowserAccessibility* cell =
409 browserAccessibility_->manager()->GetFromID(id);
410 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
411 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
416 - (NSValue*)columnIndexRange {
417 if ([self internalRole] != ui::AX_ROLE_CELL)
422 browserAccessibility_->GetIntAttribute(
423 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
424 browserAccessibility_->GetIntAttribute(
425 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
426 if (column >= 0 && colspan >= 1)
427 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
431 - (NSArray*)columns {
432 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
433 for (BrowserAccessibilityCocoa* child in [self children]) {
434 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
435 [ret addObject:child];
440 - (NSString*)description {
441 std::string description;
442 if (browserAccessibility_->GetStringAttribute(
443 ui::AX_ATTR_DESCRIPTION, &description)) {
444 return base::SysUTF8ToNSString(description);
447 // If the role is anything other than an image, or if there's
448 // a title or title UI element, just return an empty string.
449 if (![[self role] isEqualToString:NSAccessibilityImageRole])
451 if (browserAccessibility_->HasStringAttribute(
455 if ([self titleUIElement])
458 // The remaining case is an image where there's no other title.
459 // Return the base part of the filename as the description.
461 if (browserAccessibility_->GetStringAttribute(
462 ui::AX_ATTR_URL, &url)) {
463 // Given a url like http://foo.com/bar/baz.png, just return the
464 // base name, e.g., "baz.png".
465 size_t leftIndex = url.rfind('/');
466 std::string basename =
467 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
468 return base::SysUTF8ToNSString(basename);
474 - (NSNumber*)disclosing {
475 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
476 return [NSNumber numberWithBool:
477 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
483 - (id)disclosedByRow {
484 // The row that contains this row.
485 // It should be the same as the first parent that is a treeitem.
489 - (NSNumber*)disclosureLevel {
490 ui::AXRole role = [self internalRole];
491 if (role == ui::AX_ROLE_ROW ||
492 role == ui::AX_ROLE_TREE_ITEM) {
493 int level = browserAccessibility_->GetIntAttribute(
494 ui::AX_ATTR_HIERARCHICAL_LEVEL);
495 // Mac disclosureLevel is 0-based, but web levels are 1-based.
498 return [NSNumber numberWithInt:level];
504 - (id)disclosedRows {
505 // The rows that are considered inside this row.
509 - (NSNumber*)enabled {
510 return [NSNumber numberWithBool:
511 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
514 - (NSNumber*)focused {
515 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
516 NSNumber* ret = [NSNumber numberWithBool:
517 manager->GetFocus(NULL) == browserAccessibility_];
522 int headerElementId = -1;
523 if ([self internalRole] == ui::AX_ROLE_TABLE ||
524 [self internalRole] == ui::AX_ROLE_GRID) {
525 browserAccessibility_->GetIntAttribute(
526 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
527 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
528 browserAccessibility_->GetIntAttribute(
529 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
530 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
531 browserAccessibility_->GetIntAttribute(
532 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
535 if (headerElementId > 0) {
536 BrowserAccessibility* headerObject =
537 browserAccessibility_->manager()->GetFromID(headerElementId);
539 return headerObject->ToBrowserAccessibilityCocoa();
545 return NSStringForStringAttribute(
546 browserAccessibility_, ui::AX_ATTR_HELP);
550 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
551 int columnIndex = browserAccessibility_->GetIntAttribute(
552 ui::AX_ATTR_TABLE_COLUMN_INDEX);
553 return [NSNumber numberWithInt:columnIndex];
554 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
555 int rowIndex = browserAccessibility_->GetIntAttribute(
556 ui::AX_ATTR_TABLE_ROW_INDEX);
557 return [NSNumber numberWithInt:rowIndex];
563 // Returns whether or not this node should be ignored in the
564 // accessibility tree.
566 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
569 - (NSString*)invalid {
570 base::string16 invalidUTF;
571 if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
573 NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
574 if ([invalid isEqualToString:@"false"] ||
575 [invalid isEqualToString:@""]) {
581 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
582 addTo:(NSMutableArray*)outArray {
583 const std::vector<int32>& attributeValues =
584 browserAccessibility_->GetIntListAttribute(attribute);
585 for (size_t i = 0; i < attributeValues.size(); ++i) {
586 BrowserAccessibility* element =
587 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
589 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
593 - (NSArray*)linkedUIElements {
594 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
595 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
596 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
597 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
598 if ([ret count] == 0)
603 - (NSNumber*)loaded {
604 return [NSNumber numberWithBool:YES];
607 - (NSNumber*)loadingProgress {
608 float floatValue = browserAccessibility_->GetFloatAttribute(
609 ui::AX_ATTR_DOC_LOADING_PROGRESS);
610 return [NSNumber numberWithFloat:floatValue];
613 - (NSNumber*)maxValue {
614 float floatValue = browserAccessibility_->GetFloatAttribute(
615 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
616 return [NSNumber numberWithFloat:floatValue];
619 - (NSNumber*)minValue {
620 float floatValue = browserAccessibility_->GetFloatAttribute(
621 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
622 return [NSNumber numberWithFloat:floatValue];
625 - (NSString*)orientation {
626 // We present a spin button as a vertical slider, with a role description
628 if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
629 return NSAccessibilityVerticalOrientationValue;
631 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
632 return NSAccessibilityVerticalOrientationValue;
634 return NSAccessibilityHorizontalOrientationValue;
637 - (NSNumber*)numberOfCharacters {
638 return [NSNumber numberWithInt:browserAccessibility_->value().length()];
641 // The origin of this accessibility object in the page's document.
642 // This is relative to webkit's top-left origin, not Cocoa's
643 // bottom-left origin.
645 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
646 return NSMakePoint(bounds.x(), bounds.y());
650 // A nil parent means we're the root.
651 if (browserAccessibility_->GetParent()) {
652 return NSAccessibilityUnignoredAncestor(
653 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
655 // Hook back up to RenderWidgetHostViewCocoa.
656 BrowserAccessibilityManagerMac* manager =
657 static_cast<BrowserAccessibilityManagerMac*>(
658 browserAccessibility_->manager());
659 return manager->parent_view();
663 - (NSValue*)position {
664 NSPoint origin = [self origin];
665 NSSize size = [[self size] sizeValue];
666 NSPoint pointInScreen = [self pointInScreen:origin size:size];
667 return [NSValue valueWithPoint:pointInScreen];
670 - (NSNumber*)required {
671 return [NSNumber numberWithBool:
672 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
675 // Returns an enum indicating the role from browserAccessibility_.
676 - (ui::AXRole)internalRole {
677 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
680 - (content::BrowserAccessibilityDelegate*)delegate {
681 return browserAccessibility_->manager() ?
682 browserAccessibility_->manager()->delegate() :
686 - (NSPoint)pointInScreen:(NSPoint)origin
688 if (!browserAccessibility_)
691 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
692 gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
693 return NSMakePoint(point.x(), point.y());
696 // Returns a string indicating the NSAccessibility role of this object.
698 ui::AXRole role = [self internalRole];
699 if (role == ui::AX_ROLE_CANVAS &&
700 browserAccessibility_->GetBoolAttribute(
701 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
702 return NSAccessibilityGroupRole;
704 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
705 bool isAriaPressedDefined;
707 browserAccessibility_->GetAriaTristate("aria-pressed",
708 &isAriaPressedDefined,
710 if (isAriaPressedDefined)
711 return NSAccessibilityCheckBoxRole;
713 return NSAccessibilityButtonRole;
715 return NativeRoleFromAXRole(role);
718 // Returns a string indicating the role description of this object.
719 - (NSString*)roleDescription {
720 NSString* role = [self role];
722 ContentClient* content_client = content::GetContentClient();
724 // The following descriptions are specific to webkit.
725 if ([role isEqualToString:@"AXWebArea"]) {
726 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
727 IDS_AX_ROLE_WEB_AREA));
730 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
731 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
735 if ([role isEqualToString:@"AXHeading"]) {
736 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
737 IDS_AX_ROLE_HEADING));
740 if ([role isEqualToString:NSAccessibilityGroupRole] ||
741 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
743 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
744 ui::AXRole internalRole = [self internalRole];
745 if ((internalRole != ui::AX_ROLE_GROUP &&
746 internalRole != ui::AX_ROLE_LIST_ITEM) ||
747 internalRole == ui::AX_ROLE_TAB) {
748 // TODO(dtseng): This is not localized; see crbug/84814.
749 return base::SysUTF8ToNSString(role);
754 switch([self internalRole]) {
755 case ui::AX_ROLE_FOOTER:
756 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
757 IDS_AX_ROLE_FOOTER));
758 case ui::AX_ROLE_SPIN_BUTTON:
759 // This control is similar to what VoiceOver calls a "stepper".
760 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
761 IDS_AX_ROLE_STEPPER));
762 case ui::AX_ROLE_TOGGLE_BUTTON:
763 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
764 IDS_AX_ROLE_TOGGLE_BUTTON));
769 return NSAccessibilityRoleDescription(role, nil);
772 - (NSArray*)rowHeaders {
773 if ([self internalRole] != ui::AX_ROLE_TABLE &&
774 [self internalRole] != ui::AX_ROLE_GRID) {
778 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
779 const std::vector<int32>& uniqueCellIds =
780 browserAccessibility_->GetIntListAttribute(
781 ui::AX_ATTR_UNIQUE_CELL_IDS);
782 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
783 int id = uniqueCellIds[i];
784 BrowserAccessibility* cell =
785 browserAccessibility_->manager()->GetFromID(id);
786 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
787 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
792 - (NSValue*)rowIndexRange {
793 if ([self internalRole] != ui::AX_ROLE_CELL)
798 browserAccessibility_->GetIntAttribute(
799 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
800 browserAccessibility_->GetIntAttribute(
801 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
802 if (row >= 0 && rowspan >= 1)
803 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
808 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
810 if ([self internalRole] == ui::AX_ROLE_TABLE||
811 [self internalRole] == ui::AX_ROLE_GRID) {
812 for (BrowserAccessibilityCocoa* child in [self children]) {
813 if ([[child role] isEqualToString:NSAccessibilityRowRole])
814 [ret addObject:child];
816 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
817 const std::vector<int32>& indirectChildIds =
818 browserAccessibility_->GetIntListAttribute(
819 ui::AX_ATTR_INDIRECT_CHILD_IDS);
820 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
821 int id = indirectChildIds[i];
822 BrowserAccessibility* rowElement =
823 browserAccessibility_->manager()->GetFromID(id);
825 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
832 // Returns the size of this object.
834 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
835 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
838 // Returns a subrole based upon the role.
839 - (NSString*) subrole {
840 ui::AXRole browserAccessibilityRole = [self internalRole];
841 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
842 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
843 return @"AXSecureTextField";
846 NSString* htmlTag = NSStringForStringAttribute(
847 browserAccessibility_, ui::AX_ATTR_HTML_TAG);
849 if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
850 if ([htmlTag isEqualToString:@"ul"] ||
851 [htmlTag isEqualToString:@"ol"]) {
852 return @"AXContentList";
853 } else if ([htmlTag isEqualToString:@"dl"]) {
854 return @"AXDescriptionList";
858 return NativeSubroleFromAXRole(browserAccessibilityRole);
861 // Returns all tabs in this subtree.
863 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
865 if ([self internalRole] == ui::AX_ROLE_TAB)
866 [tabSubtree addObject:self];
868 for (uint i=0; i < [[self children] count]; ++i) {
869 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
870 if ([tabChildren count] > 0)
871 [tabSubtree addObjectsFromArray:tabChildren];
878 return NSStringForStringAttribute(
879 browserAccessibility_, ui::AX_ATTR_NAME);
882 - (id)titleUIElement {
884 if (browserAccessibility_->GetIntAttribute(
885 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
886 BrowserAccessibility* titleElement =
887 browserAccessibility_->manager()->GetFromID(titleElementId);
889 return titleElement->ToBrowserAccessibilityCocoa();
891 std::vector<int32> labelledby_ids =
892 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
893 if (labelledby_ids.size() == 1) {
894 BrowserAccessibility* titleElement =
895 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
897 return titleElement->ToBrowserAccessibilityCocoa();
904 StringAttribute urlAttribute =
905 [[self role] isEqualToString:@"AXWebArea"] ?
906 ui::AX_ATTR_DOC_URL :
909 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
913 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
917 // WebCore uses an attachmentView to get the below behavior.
918 // We do not have any native views backing this object, so need
919 // to approximate Cocoa ax behavior best as we can.
920 NSString* role = [self role];
921 if ([role isEqualToString:@"AXHeading"]) {
923 if (browserAccessibility_->GetIntAttribute(
924 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
925 return [NSNumber numberWithInt:level];
927 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
928 // AXValue does not make sense for pure buttons.
930 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
932 bool isAriaPressedDefined;
934 value = browserAccessibility_->GetAriaTristate(
935 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
940 return [NSNumber numberWithInt:value];
942 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
943 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
946 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
948 browserAccessibility_, ui::AX_STATE_SELECTED) ?
952 if (browserAccessibility_->GetBoolAttribute(
953 ui::AX_ATTR_BUTTON_MIXED)) {
956 return [NSNumber numberWithInt:value];
957 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
958 [role isEqualToString:NSAccessibilitySliderRole] ||
959 [role isEqualToString:NSAccessibilityScrollBarRole]) {
961 if (browserAccessibility_->GetFloatAttribute(
962 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
963 return [NSNumber numberWithFloat:floatValue];
965 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
966 int r = browserAccessibility_->GetIntAttribute(
967 ui::AX_ATTR_COLOR_VALUE_RED);
968 int g = browserAccessibility_->GetIntAttribute(
969 ui::AX_ATTR_COLOR_VALUE_GREEN);
970 int b = browserAccessibility_->GetIntAttribute(
971 ui::AX_ATTR_COLOR_VALUE_BLUE);
972 // This string matches the one returned by a native Mac color well.
973 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
974 r / 255., g / 255., b / 255.];
977 return NSStringForStringAttribute(
978 browserAccessibility_, ui::AX_ATTR_VALUE);
981 - (NSString*)valueDescription {
982 return NSStringForStringAttribute(
983 browserAccessibility_, ui::AX_ATTR_VALUE);
986 - (NSValue*)visibleCharacterRange {
987 return [NSValue valueWithRange:
988 NSMakeRange(0, browserAccessibility_->value().length())];
991 - (NSArray*)visibleCells {
992 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
993 const std::vector<int32>& uniqueCellIds =
994 browserAccessibility_->GetIntListAttribute(
995 ui::AX_ATTR_UNIQUE_CELL_IDS);
996 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
997 int id = uniqueCellIds[i];
998 BrowserAccessibility* cell =
999 browserAccessibility_->manager()->GetFromID(id);
1001 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1006 - (NSArray*)visibleColumns {
1007 return [self columns];
1010 - (NSArray*)visibleRows {
1014 - (NSNumber*)visited {
1015 return [NSNumber numberWithBool:
1016 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1020 if (!browserAccessibility_)
1023 BrowserAccessibilityManagerMac* manager =
1024 static_cast<BrowserAccessibilityManagerMac*>(
1025 browserAccessibility_->manager());
1026 return [manager->parent_view() window];
1029 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1030 return [attributeToMethodNameMap objectForKey:attribute];
1033 // Returns the accessibility value for the given attribute. If the value isn't
1034 // supported this will return nil.
1035 - (id)accessibilityAttributeValue:(NSString*)attribute {
1036 if (!browserAccessibility_)
1040 NSSelectorFromString([self methodNameForAttribute:attribute]);
1042 return [self performSelector:selector];
1044 // TODO(dtseng): refactor remaining attributes.
1045 int selStart, selEnd;
1046 if (browserAccessibility_->GetIntAttribute(
1047 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1048 browserAccessibility_->
1049 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1050 if (selStart > selEnd)
1051 std::swap(selStart, selEnd);
1052 int selLength = selEnd - selStart;
1053 if ([attribute isEqualToString:
1054 NSAccessibilityInsertionPointLineNumberAttribute]) {
1055 const std::vector<int32>& line_breaks =
1056 browserAccessibility_->GetIntListAttribute(
1057 ui::AX_ATTR_LINE_BREAKS);
1058 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1059 if (line_breaks[i] > selStart)
1060 return [NSNumber numberWithInt:i];
1062 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1064 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1065 std::string value = browserAccessibility_->GetStringAttribute(
1067 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1069 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1070 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1076 // Returns the accessibility value for the given attribute and parameter. If the
1077 // value isn't supported this will return nil.
1078 - (id)accessibilityAttributeValue:(NSString*)attribute
1079 forParameter:(id)parameter {
1080 if (!browserAccessibility_)
1083 const std::vector<int32>& line_breaks =
1084 browserAccessibility_->GetIntListAttribute(
1085 ui::AX_ATTR_LINE_BREAKS);
1086 int len = static_cast<int>(browserAccessibility_->value().size());
1088 if ([attribute isEqualToString:
1089 NSAccessibilityStringForRangeParameterizedAttribute]) {
1090 NSRange range = [(NSValue*)parameter rangeValue];
1091 std::string value = browserAccessibility_->GetStringAttribute(
1093 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1096 if ([attribute isEqualToString:
1097 NSAccessibilityLineForIndexParameterizedAttribute]) {
1098 int index = [(NSNumber*)parameter intValue];
1099 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1100 if (line_breaks[i] > index)
1101 return [NSNumber numberWithInt:i];
1103 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1106 if ([attribute isEqualToString:
1107 NSAccessibilityRangeForLineParameterizedAttribute]) {
1108 int line_index = [(NSNumber*)parameter intValue];
1109 int line_count = static_cast<int>(line_breaks.size()) + 1;
1110 if (line_index < 0 || line_index >= line_count)
1112 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1113 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1114 return [NSValue valueWithRange:
1115 NSMakeRange(start, end - start)];
1118 if ([attribute isEqualToString:
1119 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1120 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1121 [self internalRole] != ui::AX_ROLE_GRID) {
1124 if (![parameter isKindOfClass:[NSArray self]])
1126 NSArray* array = parameter;
1127 int column = [[array objectAtIndex:0] intValue];
1128 int row = [[array objectAtIndex:1] intValue];
1129 int num_columns = browserAccessibility_->GetIntAttribute(
1130 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1131 int num_rows = browserAccessibility_->GetIntAttribute(
1132 ui::AX_ATTR_TABLE_ROW_COUNT);
1133 if (column < 0 || column >= num_columns ||
1134 row < 0 || row >= num_rows) {
1138 i < browserAccessibility_->PlatformChildCount();
1140 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1141 if (child->GetRole() != ui::AX_ROLE_ROW)
1144 if (!child->GetIntAttribute(
1145 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1153 j < child->PlatformChildCount();
1155 BrowserAccessibility* cell = child->PlatformGetChild(j);
1156 if (cell->GetRole() != ui::AX_ROLE_CELL)
1159 if (!cell->GetIntAttribute(
1160 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1164 if (colIndex == column)
1165 return cell->ToBrowserAccessibilityCocoa();
1166 if (colIndex > column)
1173 if ([attribute isEqualToString:
1174 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1175 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1177 NSRange range = [(NSValue*)parameter rangeValue];
1178 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1179 range.location, range.length);
1180 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1181 NSSize size = NSMakeSize(rect.width(), rect.height());
1182 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1183 NSRect nsrect = NSMakeRect(
1184 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1185 return [NSValue valueWithRect:nsrect];
1188 // TODO(dtseng): support the following attributes.
1189 if ([attribute isEqualTo:
1190 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1191 [attribute isEqualTo:
1192 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1193 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1194 [attribute isEqualTo:
1195 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1201 // Returns an array of parameterized attributes names that this object will
1203 - (NSArray*)accessibilityParameterizedAttributeNames {
1204 if (!browserAccessibility_)
1207 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1208 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1209 return [NSArray arrayWithObjects:
1210 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1213 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1214 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1215 return [NSArray arrayWithObjects:
1216 NSAccessibilityLineForIndexParameterizedAttribute,
1217 NSAccessibilityRangeForLineParameterizedAttribute,
1218 NSAccessibilityStringForRangeParameterizedAttribute,
1219 NSAccessibilityRangeForPositionParameterizedAttribute,
1220 NSAccessibilityRangeForIndexParameterizedAttribute,
1221 NSAccessibilityBoundsForRangeParameterizedAttribute,
1222 NSAccessibilityRTFForRangeParameterizedAttribute,
1223 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1224 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1227 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1228 return [NSArray arrayWithObjects:
1229 NSAccessibilityBoundsForRangeParameterizedAttribute,
1235 // Returns an array of action names that this object will respond to.
1236 - (NSArray*)accessibilityActionNames {
1237 if (!browserAccessibility_)
1240 NSMutableArray* ret =
1241 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1242 NSString* role = [self role];
1243 // TODO(dtseng): this should only get set when there's a default action.
1244 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1245 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1246 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1247 [ret addObject:NSAccessibilityPressAction];
1253 // Returns a sub-array of values for the given attribute value, starting at
1254 // index, with up to maxCount items. If the given index is out of bounds,
1255 // or there are no values for the given attribute, it will return nil.
1256 // This method is used for querying subsets of values, without having to
1257 // return a large set of data, such as elements with a large number of
1259 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1260 index:(NSUInteger)index
1261 maxCount:(NSUInteger)maxCount {
1262 if (!browserAccessibility_)
1265 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1268 NSUInteger arrayCount = [fullArray count];
1269 if (index >= arrayCount)
1272 if ((index + maxCount) > arrayCount) {
1273 subRange = NSMakeRange(index, arrayCount - index);
1275 subRange = NSMakeRange(index, maxCount);
1277 return [fullArray subarrayWithRange:subRange];
1280 // Returns the count of the specified accessibility array attribute.
1281 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1282 if (!browserAccessibility_)
1285 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1286 return [fullArray count];
1289 // Returns the list of accessibility attributes that this object supports.
1290 - (NSArray*)accessibilityAttributeNames {
1291 if (!browserAccessibility_)
1294 // General attributes.
1295 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1296 NSAccessibilityChildrenAttribute,
1297 NSAccessibilityDescriptionAttribute,
1298 NSAccessibilityEnabledAttribute,
1299 NSAccessibilityFocusedAttribute,
1300 NSAccessibilityHelpAttribute,
1301 NSAccessibilityLinkedUIElementsAttribute,
1302 NSAccessibilityParentAttribute,
1303 NSAccessibilityPositionAttribute,
1304 NSAccessibilityRoleAttribute,
1305 NSAccessibilityRoleDescriptionAttribute,
1306 NSAccessibilitySizeAttribute,
1307 NSAccessibilitySubroleAttribute,
1308 NSAccessibilityTitleAttribute,
1309 NSAccessibilityTopLevelUIElementAttribute,
1310 NSAccessibilityValueAttribute,
1311 NSAccessibilityWindowAttribute,
1318 // Specific role attributes.
1319 NSString* role = [self role];
1320 NSString* subrole = [self subrole];
1321 if ([role isEqualToString:NSAccessibilityTableRole] ||
1322 [role isEqualToString:NSAccessibilityGridRole]) {
1323 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1324 NSAccessibilityColumnsAttribute,
1325 NSAccessibilityVisibleColumnsAttribute,
1326 NSAccessibilityRowsAttribute,
1327 NSAccessibilityVisibleRowsAttribute,
1328 NSAccessibilityVisibleCellsAttribute,
1329 NSAccessibilityHeaderAttribute,
1330 NSAccessibilityColumnHeaderUIElementsAttribute,
1331 NSAccessibilityRowHeaderUIElementsAttribute,
1333 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1334 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1335 NSAccessibilityIndexAttribute,
1336 NSAccessibilityHeaderAttribute,
1337 NSAccessibilityRowsAttribute,
1338 NSAccessibilityVisibleRowsAttribute,
1340 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1341 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1342 NSAccessibilityColumnIndexRangeAttribute,
1343 NSAccessibilityRowIndexRangeAttribute,
1345 } else if ([role isEqualToString:@"AXWebArea"]) {
1346 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1348 @"AXLoadingProgress",
1350 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1351 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1352 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1353 NSAccessibilityInsertionPointLineNumberAttribute,
1354 NSAccessibilityNumberOfCharactersAttribute,
1355 NSAccessibilitySelectedTextAttribute,
1356 NSAccessibilitySelectedTextRangeAttribute,
1357 NSAccessibilityVisibleCharacterRangeAttribute,
1359 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1360 [ret addObject:NSAccessibilityTabsAttribute];
1361 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1362 [role isEqualToString:NSAccessibilitySliderRole] ||
1363 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1364 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1365 NSAccessibilityMaxValueAttribute,
1366 NSAccessibilityMinValueAttribute,
1367 NSAccessibilityOrientationAttribute,
1368 NSAccessibilityValueDescriptionAttribute,
1370 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1371 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1372 NSAccessibilityDisclosingAttribute,
1373 NSAccessibilityDisclosedByRowAttribute,
1374 NSAccessibilityDisclosureLevelAttribute,
1375 NSAccessibilityDisclosedRowsAttribute,
1377 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1378 if (browserAccessibility_->GetParent()) {
1379 base::string16 parentRole;
1380 browserAccessibility_->GetParent()->GetHtmlAttribute(
1381 "role", &parentRole);
1382 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1383 if (parentRole == treegridRole) {
1384 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1385 NSAccessibilityDisclosingAttribute,
1386 NSAccessibilityDisclosedByRowAttribute,
1387 NSAccessibilityDisclosureLevelAttribute,
1388 NSAccessibilityDisclosedRowsAttribute,
1391 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1392 NSAccessibilityIndexAttribute,
1398 // Add the url attribute only if it has a valid url.
1399 if ([self url] != nil) {
1400 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1401 NSAccessibilityURLAttribute,
1406 if (browserAccessibility_->HasStringAttribute(
1407 ui::AX_ATTR_LIVE_STATUS)) {
1408 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1413 if (browserAccessibility_->HasStringAttribute(
1414 ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1415 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1421 // Title UI Element.
1422 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1423 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1424 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1426 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1427 NSAccessibilityTitleUIElementAttribute,
1430 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1431 // for elements which are referred to by labelledby or are labels
1436 // Returns the index of the child in this objects array of children.
1437 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1438 if (!browserAccessibility_)
1441 NSUInteger index = 0;
1442 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1443 if ([child isEqual:childToCheck])
1450 // Returns whether or not the specified attribute can be set by the
1451 // accessibility API via |accessibilitySetValue:forAttribute:|.
1452 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1453 if (!browserAccessibility_)
1456 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1457 return GetState(browserAccessibility_,
1458 ui::AX_STATE_FOCUSABLE);
1459 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1460 return browserAccessibility_->GetBoolAttribute(
1461 ui::AX_ATTR_CAN_SET_VALUE);
1463 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1464 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1465 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1471 // Returns whether or not this object should be ignored in the accessibilty
1473 - (BOOL)accessibilityIsIgnored {
1474 if (!browserAccessibility_)
1477 return [self isIgnored];
1480 // Performs the given accessibilty action on the webkit accessibility object
1481 // that backs this object.
1482 - (void)accessibilityPerformAction:(NSString*)action {
1483 if (!browserAccessibility_)
1486 // TODO(dmazzoni): Support more actions.
1487 if ([action isEqualToString:NSAccessibilityPressAction]) {
1488 [self delegate]->AccessibilityDoDefaultAction(
1489 browserAccessibility_->GetId());
1490 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1491 [self delegate]->AccessibilityShowMenu(browserAccessibility_->GetId());
1495 // Returns the description of the given action.
1496 - (NSString*)accessibilityActionDescription:(NSString*)action {
1497 if (!browserAccessibility_)
1500 return NSAccessibilityActionDescription(action);
1503 // Sets an override value for a specific accessibility attribute.
1504 // This class does not support this.
1505 - (BOOL)accessibilitySetOverrideValue:(id)value
1506 forAttribute:(NSString*)attribute {
1510 // Sets the value for an accessibility attribute via the accessibility API.
1511 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1512 if (!browserAccessibility_)
1515 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1516 NSNumber* focusedNumber = value;
1517 BOOL focused = [focusedNumber intValue];
1519 [self delegate]->AccessibilitySetFocus(browserAccessibility_->GetId());
1521 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1522 NSRange range = [(NSValue*)value rangeValue];
1523 [self delegate]->AccessibilitySetTextSelection(
1524 browserAccessibility_->GetId(),
1525 range.location, range.location + range.length);
1529 // Returns the deepest accessibility child that should not be ignored.
1530 // It is assumed that the hit test has been narrowed down to this object
1531 // or one of its children, so this will never return nil unless this
1532 // object is invalid.
1533 - (id)accessibilityHitTest:(NSPoint)point {
1534 if (!browserAccessibility_)
1537 BrowserAccessibilityCocoa* hit = self;
1538 for (BrowserAccessibilityCocoa* child in [self children]) {
1539 if (!child->browserAccessibility_)
1541 NSPoint origin = [child origin];
1542 NSSize size = [[child size] sizeValue];
1544 rect.origin = origin;
1546 if (NSPointInRect(point, rect)) {
1548 id childResult = [child accessibilityHitTest:point];
1549 if (![childResult accessibilityIsIgnored]) {
1555 return NSAccessibilityUnignoredAncestor(hit);
1558 - (BOOL)isEqual:(id)object {
1559 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1561 return ([self hash] == [object hash]);
1564 - (NSUInteger)hash {
1565 // Potentially called during dealloc.
1566 if (!browserAccessibility_)
1567 return [super hash];
1568 return browserAccessibility_->GetId();
1571 - (BOOL)accessibilityShouldUseUniqueId {