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_TEXT, NSAccessibilityGroupRole },
99 { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
100 { ui::AX_ROLE_LIST, NSAccessibilityListRole },
101 { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
102 { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
103 { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
104 { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
105 { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
106 { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
107 { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
108 { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
109 { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
110 { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
111 { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
112 { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
113 { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
114 { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
115 { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
116 { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
117 { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
118 { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
119 { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
120 { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
121 { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
122 { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
123 { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
124 { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
125 { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
126 { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
127 { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
128 { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
129 { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
130 { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
131 { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
132 { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
133 { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
134 { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
135 { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
136 { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
137 { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
138 { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
139 { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
140 { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
141 { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
142 { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
143 { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
144 { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
145 { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
146 { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
147 { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
148 { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
149 { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
150 { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
151 { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityCheckBoxRole },
152 { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
153 { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
154 { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
155 { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
156 { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
157 { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
158 { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
159 { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
161 // TODO(dtseng): we don't correctly support the attributes for these roles.
162 // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
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_TOGGLE_BUTTON, @"AXToggleButton" },
210 { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
211 { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
215 for (size_t i = 0; i < arraysize(subroles); ++i)
216 subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
220 // A mapping of webkit roles to native subroles.
221 NSString* NativeSubroleFromAXRole(
222 const ui::AXRole& role) {
223 CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
224 (BuildSubroleMap()));
225 RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
226 if (it != web_accessibility_to_native_subrole.end())
232 // A mapping from an accessibility attribute to its method name.
233 NSDictionary* attributeToMethodNameMap = nil;
237 @implementation BrowserAccessibilityCocoa
242 NSString* methodName;
243 } attributeToMethodNameContainer[] = {
244 { NSAccessibilityChildrenAttribute, @"children" },
245 { NSAccessibilityColumnsAttribute, @"columns" },
246 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
247 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
248 { NSAccessibilityContentsAttribute, @"contents" },
249 { NSAccessibilityDescriptionAttribute, @"description" },
250 { NSAccessibilityDisclosingAttribute, @"disclosing" },
251 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
252 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
253 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
254 { NSAccessibilityEnabledAttribute, @"enabled" },
255 { NSAccessibilityFocusedAttribute, @"focused" },
256 { NSAccessibilityHeaderAttribute, @"header" },
257 { NSAccessibilityHelpAttribute, @"help" },
258 { NSAccessibilityIndexAttribute, @"index" },
259 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
260 { NSAccessibilityMaxValueAttribute, @"maxValue" },
261 { NSAccessibilityMinValueAttribute, @"minValue" },
262 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
263 { NSAccessibilityOrientationAttribute, @"orientation" },
264 { NSAccessibilityParentAttribute, @"parent" },
265 { NSAccessibilityPositionAttribute, @"position" },
266 { NSAccessibilityRoleAttribute, @"role" },
267 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
268 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
269 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
270 { NSAccessibilityRowsAttribute, @"rows" },
271 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
272 { NSAccessibilitySizeAttribute, @"size" },
273 { NSAccessibilitySubroleAttribute, @"subrole" },
274 { NSAccessibilityTabsAttribute, @"tabs" },
275 { NSAccessibilityTitleAttribute, @"title" },
276 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
277 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
278 { NSAccessibilityURLAttribute, @"url" },
279 { NSAccessibilityValueAttribute, @"value" },
280 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
281 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
282 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
283 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
284 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
285 { NSAccessibilityWindowAttribute, @"window" },
286 { @"AXAccessKey", @"accessKey" },
287 { @"AXARIAAtomic", @"ariaAtomic" },
288 { @"AXARIABusy", @"ariaBusy" },
289 { @"AXARIALive", @"ariaLive" },
290 { @"AXARIARelevant", @"ariaRelevant" },
291 { @"AXInvalid", @"invalid" },
292 { @"AXLoaded", @"loaded" },
293 { @"AXLoadingProgress", @"loadingProgress" },
294 { @"AXRequired", @"required" },
295 { @"AXVisited", @"visited" },
298 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
299 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
300 sizeof(attributeToMethodNameContainer[0]);
301 for (size_t i = 0; i < numAttributes; ++i) {
302 [dict setObject:attributeToMethodNameContainer[i].methodName
303 forKey:attributeToMethodNameContainer[i].attribute];
305 attributeToMethodNameMap = dict;
309 - (id)initWithObject:(BrowserAccessibility*)accessibility
310 delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate {
311 if ((self = [super init])) {
312 browserAccessibility_ = accessibility;
313 delegate_ = delegate;
319 if (browserAccessibility_) {
320 NSAccessibilityUnregisterUniqueIdForUIElement(self);
321 browserAccessibility_ = NULL;
325 - (NSString*)accessKey {
326 return NSStringForStringAttribute(
327 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
330 - (NSNumber*)ariaAtomic {
331 bool boolValue = browserAccessibility_->GetBoolAttribute(
332 ui::AX_ATTR_LIVE_ATOMIC);
333 return [NSNumber numberWithBool:boolValue];
336 - (NSNumber*)ariaBusy {
337 bool boolValue = browserAccessibility_->GetBoolAttribute(
338 ui::AX_ATTR_LIVE_BUSY);
339 return [NSNumber numberWithBool:boolValue];
342 - (NSString*)ariaLive {
343 return NSStringForStringAttribute(
344 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
347 - (NSString*)ariaRelevant {
348 return NSStringForStringAttribute(
349 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
352 // Returns an array of BrowserAccessibilityCocoa objects, representing the
353 // accessibility children of this object.
354 - (NSArray*)children {
356 uint32 childCount = browserAccessibility_->PlatformChildCount();
357 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
358 for (uint32 index = 0; index < childCount; ++index) {
359 BrowserAccessibilityCocoa* child =
360 browserAccessibility_->PlatformGetChild(index)->
361 ToBrowserAccessibilityCocoa();
362 if ([child isIgnored])
363 [children_ addObjectsFromArray:[child children]];
365 [children_ addObject:child];
368 // Also, add indirect children (if any).
369 const std::vector<int32>& indirectChildIds =
370 browserAccessibility_->GetIntListAttribute(
371 ui::AX_ATTR_INDIRECT_CHILD_IDS);
372 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
373 int32 child_id = indirectChildIds[i];
374 BrowserAccessibility* child =
375 browserAccessibility_->manager()->GetFromRendererID(child_id);
377 // This only became necessary as a result of crbug.com/93095. It should be
378 // a DCHECK in the future.
380 BrowserAccessibilityCocoa* child_cocoa =
381 child->ToBrowserAccessibilityCocoa();
382 [children_ addObject:child_cocoa];
389 - (void)childrenChanged {
390 if (![self isIgnored]) {
393 [browserAccessibility_->parent()->ToBrowserAccessibilityCocoa()
398 - (NSArray*)columnHeaders {
399 if ([self internalRole] != ui::AX_ROLE_TABLE &&
400 [self internalRole] != ui::AX_ROLE_GRID) {
404 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
405 const std::vector<int32>& uniqueCellIds =
406 browserAccessibility_->GetIntListAttribute(
407 ui::AX_ATTR_UNIQUE_CELL_IDS);
408 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
409 int id = uniqueCellIds[i];
410 BrowserAccessibility* cell =
411 browserAccessibility_->manager()->GetFromRendererID(id);
412 if (cell && cell->role() == ui::AX_ROLE_COLUMN_HEADER)
413 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
418 - (NSValue*)columnIndexRange {
419 if ([self internalRole] != ui::AX_ROLE_CELL)
424 browserAccessibility_->GetIntAttribute(
425 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
426 browserAccessibility_->GetIntAttribute(
427 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
428 if (column >= 0 && colspan >= 1)
429 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
433 - (NSArray*)columns {
434 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
435 for (BrowserAccessibilityCocoa* child in [self children]) {
436 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
437 [ret addObject:child];
442 - (NSString*)description {
443 std::string description;
444 if (browserAccessibility_->GetStringAttribute(
445 ui::AX_ATTR_DESCRIPTION, &description)) {
446 return base::SysUTF8ToNSString(description);
449 // If the role is anything other than an image, or if there's
450 // a title or title UI element, just return an empty string.
451 if (![[self role] isEqualToString:NSAccessibilityImageRole])
453 if (browserAccessibility_->HasStringAttribute(
457 if ([self titleUIElement])
460 // The remaining case is an image where there's no other title.
461 // Return the base part of the filename as the description.
463 if (browserAccessibility_->GetStringAttribute(
464 ui::AX_ATTR_URL, &url)) {
465 // Given a url like http://foo.com/bar/baz.png, just return the
466 // base name, e.g., "baz.png".
467 size_t leftIndex = url.rfind('/');
468 std::string basename =
469 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
470 return base::SysUTF8ToNSString(basename);
476 - (NSNumber*)disclosing {
477 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
478 return [NSNumber numberWithBool:
479 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
485 - (id)disclosedByRow {
486 // The row that contains this row.
487 // It should be the same as the first parent that is a treeitem.
491 - (NSNumber*)disclosureLevel {
492 ui::AXRole role = [self internalRole];
493 if (role == ui::AX_ROLE_ROW ||
494 role == ui::AX_ROLE_TREE_ITEM) {
495 int level = browserAccessibility_->GetIntAttribute(
496 ui::AX_ATTR_HIERARCHICAL_LEVEL);
497 // Mac disclosureLevel is 0-based, but web levels are 1-based.
500 return [NSNumber numberWithInt:level];
506 - (id)disclosedRows {
507 // The rows that are considered inside this row.
511 - (NSNumber*)enabled {
512 return [NSNumber numberWithBool:
513 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
516 - (NSNumber*)focused {
517 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
518 NSNumber* ret = [NSNumber numberWithBool:
519 manager->GetFocus(NULL) == browserAccessibility_];
524 int headerElementId = -1;
525 if ([self internalRole] == ui::AX_ROLE_TABLE ||
526 [self internalRole] == ui::AX_ROLE_GRID) {
527 browserAccessibility_->GetIntAttribute(
528 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
529 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
530 browserAccessibility_->GetIntAttribute(
531 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
532 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
533 browserAccessibility_->GetIntAttribute(
534 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
537 if (headerElementId > 0) {
538 BrowserAccessibility* headerObject =
539 browserAccessibility_->manager()->GetFromRendererID(headerElementId);
541 return headerObject->ToBrowserAccessibilityCocoa();
547 return NSStringForStringAttribute(
548 browserAccessibility_, ui::AX_ATTR_HELP);
552 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
553 int columnIndex = browserAccessibility_->GetIntAttribute(
554 ui::AX_ATTR_TABLE_COLUMN_INDEX);
555 return [NSNumber numberWithInt:columnIndex];
556 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
557 int rowIndex = browserAccessibility_->GetIntAttribute(
558 ui::AX_ATTR_TABLE_ROW_INDEX);
559 return [NSNumber numberWithInt:rowIndex];
565 // Returns whether or not this node should be ignored in the
566 // accessibility tree.
568 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
571 - (NSString*)invalid {
572 base::string16 invalidUTF;
573 if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
575 NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
576 if ([invalid isEqualToString:@"false"] ||
577 [invalid isEqualToString:@""]) {
583 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
584 addTo:(NSMutableArray*)outArray {
585 const std::vector<int32>& attributeValues =
586 browserAccessibility_->GetIntListAttribute(attribute);
587 for (size_t i = 0; i < attributeValues.size(); ++i) {
588 BrowserAccessibility* element =
589 browserAccessibility_->manager()->GetFromRendererID(attributeValues[i]);
591 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
595 - (NSArray*)linkedUIElements {
596 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
597 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
598 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
599 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
600 if ([ret count] == 0)
605 - (NSNumber*)loaded {
606 return [NSNumber numberWithBool:YES];
609 - (NSNumber*)loadingProgress {
610 float floatValue = browserAccessibility_->GetFloatAttribute(
611 ui::AX_ATTR_DOC_LOADING_PROGRESS);
612 return [NSNumber numberWithFloat:floatValue];
615 - (NSNumber*)maxValue {
616 float floatValue = browserAccessibility_->GetFloatAttribute(
617 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
618 return [NSNumber numberWithFloat:floatValue];
621 - (NSNumber*)minValue {
622 float floatValue = browserAccessibility_->GetFloatAttribute(
623 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
624 return [NSNumber numberWithFloat:floatValue];
627 - (NSString*)orientation {
628 // We present a spin button as a vertical slider, with a role description
630 if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
631 return NSAccessibilityVerticalOrientationValue;
633 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
634 return NSAccessibilityVerticalOrientationValue;
636 return NSAccessibilityHorizontalOrientationValue;
639 - (NSNumber*)numberOfCharacters {
640 return [NSNumber numberWithInt:browserAccessibility_->value().length()];
643 // The origin of this accessibility object in the page's document.
644 // This is relative to webkit's top-left origin, not Cocoa's
645 // bottom-left origin.
647 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
648 return NSMakePoint(bounds.x(), bounds.y());
652 // A nil parent means we're the root.
653 if (browserAccessibility_->parent()) {
654 return NSAccessibilityUnignoredAncestor(
655 browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
657 // Hook back up to RenderWidgetHostViewCocoa.
658 BrowserAccessibilityManagerMac* manager =
659 static_cast<BrowserAccessibilityManagerMac*>(
660 browserAccessibility_->manager());
661 return manager->parent_view();
665 - (NSValue*)position {
666 NSPoint origin = [self origin];
667 NSSize size = [[self size] sizeValue];
668 NSPoint pointInScreen =
669 [delegate_ accessibilityPointInScreen:origin size:size];
670 return [NSValue valueWithPoint:pointInScreen];
673 - (NSNumber*)required {
674 return [NSNumber numberWithBool:
675 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
678 // Returns an enum indicating the role from browserAccessibility_.
679 - (ui::AXRole)internalRole {
680 return static_cast<ui::AXRole>(browserAccessibility_->role());
683 // Returns a string indicating the NSAccessibility role of this object.
685 ui::AXRole role = [self internalRole];
686 if (role == ui::AX_ROLE_CANVAS &&
687 browserAccessibility_->GetBoolAttribute(
688 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
689 return NSAccessibilityGroupRole;
691 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
692 bool isAriaPressedDefined;
694 browserAccessibility_->GetAriaTristate("aria-pressed",
695 &isAriaPressedDefined,
697 if (isAriaPressedDefined)
698 return NSAccessibilityCheckBoxRole;
700 return NSAccessibilityButtonRole;
702 return NativeRoleFromAXRole(role);
705 // Returns a string indicating the role description of this object.
706 - (NSString*)roleDescription {
707 NSString* role = [self role];
709 ContentClient* content_client = content::GetContentClient();
711 // The following descriptions are specific to webkit.
712 if ([role isEqualToString:@"AXWebArea"]) {
713 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
714 IDS_AX_ROLE_WEB_AREA));
717 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
718 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
722 if ([role isEqualToString:@"AXHeading"]) {
723 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
724 IDS_AX_ROLE_HEADING));
727 if ([role isEqualToString:NSAccessibilityGroupRole] ||
728 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
730 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
731 ui::AXRole internalRole = [self internalRole];
732 if ((internalRole != ui::AX_ROLE_GROUP &&
733 internalRole != ui::AX_ROLE_LIST_ITEM) ||
734 internalRole == ui::AX_ROLE_TAB) {
735 // TODO(dtseng): This is not localized; see crbug/84814.
736 return base::SysUTF8ToNSString(role);
741 switch([self internalRole]) {
742 case ui::AX_ROLE_FOOTER:
743 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
744 IDS_AX_ROLE_FOOTER));
745 case ui::AX_ROLE_SPIN_BUTTON:
746 // This control is similar to what VoiceOver calls a "stepper".
747 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
748 IDS_AX_ROLE_STEPPER));
749 case ui::AX_ROLE_TOGGLE_BUTTON:
750 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
751 IDS_AX_ROLE_TOGGLE_BUTTON));
756 return NSAccessibilityRoleDescription(role, nil);
759 - (NSArray*)rowHeaders {
760 if ([self internalRole] != ui::AX_ROLE_TABLE &&
761 [self internalRole] != ui::AX_ROLE_GRID) {
765 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
766 const std::vector<int32>& uniqueCellIds =
767 browserAccessibility_->GetIntListAttribute(
768 ui::AX_ATTR_UNIQUE_CELL_IDS);
769 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
770 int id = uniqueCellIds[i];
771 BrowserAccessibility* cell =
772 browserAccessibility_->manager()->GetFromRendererID(id);
773 if (cell && cell->role() == ui::AX_ROLE_ROW_HEADER)
774 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
779 - (NSValue*)rowIndexRange {
780 if ([self internalRole] != ui::AX_ROLE_CELL)
785 browserAccessibility_->GetIntAttribute(
786 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
787 browserAccessibility_->GetIntAttribute(
788 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
789 if (row >= 0 && rowspan >= 1)
790 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
795 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
797 if ([self internalRole] == ui::AX_ROLE_TABLE||
798 [self internalRole] == ui::AX_ROLE_GRID) {
799 for (BrowserAccessibilityCocoa* child in [self children]) {
800 if ([[child role] isEqualToString:NSAccessibilityRowRole])
801 [ret addObject:child];
803 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
804 const std::vector<int32>& indirectChildIds =
805 browserAccessibility_->GetIntListAttribute(
806 ui::AX_ATTR_INDIRECT_CHILD_IDS);
807 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
808 int id = indirectChildIds[i];
809 BrowserAccessibility* rowElement =
810 browserAccessibility_->manager()->GetFromRendererID(id);
812 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
819 // Returns the size of this object.
821 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
822 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
825 // Returns a subrole based upon the role.
826 - (NSString*) subrole {
827 ui::AXRole browserAccessibilityRole = [self internalRole];
828 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
829 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
830 return @"AXSecureTextField";
833 NSString* htmlTag = NSStringForStringAttribute(
834 browserAccessibility_, ui::AX_ATTR_HTML_TAG);
836 if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
837 if ([htmlTag isEqualToString:@"ul"] ||
838 [htmlTag isEqualToString:@"ol"]) {
839 return @"AXContentList";
840 } else if ([htmlTag isEqualToString:@"dl"]) {
841 return @"AXDescriptionList";
845 return NativeSubroleFromAXRole(browserAccessibilityRole);
848 // Returns all tabs in this subtree.
850 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
852 if ([self internalRole] == ui::AX_ROLE_TAB)
853 [tabSubtree addObject:self];
855 for (uint i=0; i < [[self children] count]; ++i) {
856 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
857 if ([tabChildren count] > 0)
858 [tabSubtree addObjectsFromArray:tabChildren];
865 return NSStringForStringAttribute(
866 browserAccessibility_, ui::AX_ATTR_NAME);
869 - (id)titleUIElement {
871 if (browserAccessibility_->GetIntAttribute(
872 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
873 BrowserAccessibility* titleElement =
874 browserAccessibility_->manager()->GetFromRendererID(titleElementId);
876 return titleElement->ToBrowserAccessibilityCocoa();
878 std::vector<int32> labelledby_ids =
879 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
880 if (labelledby_ids.size() == 1) {
881 BrowserAccessibility* titleElement =
882 browserAccessibility_->manager()->GetFromRendererID(labelledby_ids[0]);
884 return titleElement->ToBrowserAccessibilityCocoa();
891 StringAttribute urlAttribute =
892 [[self role] isEqualToString:@"AXWebArea"] ?
893 ui::AX_ATTR_DOC_URL :
895 return NSStringForStringAttribute(browserAccessibility_, urlAttribute);
899 // WebCore uses an attachmentView to get the below behavior.
900 // We do not have any native views backing this object, so need
901 // to approximate Cocoa ax behavior best as we can.
902 NSString* role = [self role];
903 if ([role isEqualToString:@"AXHeading"]) {
905 if (browserAccessibility_->GetIntAttribute(
906 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
907 return [NSNumber numberWithInt:level];
909 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
910 // AXValue does not make sense for pure buttons.
912 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
914 bool isAriaPressedDefined;
916 value = browserAccessibility_->GetAriaTristate(
917 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
922 return [NSNumber numberWithInt:value];
924 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
925 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
928 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
930 browserAccessibility_, ui::AX_STATE_SELECTED) ?
934 if (browserAccessibility_->GetBoolAttribute(
935 ui::AX_ATTR_BUTTON_MIXED)) {
938 return [NSNumber numberWithInt:value];
939 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
940 [role isEqualToString:NSAccessibilitySliderRole] ||
941 [role isEqualToString:NSAccessibilityScrollBarRole]) {
943 if (browserAccessibility_->GetFloatAttribute(
944 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
945 return [NSNumber numberWithFloat:floatValue];
947 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
948 int r = browserAccessibility_->GetIntAttribute(
949 ui::AX_ATTR_COLOR_VALUE_RED);
950 int g = browserAccessibility_->GetIntAttribute(
951 ui::AX_ATTR_COLOR_VALUE_GREEN);
952 int b = browserAccessibility_->GetIntAttribute(
953 ui::AX_ATTR_COLOR_VALUE_BLUE);
954 // This string matches the one returned by a native Mac color well.
955 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
956 r / 255., g / 255., b / 255.];
959 return NSStringForStringAttribute(
960 browserAccessibility_, ui::AX_ATTR_VALUE);
963 - (NSString*)valueDescription {
964 return NSStringForStringAttribute(
965 browserAccessibility_, ui::AX_ATTR_VALUE);
968 - (NSValue*)visibleCharacterRange {
969 return [NSValue valueWithRange:
970 NSMakeRange(0, browserAccessibility_->value().length())];
973 - (NSArray*)visibleCells {
974 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
975 const std::vector<int32>& uniqueCellIds =
976 browserAccessibility_->GetIntListAttribute(
977 ui::AX_ATTR_UNIQUE_CELL_IDS);
978 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
979 int id = uniqueCellIds[i];
980 BrowserAccessibility* cell =
981 browserAccessibility_->manager()->GetFromRendererID(id);
983 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
988 - (NSArray*)visibleColumns {
989 return [self columns];
992 - (NSArray*)visibleRows {
996 - (NSNumber*)visited {
997 return [NSNumber numberWithBool:
998 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1002 return [delegate_ window];
1005 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1006 return [attributeToMethodNameMap objectForKey:attribute];
1009 // Returns the accessibility value for the given attribute. If the value isn't
1010 // supported this will return nil.
1011 - (id)accessibilityAttributeValue:(NSString*)attribute {
1012 if (!browserAccessibility_)
1016 NSSelectorFromString([self methodNameForAttribute:attribute]);
1018 return [self performSelector:selector];
1020 // TODO(dtseng): refactor remaining attributes.
1021 int selStart, selEnd;
1022 if (browserAccessibility_->GetIntAttribute(
1023 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1024 browserAccessibility_->
1025 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1026 if (selStart > selEnd)
1027 std::swap(selStart, selEnd);
1028 int selLength = selEnd - selStart;
1029 if ([attribute isEqualToString:
1030 NSAccessibilityInsertionPointLineNumberAttribute]) {
1031 const std::vector<int32>& line_breaks =
1032 browserAccessibility_->GetIntListAttribute(
1033 ui::AX_ATTR_LINE_BREAKS);
1034 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1035 if (line_breaks[i] > selStart)
1036 return [NSNumber numberWithInt:i];
1038 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1040 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1041 std::string value = browserAccessibility_->GetStringAttribute(
1043 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1045 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1046 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1052 // Returns the accessibility value for the given attribute and parameter. If the
1053 // value isn't supported this will return nil.
1054 - (id)accessibilityAttributeValue:(NSString*)attribute
1055 forParameter:(id)parameter {
1056 if (!browserAccessibility_)
1059 const std::vector<int32>& line_breaks =
1060 browserAccessibility_->GetIntListAttribute(
1061 ui::AX_ATTR_LINE_BREAKS);
1062 int len = static_cast<int>(browserAccessibility_->value().size());
1064 if ([attribute isEqualToString:
1065 NSAccessibilityStringForRangeParameterizedAttribute]) {
1066 NSRange range = [(NSValue*)parameter rangeValue];
1067 std::string value = browserAccessibility_->GetStringAttribute(
1069 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1072 if ([attribute isEqualToString:
1073 NSAccessibilityLineForIndexParameterizedAttribute]) {
1074 int index = [(NSNumber*)parameter intValue];
1075 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1076 if (line_breaks[i] > index)
1077 return [NSNumber numberWithInt:i];
1079 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1082 if ([attribute isEqualToString:
1083 NSAccessibilityRangeForLineParameterizedAttribute]) {
1084 int line_index = [(NSNumber*)parameter intValue];
1085 int line_count = static_cast<int>(line_breaks.size()) + 1;
1086 if (line_index < 0 || line_index >= line_count)
1088 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1089 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1090 return [NSValue valueWithRange:
1091 NSMakeRange(start, end - start)];
1094 if ([attribute isEqualToString:
1095 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1096 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1097 [self internalRole] != ui::AX_ROLE_GRID) {
1100 if (![parameter isKindOfClass:[NSArray self]])
1102 NSArray* array = parameter;
1103 int column = [[array objectAtIndex:0] intValue];
1104 int row = [[array objectAtIndex:1] intValue];
1105 int num_columns = browserAccessibility_->GetIntAttribute(
1106 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1107 int num_rows = browserAccessibility_->GetIntAttribute(
1108 ui::AX_ATTR_TABLE_ROW_COUNT);
1109 if (column < 0 || column >= num_columns ||
1110 row < 0 || row >= num_rows) {
1114 i < browserAccessibility_->PlatformChildCount();
1116 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1117 if (child->role() != ui::AX_ROLE_ROW)
1120 if (!child->GetIntAttribute(
1121 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1129 j < child->PlatformChildCount();
1131 BrowserAccessibility* cell = child->PlatformGetChild(j);
1132 if (cell->role() != ui::AX_ROLE_CELL)
1135 if (!cell->GetIntAttribute(
1136 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1140 if (colIndex == column)
1141 return cell->ToBrowserAccessibilityCocoa();
1142 if (colIndex > column)
1149 if ([attribute isEqualToString:
1150 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1151 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1153 NSRange range = [(NSValue*)parameter rangeValue];
1154 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1155 range.location, range.length);
1156 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1157 NSSize size = NSMakeSize(rect.width(), rect.height());
1158 NSPoint pointInScreen =
1159 [delegate_ accessibilityPointInScreen:origin size:size];
1160 NSRect nsrect = NSMakeRect(
1161 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1162 return [NSValue valueWithRect:nsrect];
1165 // TODO(dtseng): support the following attributes.
1166 if ([attribute isEqualTo:
1167 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1168 [attribute isEqualTo:
1169 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1170 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1171 [attribute isEqualTo:
1172 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1178 // Returns an array of parameterized attributes names that this object will
1180 - (NSArray*)accessibilityParameterizedAttributeNames {
1181 if (!browserAccessibility_)
1184 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1185 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1186 return [NSArray arrayWithObjects:
1187 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1190 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1191 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1192 return [NSArray arrayWithObjects:
1193 NSAccessibilityLineForIndexParameterizedAttribute,
1194 NSAccessibilityRangeForLineParameterizedAttribute,
1195 NSAccessibilityStringForRangeParameterizedAttribute,
1196 NSAccessibilityRangeForPositionParameterizedAttribute,
1197 NSAccessibilityRangeForIndexParameterizedAttribute,
1198 NSAccessibilityBoundsForRangeParameterizedAttribute,
1199 NSAccessibilityRTFForRangeParameterizedAttribute,
1200 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1201 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1204 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1205 return [NSArray arrayWithObjects:
1206 NSAccessibilityBoundsForRangeParameterizedAttribute,
1212 // Returns an array of action names that this object will respond to.
1213 - (NSArray*)accessibilityActionNames {
1214 if (!browserAccessibility_)
1217 NSMutableArray* ret =
1218 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1219 NSString* role = [self role];
1220 // TODO(dtseng): this should only get set when there's a default action.
1221 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1222 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1223 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1224 [ret addObject:NSAccessibilityPressAction];
1230 // Returns a sub-array of values for the given attribute value, starting at
1231 // index, with up to maxCount items. If the given index is out of bounds,
1232 // or there are no values for the given attribute, it will return nil.
1233 // This method is used for querying subsets of values, without having to
1234 // return a large set of data, such as elements with a large number of
1236 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1237 index:(NSUInteger)index
1238 maxCount:(NSUInteger)maxCount {
1239 if (!browserAccessibility_)
1242 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1245 NSUInteger arrayCount = [fullArray count];
1246 if (index >= arrayCount)
1249 if ((index + maxCount) > arrayCount) {
1250 subRange = NSMakeRange(index, arrayCount - index);
1252 subRange = NSMakeRange(index, maxCount);
1254 return [fullArray subarrayWithRange:subRange];
1257 // Returns the count of the specified accessibility array attribute.
1258 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1259 if (!browserAccessibility_)
1262 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1263 return [fullArray count];
1266 // Returns the list of accessibility attributes that this object supports.
1267 - (NSArray*)accessibilityAttributeNames {
1268 if (!browserAccessibility_)
1271 // General attributes.
1272 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1273 NSAccessibilityChildrenAttribute,
1274 NSAccessibilityDescriptionAttribute,
1275 NSAccessibilityEnabledAttribute,
1276 NSAccessibilityFocusedAttribute,
1277 NSAccessibilityHelpAttribute,
1278 NSAccessibilityLinkedUIElementsAttribute,
1279 NSAccessibilityParentAttribute,
1280 NSAccessibilityPositionAttribute,
1281 NSAccessibilityRoleAttribute,
1282 NSAccessibilityRoleDescriptionAttribute,
1283 NSAccessibilitySizeAttribute,
1284 NSAccessibilitySubroleAttribute,
1285 NSAccessibilityTitleAttribute,
1286 NSAccessibilityTopLevelUIElementAttribute,
1287 NSAccessibilityValueAttribute,
1288 NSAccessibilityWindowAttribute,
1289 NSAccessibilityURLAttribute,
1296 // Specific role attributes.
1297 NSString* role = [self role];
1298 NSString* subrole = [self subrole];
1299 if ([role isEqualToString:NSAccessibilityTableRole] ||
1300 [role isEqualToString:NSAccessibilityGridRole]) {
1301 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1302 NSAccessibilityColumnsAttribute,
1303 NSAccessibilityVisibleColumnsAttribute,
1304 NSAccessibilityRowsAttribute,
1305 NSAccessibilityVisibleRowsAttribute,
1306 NSAccessibilityVisibleCellsAttribute,
1307 NSAccessibilityHeaderAttribute,
1308 NSAccessibilityColumnHeaderUIElementsAttribute,
1309 NSAccessibilityRowHeaderUIElementsAttribute,
1311 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1312 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1313 NSAccessibilityIndexAttribute,
1314 NSAccessibilityHeaderAttribute,
1315 NSAccessibilityRowsAttribute,
1316 NSAccessibilityVisibleRowsAttribute,
1318 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1319 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1320 NSAccessibilityColumnIndexRangeAttribute,
1321 NSAccessibilityRowIndexRangeAttribute,
1323 } else if ([role isEqualToString:@"AXWebArea"]) {
1324 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1326 @"AXLoadingProgress",
1328 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1329 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1330 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1331 NSAccessibilityInsertionPointLineNumberAttribute,
1332 NSAccessibilityNumberOfCharactersAttribute,
1333 NSAccessibilitySelectedTextAttribute,
1334 NSAccessibilitySelectedTextRangeAttribute,
1335 NSAccessibilityVisibleCharacterRangeAttribute,
1337 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1338 [ret addObject:NSAccessibilityTabsAttribute];
1339 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1340 [role isEqualToString:NSAccessibilitySliderRole] ||
1341 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1342 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1343 NSAccessibilityMaxValueAttribute,
1344 NSAccessibilityMinValueAttribute,
1345 NSAccessibilityOrientationAttribute,
1346 NSAccessibilityValueDescriptionAttribute,
1348 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1349 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1350 NSAccessibilityDisclosingAttribute,
1351 NSAccessibilityDisclosedByRowAttribute,
1352 NSAccessibilityDisclosureLevelAttribute,
1353 NSAccessibilityDisclosedRowsAttribute,
1355 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1356 if (browserAccessibility_->parent()) {
1357 base::string16 parentRole;
1358 browserAccessibility_->parent()->GetHtmlAttribute(
1359 "role", &parentRole);
1360 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1361 if (parentRole == treegridRole) {
1362 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1363 NSAccessibilityDisclosingAttribute,
1364 NSAccessibilityDisclosedByRowAttribute,
1365 NSAccessibilityDisclosureLevelAttribute,
1366 NSAccessibilityDisclosedRowsAttribute,
1369 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1370 NSAccessibilityIndexAttribute,
1377 if (browserAccessibility_->HasStringAttribute(
1378 ui::AX_ATTR_LIVE_STATUS)) {
1379 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1384 if (browserAccessibility_->HasStringAttribute(
1385 ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1386 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1392 // Title UI Element.
1393 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1394 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1395 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1397 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1398 NSAccessibilityTitleUIElementAttribute,
1401 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1402 // for elements which are referred to by labelledby or are labels
1407 // Returns the index of the child in this objects array of children.
1408 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1409 if (!browserAccessibility_)
1412 NSUInteger index = 0;
1413 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1414 if ([child isEqual:childToCheck])
1421 // Returns whether or not the specified attribute can be set by the
1422 // accessibility API via |accessibilitySetValue:forAttribute:|.
1423 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1424 if (!browserAccessibility_)
1427 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1428 return GetState(browserAccessibility_,
1429 ui::AX_STATE_FOCUSABLE);
1430 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1431 return browserAccessibility_->GetBoolAttribute(
1432 ui::AX_ATTR_CAN_SET_VALUE);
1434 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1435 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1436 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1442 // Returns whether or not this object should be ignored in the accessibilty
1444 - (BOOL)accessibilityIsIgnored {
1445 if (!browserAccessibility_)
1448 return [self isIgnored];
1451 // Performs the given accessibilty action on the webkit accessibility object
1452 // that backs this object.
1453 - (void)accessibilityPerformAction:(NSString*)action {
1454 if (!browserAccessibility_)
1457 // TODO(feldstein): Support more actions.
1458 if ([action isEqualToString:NSAccessibilityPressAction])
1459 [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
1460 else if ([action isEqualToString:NSAccessibilityShowMenuAction])
1461 [delegate_ performShowMenuAction:self];
1464 // Returns the description of the given action.
1465 - (NSString*)accessibilityActionDescription:(NSString*)action {
1466 if (!browserAccessibility_)
1469 return NSAccessibilityActionDescription(action);
1472 // Sets an override value for a specific accessibility attribute.
1473 // This class does not support this.
1474 - (BOOL)accessibilitySetOverrideValue:(id)value
1475 forAttribute:(NSString*)attribute {
1479 // Sets the value for an accessibility attribute via the accessibility API.
1480 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1481 if (!browserAccessibility_)
1484 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1485 NSNumber* focusedNumber = value;
1486 BOOL focused = [focusedNumber intValue];
1487 [delegate_ setAccessibilityFocus:focused
1488 accessibilityId:browserAccessibility_->renderer_id()];
1490 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1491 NSRange range = [(NSValue*)value rangeValue];
1493 accessibilitySetTextSelection:browserAccessibility_->renderer_id()
1494 startOffset:range.location
1495 endOffset:range.location + range.length];
1499 // Returns the deepest accessibility child that should not be ignored.
1500 // It is assumed that the hit test has been narrowed down to this object
1501 // or one of its children, so this will never return nil unless this
1502 // object is invalid.
1503 - (id)accessibilityHitTest:(NSPoint)point {
1504 if (!browserAccessibility_)
1507 BrowserAccessibilityCocoa* hit = self;
1508 for (BrowserAccessibilityCocoa* child in [self children]) {
1509 if (!child->browserAccessibility_)
1511 NSPoint origin = [child origin];
1512 NSSize size = [[child size] sizeValue];
1514 rect.origin = origin;
1516 if (NSPointInRect(point, rect)) {
1518 id childResult = [child accessibilityHitTest:point];
1519 if (![childResult accessibilityIsIgnored]) {
1525 return NSAccessibilityUnignoredAncestor(hit);
1528 - (BOOL)isEqual:(id)object {
1529 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1531 return ([self hash] == [object hash]);
1534 - (NSUInteger)hash {
1535 // Potentially called during dealloc.
1536 if (!browserAccessibility_)
1537 return [super hash];
1538 return browserAccessibility_->renderer_id();
1541 - (BOOL)accessibilityShouldUseUniqueId {