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);
25 using content::AccessibilityNodeData;
26 using content::BrowserAccessibility;
27 using content::BrowserAccessibilityManager;
28 using content::BrowserAccessibilityManagerMac;
29 using content::ContentClient;
30 typedef AccessibilityNodeData::StringAttribute StringAttribute;
34 // Returns an autoreleased copy of the AccessibilityNodeData's attribute.
35 NSString* NSStringForStringAttribute(
36 BrowserAccessibility* browserAccessibility,
37 StringAttribute attribute) {
38 return base::SysUTF8ToNSString(
39 browserAccessibility->GetStringAttribute(attribute));
43 WebKit::WebAXRole webKitValue;
44 NSString* nativeValue;
47 typedef std::map<WebKit::WebAXRole, NSString*> RoleMap;
49 // GetState checks the bitmask used in AccessibilityNodeData to check
50 // if the given state was set on the accessibility object.
51 bool GetState(BrowserAccessibility* accessibility, WebKit::WebAXState state) {
52 return ((accessibility->state() >> state) & 1);
55 RoleMap BuildRoleMap() {
56 const MapEntry roles[] = {
57 { WebKit::WebAXRoleAlert, NSAccessibilityGroupRole },
58 { WebKit::WebAXRoleAlertDialog, NSAccessibilityGroupRole },
59 { WebKit::WebAXRoleAnnotation, NSAccessibilityUnknownRole },
60 { WebKit::WebAXRoleApplication, NSAccessibilityGroupRole },
61 { WebKit::WebAXRoleArticle, NSAccessibilityGroupRole },
62 { WebKit::WebAXRoleBrowser, NSAccessibilityBrowserRole },
63 { WebKit::WebAXRoleBusyIndicator, NSAccessibilityBusyIndicatorRole },
64 { WebKit::WebAXRoleButton, NSAccessibilityButtonRole },
65 { WebKit::WebAXRoleCanvas, NSAccessibilityImageRole },
66 { WebKit::WebAXRoleCell, @"AXCell" },
67 { WebKit::WebAXRoleCheckBox, NSAccessibilityCheckBoxRole },
68 { WebKit::WebAXRoleColorWell, NSAccessibilityColorWellRole },
69 { WebKit::WebAXRoleComboBox, NSAccessibilityComboBoxRole },
70 { WebKit::WebAXRoleColumn, NSAccessibilityColumnRole },
71 { WebKit::WebAXRoleColumnHeader, @"AXCell" },
72 { WebKit::WebAXRoleDefinition, NSAccessibilityGroupRole },
73 { WebKit::WebAXRoleDescriptionListDetail, NSAccessibilityGroupRole },
74 { WebKit::WebAXRoleDescriptionListTerm, NSAccessibilityGroupRole },
75 { WebKit::WebAXRoleDialog, NSAccessibilityGroupRole },
76 { WebKit::WebAXRoleDirectory, NSAccessibilityListRole },
77 { WebKit::WebAXRoleDisclosureTriangle,
78 NSAccessibilityDisclosureTriangleRole },
79 { WebKit::WebAXRoleDiv, NSAccessibilityGroupRole },
80 { WebKit::WebAXRoleDocument, NSAccessibilityGroupRole },
81 { WebKit::WebAXRoleDrawer, NSAccessibilityDrawerRole },
82 { WebKit::WebAXRoleEditableText, NSAccessibilityTextFieldRole },
83 { WebKit::WebAXRoleFooter, NSAccessibilityGroupRole },
84 { WebKit::WebAXRoleForm, NSAccessibilityGroupRole },
85 { WebKit::WebAXRoleGrid, NSAccessibilityGridRole },
86 { WebKit::WebAXRoleGroup, NSAccessibilityGroupRole },
87 { WebKit::WebAXRoleGrowArea, NSAccessibilityGrowAreaRole },
88 { WebKit::WebAXRoleHeading, @"AXHeading" },
89 { WebKit::WebAXRoleHelpTag, NSAccessibilityHelpTagRole },
90 { WebKit::WebAXRoleHorizontalRule, NSAccessibilityGroupRole },
91 { WebKit::WebAXRoleIgnored, NSAccessibilityUnknownRole },
92 { WebKit::WebAXRoleImage, NSAccessibilityImageRole },
93 { WebKit::WebAXRoleImageMap, NSAccessibilityGroupRole },
94 { WebKit::WebAXRoleImageMapLink, NSAccessibilityLinkRole },
95 { WebKit::WebAXRoleIncrementor, NSAccessibilityIncrementorRole },
96 { WebKit::WebAXRoleLabel, NSAccessibilityGroupRole },
97 { WebKit::WebAXRoleApplication, NSAccessibilityGroupRole },
98 { WebKit::WebAXRoleBanner, NSAccessibilityGroupRole },
99 { WebKit::WebAXRoleComplementary, NSAccessibilityGroupRole },
100 { WebKit::WebAXRoleContentInfo, NSAccessibilityGroupRole },
101 { WebKit::WebAXRoleMain, NSAccessibilityGroupRole },
102 { WebKit::WebAXRoleNavigation, NSAccessibilityGroupRole },
103 { WebKit::WebAXRoleSearch, NSAccessibilityGroupRole },
104 { WebKit::WebAXRoleLink, NSAccessibilityLinkRole },
105 { WebKit::WebAXRoleList, NSAccessibilityListRole },
106 { WebKit::WebAXRoleListItem, NSAccessibilityGroupRole },
107 { WebKit::WebAXRoleListMarker, @"AXListMarker" },
108 { WebKit::WebAXRoleListBox, NSAccessibilityListRole },
109 { WebKit::WebAXRoleListBoxOption, NSAccessibilityStaticTextRole },
110 { WebKit::WebAXRoleLog, NSAccessibilityGroupRole },
111 { WebKit::WebAXRoleMarquee, NSAccessibilityGroupRole },
112 { WebKit::WebAXRoleMath, NSAccessibilityGroupRole },
113 { WebKit::WebAXRoleMatte, NSAccessibilityMatteRole },
114 { WebKit::WebAXRoleMenu, NSAccessibilityMenuRole },
115 { WebKit::WebAXRoleMenuBar, NSAccessibilityMenuBarRole },
116 { WebKit::WebAXRoleMenuItem, NSAccessibilityMenuItemRole },
117 { WebKit::WebAXRoleMenuButton, NSAccessibilityButtonRole },
118 { WebKit::WebAXRoleMenuListOption, NSAccessibilityMenuItemRole },
119 { WebKit::WebAXRoleMenuListPopup, NSAccessibilityUnknownRole },
120 { WebKit::WebAXRoleNote, NSAccessibilityGroupRole },
121 { WebKit::WebAXRoleOutline, NSAccessibilityOutlineRole },
122 { WebKit::WebAXRoleParagraph, NSAccessibilityGroupRole },
123 { WebKit::WebAXRolePopUpButton, NSAccessibilityPopUpButtonRole },
124 { WebKit::WebAXRolePresentational, NSAccessibilityGroupRole },
125 { WebKit::WebAXRoleProgressIndicator,
126 NSAccessibilityProgressIndicatorRole },
127 { WebKit::WebAXRoleRadioButton, NSAccessibilityRadioButtonRole },
128 { WebKit::WebAXRoleRadioGroup, NSAccessibilityRadioGroupRole },
129 { WebKit::WebAXRoleRegion, NSAccessibilityGroupRole },
130 { WebKit::WebAXRoleRootWebArea, @"AXWebArea" },
131 { WebKit::WebAXRoleRow, NSAccessibilityRowRole },
132 { WebKit::WebAXRoleRowHeader, @"AXCell" },
133 { WebKit::WebAXRoleRuler, NSAccessibilityRulerRole },
134 { WebKit::WebAXRoleRulerMarker, NSAccessibilityRulerMarkerRole },
135 // TODO(dtseng): we don't correctly support the attributes for these roles.
136 // { WebKit::WebAXRoleScrollArea, NSAccessibilityScrollAreaRole },
137 { WebKit::WebAXRoleScrollBar, NSAccessibilityScrollBarRole },
138 { WebKit::WebAXRoleSheet, NSAccessibilitySheetRole },
139 { WebKit::WebAXRoleSlider, NSAccessibilitySliderRole },
140 { WebKit::WebAXRoleSliderThumb, NSAccessibilityValueIndicatorRole },
141 { WebKit::WebAXRoleSpinButton, NSAccessibilitySliderRole },
142 { WebKit::WebAXRoleSplitter, NSAccessibilitySplitterRole },
143 { WebKit::WebAXRoleSplitGroup, NSAccessibilitySplitGroupRole },
144 { WebKit::WebAXRoleStaticText, NSAccessibilityStaticTextRole },
145 { WebKit::WebAXRoleStatus, NSAccessibilityGroupRole },
146 { WebKit::WebAXRoleSVGRoot, NSAccessibilityGroupRole },
147 { WebKit::WebAXRoleSystemWide, NSAccessibilityUnknownRole },
148 { WebKit::WebAXRoleTab, NSAccessibilityRadioButtonRole },
149 { WebKit::WebAXRoleTabList, NSAccessibilityTabGroupRole },
150 { WebKit::WebAXRoleTabPanel, NSAccessibilityGroupRole },
151 { WebKit::WebAXRoleTable, NSAccessibilityTableRole },
152 { WebKit::WebAXRoleTableHeaderContainer, NSAccessibilityGroupRole },
153 { WebKit::WebAXRoleTextArea, NSAccessibilityTextAreaRole },
154 { WebKit::WebAXRoleTextField, NSAccessibilityTextFieldRole },
155 { WebKit::WebAXRoleTimer, NSAccessibilityGroupRole },
156 { WebKit::WebAXRoleToggleButton, NSAccessibilityButtonRole },
157 { WebKit::WebAXRoleToolbar, NSAccessibilityToolbarRole },
158 { WebKit::WebAXRoleUserInterfaceTooltip, NSAccessibilityGroupRole },
159 { WebKit::WebAXRoleTree, NSAccessibilityOutlineRole },
160 { WebKit::WebAXRoleTreeGrid, NSAccessibilityTableRole },
161 { WebKit::WebAXRoleTreeItem, NSAccessibilityRowRole },
162 { WebKit::WebAXRoleValueIndicator, NSAccessibilityValueIndicatorRole },
163 { WebKit::WebAXRoleLink, NSAccessibilityLinkRole },
164 { WebKit::WebAXRoleWebArea, @"AXWebArea" },
165 { WebKit::WebAXRoleWindow, NSAccessibilityWindowRole },
169 for (size_t i = 0; i < arraysize(roles); ++i)
170 role_map[roles[i].webKitValue] = roles[i].nativeValue;
174 // A mapping of webkit roles to native roles.
175 NSString* NativeRoleFromAccessibilityNodeDataRole(
176 const WebKit::WebAXRole& role) {
177 CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
179 RoleMap::iterator it = web_accessibility_to_native_role.find(role);
180 if (it != web_accessibility_to_native_role.end())
183 return NSAccessibilityUnknownRole;
186 RoleMap BuildSubroleMap() {
187 const MapEntry subroles[] = {
188 { WebKit::WebAXRoleAlert, @"AXApplicationAlert" },
189 { WebKit::WebAXRoleAlertDialog, @"AXApplicationAlertDialog" },
190 { WebKit::WebAXRoleArticle, @"AXDocumentArticle" },
191 { WebKit::WebAXRoleDefinition, @"AXDefinition" },
192 { WebKit::WebAXRoleDescriptionListDetail, @"AXDescription" },
193 { WebKit::WebAXRoleDescriptionListTerm, @"AXTerm" },
194 { WebKit::WebAXRoleDialog, @"AXApplicationDialog" },
195 { WebKit::WebAXRoleDocument, @"AXDocument" },
196 { WebKit::WebAXRoleFooter, @"AXLandmarkContentInfo" },
197 { WebKit::WebAXRoleApplication, @"AXLandmarkApplication" },
198 { WebKit::WebAXRoleBanner, @"AXLandmarkBanner" },
199 { WebKit::WebAXRoleComplementary, @"AXLandmarkComplementary" },
200 { WebKit::WebAXRoleContentInfo, @"AXLandmarkContentInfo" },
201 { WebKit::WebAXRoleMain, @"AXLandmarkMain" },
202 { WebKit::WebAXRoleNavigation, @"AXLandmarkNavigation" },
203 { WebKit::WebAXRoleSearch, @"AXLandmarkSearch" },
204 { WebKit::WebAXRoleLog, @"AXApplicationLog" },
205 { WebKit::WebAXRoleMarquee, @"AXApplicationMarquee" },
206 { WebKit::WebAXRoleMath, @"AXDocumentMath" },
207 { WebKit::WebAXRoleNote, @"AXDocumentNote" },
208 { WebKit::WebAXRoleRegion, @"AXDocumentRegion" },
209 { WebKit::WebAXRoleStatus, @"AXApplicationStatus" },
210 { WebKit::WebAXRoleTabPanel, @"AXTabPanel" },
211 { WebKit::WebAXRoleTimer, @"AXApplicationTimer" },
212 { WebKit::WebAXRoleUserInterfaceTooltip, @"AXUserInterfaceTooltip" },
213 { WebKit::WebAXRoleTreeItem, NSAccessibilityOutlineRowSubrole },
217 for (size_t i = 0; i < arraysize(subroles); ++i)
218 subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
222 // A mapping of webkit roles to native subroles.
223 NSString* NativeSubroleFromAccessibilityNodeDataRole(
224 const WebKit::WebAXRole& role) {
225 CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
226 (BuildSubroleMap()));
227 RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
228 if (it != web_accessibility_to_native_subrole.end())
234 // A mapping from an accessibility attribute to its method name.
235 NSDictionary* attributeToMethodNameMap = nil;
239 @implementation BrowserAccessibilityCocoa
244 NSString* methodName;
245 } attributeToMethodNameContainer[] = {
246 { NSAccessibilityChildrenAttribute, @"children" },
247 { NSAccessibilityColumnsAttribute, @"columns" },
248 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
249 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
250 { NSAccessibilityContentsAttribute, @"contents" },
251 { NSAccessibilityDescriptionAttribute, @"description" },
252 { NSAccessibilityDisclosingAttribute, @"disclosing" },
253 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
254 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
255 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
256 { NSAccessibilityEnabledAttribute, @"enabled" },
257 { NSAccessibilityFocusedAttribute, @"focused" },
258 { NSAccessibilityHeaderAttribute, @"header" },
259 { NSAccessibilityHelpAttribute, @"help" },
260 { NSAccessibilityIndexAttribute, @"index" },
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 { 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_, AccessibilityNodeData::ATTR_ACCESS_KEY);
330 - (NSNumber*)ariaAtomic {
331 bool boolValue = browserAccessibility_->GetBoolAttribute(
332 AccessibilityNodeData::ATTR_LIVE_ATOMIC);
333 return [NSNumber numberWithBool:boolValue];
336 - (NSNumber*)ariaBusy {
337 bool boolValue = browserAccessibility_->GetBoolAttribute(
338 AccessibilityNodeData::ATTR_LIVE_BUSY);
339 return [NSNumber numberWithBool:boolValue];
342 - (NSString*)ariaLive {
343 return NSStringForStringAttribute(
344 browserAccessibility_, AccessibilityNodeData::ATTR_LIVE_STATUS);
347 - (NSString*)ariaRelevant {
348 return NSStringForStringAttribute(
349 browserAccessibility_, AccessibilityNodeData::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 AccessibilityNodeData::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] != WebKit::WebAXRoleTable &&
400 [self internalRole] != WebKit::WebAXRoleGrid) {
404 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
405 const std::vector<int32>& uniqueCellIds =
406 browserAccessibility_->GetIntListAttribute(
407 AccessibilityNodeData::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() == WebKit::WebAXRoleColumnHeader)
413 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
418 - (NSValue*)columnIndexRange {
419 if ([self internalRole] != WebKit::WebAXRoleCell)
424 browserAccessibility_->GetIntAttribute(
425 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
426 browserAccessibility_->GetIntAttribute(
427 AccessibilityNodeData::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 AccessibilityNodeData::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(
454 AccessibilityNodeData::ATTR_NAME)) {
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 AccessibilityNodeData::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] == WebKit::WebAXRoleTreeItem) {
478 return [NSNumber numberWithBool:
479 GetState(browserAccessibility_, WebKit::WebAXStateExpanded)];
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 WebKit::WebAXRole role = [self internalRole];
493 if (role == WebKit::WebAXRoleRow ||
494 role == WebKit::WebAXRoleTreeItem) {
495 int level = browserAccessibility_->GetIntAttribute(
496 AccessibilityNodeData::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_, WebKit::WebAXStateEnabled)];
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] == WebKit::WebAXRoleTable ||
526 [self internalRole] == WebKit::WebAXRoleGrid) {
527 browserAccessibility_->GetIntAttribute(
528 AccessibilityNodeData::ATTR_TABLE_HEADER_ID, &headerElementId);
529 } else if ([self internalRole] == WebKit::WebAXRoleColumn) {
530 browserAccessibility_->GetIntAttribute(
531 AccessibilityNodeData::ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
532 } else if ([self internalRole] == WebKit::WebAXRoleRow) {
533 browserAccessibility_->GetIntAttribute(
534 AccessibilityNodeData::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_, AccessibilityNodeData::ATTR_HELP);
552 if ([self internalRole] == WebKit::WebAXRoleColumn) {
553 int columnIndex = browserAccessibility_->GetIntAttribute(
554 AccessibilityNodeData::ATTR_TABLE_COLUMN_INDEX);
555 return [NSNumber numberWithInt:columnIndex];
556 } else if ([self internalRole] == WebKit::WebAXRoleRow) {
557 int rowIndex = browserAccessibility_->GetIntAttribute(
558 AccessibilityNodeData::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 {
573 if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
575 NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
576 if ([invalid isEqualToString:@"false"] ||
577 [invalid isEqualToString:@""]) {
583 - (NSNumber*)loaded {
584 return [NSNumber numberWithBool:YES];
587 - (NSNumber*)loadingProgress {
588 float floatValue = browserAccessibility_->GetFloatAttribute(
589 AccessibilityNodeData::ATTR_DOC_LOADING_PROGRESS);
590 return [NSNumber numberWithFloat:floatValue];
593 - (NSNumber*)maxValue {
594 float floatValue = browserAccessibility_->GetFloatAttribute(
595 AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE);
596 return [NSNumber numberWithFloat:floatValue];
599 - (NSNumber*)minValue {
600 float floatValue = browserAccessibility_->GetFloatAttribute(
601 AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE);
602 return [NSNumber numberWithFloat:floatValue];
605 - (NSString*)orientation {
606 // We present a spin button as a vertical slider, with a role description
608 if ([self internalRole] == WebKit::WebAXRoleSpinButton)
609 return NSAccessibilityVerticalOrientationValue;
611 if (GetState(browserAccessibility_, WebKit::WebAXStateVertical))
612 return NSAccessibilityVerticalOrientationValue;
614 return NSAccessibilityHorizontalOrientationValue;
617 - (NSNumber*)numberOfCharacters {
618 return [NSNumber numberWithInt:browserAccessibility_->value().length()];
621 // The origin of this accessibility object in the page's document.
622 // This is relative to webkit's top-left origin, not Cocoa's
623 // bottom-left origin.
625 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
626 return NSMakePoint(bounds.x(), bounds.y());
630 // A nil parent means we're the root.
631 if (browserAccessibility_->parent()) {
632 return NSAccessibilityUnignoredAncestor(
633 browserAccessibility_->parent()->ToBrowserAccessibilityCocoa());
635 // Hook back up to RenderWidgetHostViewCocoa.
636 BrowserAccessibilityManagerMac* manager =
637 static_cast<BrowserAccessibilityManagerMac*>(
638 browserAccessibility_->manager());
639 return manager->parent_view();
643 - (NSValue*)position {
644 return [NSValue valueWithPoint:[delegate_ accessibilityPointInScreen:self]];
647 - (NSNumber*)required {
648 return [NSNumber numberWithBool:
649 GetState(browserAccessibility_, WebKit::WebAXStateRequired)];
652 // Returns an enum indicating the role from browserAccessibility_.
653 - (WebKit::WebAXRole)internalRole {
654 return static_cast<WebKit::WebAXRole>(browserAccessibility_->role());
657 // Returns a string indicating the NSAccessibility role of this object.
659 WebKit::WebAXRole role = [self internalRole];
660 if (role == WebKit::WebAXRoleCanvas &&
661 browserAccessibility_->GetBoolAttribute(
662 AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) {
663 return NSAccessibilityGroupRole;
665 return NativeRoleFromAccessibilityNodeDataRole(role);
668 // Returns a string indicating the role description of this object.
669 - (NSString*)roleDescription {
670 NSString* role = [self role];
672 ContentClient* content_client = content::GetContentClient();
674 // The following descriptions are specific to webkit.
675 if ([role isEqualToString:@"AXWebArea"]) {
676 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
677 IDS_AX_ROLE_WEB_AREA));
680 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
681 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
685 if ([role isEqualToString:@"AXHeading"]) {
686 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
687 IDS_AX_ROLE_HEADING));
690 if ([role isEqualToString:NSAccessibilityGroupRole] ||
691 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
693 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
694 WebKit::WebAXRole internalRole = [self internalRole];
695 if ((internalRole != WebKit::WebAXRoleGroup &&
696 internalRole != WebKit::WebAXRoleListItem) ||
697 internalRole == WebKit::WebAXRoleTab) {
698 // TODO(dtseng): This is not localized; see crbug/84814.
699 return base::SysUTF8ToNSString(role);
704 switch([self internalRole]) {
705 case WebKit::WebAXRoleFooter:
706 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
707 IDS_AX_ROLE_FOOTER));
708 case WebKit::WebAXRoleSpinButton:
709 // This control is similar to what VoiceOver calls a "stepper".
710 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
711 IDS_AX_ROLE_STEPPER));
716 return NSAccessibilityRoleDescription(role, nil);
719 - (NSArray*)rowHeaders {
720 if ([self internalRole] != WebKit::WebAXRoleTable &&
721 [self internalRole] != WebKit::WebAXRoleGrid) {
725 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
726 const std::vector<int32>& uniqueCellIds =
727 browserAccessibility_->GetIntListAttribute(
728 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
729 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
730 int id = uniqueCellIds[i];
731 BrowserAccessibility* cell =
732 browserAccessibility_->manager()->GetFromRendererID(id);
733 if (cell && cell->role() == WebKit::WebAXRoleRowHeader)
734 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
739 - (NSValue*)rowIndexRange {
740 if ([self internalRole] != WebKit::WebAXRoleCell)
745 browserAccessibility_->GetIntAttribute(
746 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
747 browserAccessibility_->GetIntAttribute(
748 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
749 if (row >= 0 && rowspan >= 1)
750 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
755 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
757 if ([self internalRole] == WebKit::WebAXRoleTable||
758 [self internalRole] == WebKit::WebAXRoleGrid) {
759 for (BrowserAccessibilityCocoa* child in [self children]) {
760 if ([[child role] isEqualToString:NSAccessibilityRowRole])
761 [ret addObject:child];
763 } else if ([self internalRole] == WebKit::WebAXRoleColumn) {
764 const std::vector<int32>& indirectChildIds =
765 browserAccessibility_->GetIntListAttribute(
766 AccessibilityNodeData::ATTR_INDIRECT_CHILD_IDS);
767 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
768 int id = indirectChildIds[i];
769 BrowserAccessibility* rowElement =
770 browserAccessibility_->manager()->GetFromRendererID(id);
772 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
779 // Returns the size of this object.
781 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
782 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
785 // Returns a subrole based upon the role.
786 - (NSString*) subrole {
787 WebKit::WebAXRole browserAccessibilityRole = [self internalRole];
788 if (browserAccessibilityRole == WebKit::WebAXRoleTextField &&
789 GetState(browserAccessibility_, WebKit::WebAXStateProtected)) {
790 return @"AXSecureTextField";
793 NSString* htmlTag = NSStringForStringAttribute(
794 browserAccessibility_, AccessibilityNodeData::ATTR_HTML_TAG);
796 if (browserAccessibilityRole == WebKit::WebAXRoleList) {
797 if ([htmlTag isEqualToString:@"ul"] ||
798 [htmlTag isEqualToString:@"ol"]) {
799 return @"AXContentList";
800 } else if ([htmlTag isEqualToString:@"dl"]) {
801 return @"AXDescriptionList";
805 return NativeSubroleFromAccessibilityNodeDataRole(browserAccessibilityRole);
808 // Returns all tabs in this subtree.
810 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
812 if ([self internalRole] == WebKit::WebAXRoleTab)
813 [tabSubtree addObject:self];
815 for (uint i=0; i < [[self children] count]; ++i) {
816 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
817 if ([tabChildren count] > 0)
818 [tabSubtree addObjectsFromArray:tabChildren];
825 return NSStringForStringAttribute(
826 browserAccessibility_, AccessibilityNodeData::ATTR_NAME);
829 - (id)titleUIElement {
831 if (browserAccessibility_->GetIntAttribute(
832 AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
833 BrowserAccessibility* titleElement =
834 browserAccessibility_->manager()->GetFromRendererID(titleElementId);
836 return titleElement->ToBrowserAccessibilityCocoa();
842 StringAttribute urlAttribute =
843 [[self role] isEqualToString:@"AXWebArea"] ?
844 AccessibilityNodeData::ATTR_DOC_URL :
845 AccessibilityNodeData::ATTR_URL;
846 return NSStringForStringAttribute(browserAccessibility_, urlAttribute);
850 // WebCore uses an attachmentView to get the below behavior.
851 // We do not have any native views backing this object, so need
852 // to approximate Cocoa ax behavior best as we can.
853 NSString* role = [self role];
854 if ([role isEqualToString:@"AXHeading"]) {
856 if (browserAccessibility_->GetIntAttribute(
857 AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, &level)) {
858 return [NSNumber numberWithInt:level];
860 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
861 // AXValue does not make sense for pure buttons.
863 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
864 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
867 browserAccessibility_, WebKit::WebAXStateChecked) ? 1 : 0;
869 browserAccessibility_, WebKit::WebAXStateSelected) ?
873 if (browserAccessibility_->GetBoolAttribute(
874 AccessibilityNodeData::ATTR_BUTTON_MIXED)) {
877 return [NSNumber numberWithInt:value];
878 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
879 [role isEqualToString:NSAccessibilitySliderRole] ||
880 [role isEqualToString:NSAccessibilityScrollBarRole]) {
882 if (browserAccessibility_->GetFloatAttribute(
883 AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &floatValue)) {
884 return [NSNumber numberWithFloat:floatValue];
886 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
887 int r = browserAccessibility_->GetIntAttribute(
888 AccessibilityNodeData::ATTR_COLOR_VALUE_RED);
889 int g = browserAccessibility_->GetIntAttribute(
890 AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN);
891 int b = browserAccessibility_->GetIntAttribute(
892 AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE);
893 // This string matches the one returned by a native Mac color well.
894 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
895 r / 255., g / 255., b / 255.];
898 return NSStringForStringAttribute(
899 browserAccessibility_, AccessibilityNodeData::ATTR_VALUE);
902 - (NSString*)valueDescription {
903 return NSStringForStringAttribute(
904 browserAccessibility_, AccessibilityNodeData::ATTR_VALUE);
907 - (NSValue*)visibleCharacterRange {
908 return [NSValue valueWithRange:
909 NSMakeRange(0, browserAccessibility_->value().length())];
912 - (NSArray*)visibleCells {
913 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
914 const std::vector<int32>& uniqueCellIds =
915 browserAccessibility_->GetIntListAttribute(
916 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
917 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
918 int id = uniqueCellIds[i];
919 BrowserAccessibility* cell =
920 browserAccessibility_->manager()->GetFromRendererID(id);
922 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
927 - (NSArray*)visibleColumns {
928 return [self columns];
931 - (NSArray*)visibleRows {
935 - (NSNumber*)visited {
936 return [NSNumber numberWithBool:
937 GetState(browserAccessibility_, WebKit::WebAXStateVisited)];
941 return [delegate_ window];
944 - (NSString*)methodNameForAttribute:(NSString*)attribute {
945 return [attributeToMethodNameMap objectForKey:attribute];
948 // Returns the accessibility value for the given attribute. If the value isn't
949 // supported this will return nil.
950 - (id)accessibilityAttributeValue:(NSString*)attribute {
951 if (!browserAccessibility_)
955 NSSelectorFromString([self methodNameForAttribute:attribute]);
957 return [self performSelector:selector];
959 // TODO(dtseng): refactor remaining attributes.
960 int selStart, selEnd;
961 if (browserAccessibility_->GetIntAttribute(
962 AccessibilityNodeData::ATTR_TEXT_SEL_START, &selStart) &&
963 browserAccessibility_->
964 GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &selEnd)) {
965 if (selStart > selEnd)
966 std::swap(selStart, selEnd);
967 int selLength = selEnd - selStart;
968 if ([attribute isEqualToString:
969 NSAccessibilityInsertionPointLineNumberAttribute]) {
970 const std::vector<int32>& line_breaks =
971 browserAccessibility_->GetIntListAttribute(
972 AccessibilityNodeData::ATTR_LINE_BREAKS);
973 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
974 if (line_breaks[i] > selStart)
975 return [NSNumber numberWithInt:i];
977 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
979 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
980 std::string value = browserAccessibility_->GetStringAttribute(
981 AccessibilityNodeData::ATTR_VALUE);
982 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
984 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
985 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
991 // Returns the accessibility value for the given attribute and parameter. If the
992 // value isn't supported this will return nil.
993 - (id)accessibilityAttributeValue:(NSString*)attribute
994 forParameter:(id)parameter {
995 if (!browserAccessibility_)
998 const std::vector<int32>& line_breaks =
999 browserAccessibility_->GetIntListAttribute(
1000 AccessibilityNodeData::ATTR_LINE_BREAKS);
1001 int len = static_cast<int>(browserAccessibility_->value().size());
1003 if ([attribute isEqualToString:
1004 NSAccessibilityStringForRangeParameterizedAttribute]) {
1005 NSRange range = [(NSValue*)parameter rangeValue];
1006 std::string value = browserAccessibility_->GetStringAttribute(
1007 AccessibilityNodeData::ATTR_VALUE);
1008 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1011 if ([attribute isEqualToString:
1012 NSAccessibilityLineForIndexParameterizedAttribute]) {
1013 int index = [(NSNumber*)parameter intValue];
1014 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1015 if (line_breaks[i] > index)
1016 return [NSNumber numberWithInt:i];
1018 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1021 if ([attribute isEqualToString:
1022 NSAccessibilityRangeForLineParameterizedAttribute]) {
1023 int line_index = [(NSNumber*)parameter intValue];
1024 int line_count = static_cast<int>(line_breaks.size()) + 1;
1025 if (line_index < 0 || line_index >= line_count)
1027 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1028 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1029 return [NSValue valueWithRange:
1030 NSMakeRange(start, end - start)];
1033 if ([attribute isEqualToString:
1034 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1035 if ([self internalRole] != WebKit::WebAXRoleTable &&
1036 [self internalRole] != WebKit::WebAXRoleGrid) {
1039 if (![parameter isKindOfClass:[NSArray self]])
1041 NSArray* array = parameter;
1042 int column = [[array objectAtIndex:0] intValue];
1043 int row = [[array objectAtIndex:1] intValue];
1044 int num_columns = browserAccessibility_->GetIntAttribute(
1045 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT);
1046 int num_rows = browserAccessibility_->GetIntAttribute(
1047 AccessibilityNodeData::ATTR_TABLE_ROW_COUNT);
1048 if (column < 0 || column >= num_columns ||
1049 row < 0 || row >= num_rows) {
1053 i < browserAccessibility_->PlatformChildCount();
1055 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1056 if (child->role() != WebKit::WebAXRoleRow)
1059 if (!child->GetIntAttribute(
1060 AccessibilityNodeData::ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1068 j < child->PlatformChildCount();
1070 BrowserAccessibility* cell = child->PlatformGetChild(j);
1071 if (cell->role() != WebKit::WebAXRoleCell)
1074 if (!cell->GetIntAttribute(
1075 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX,
1079 if (colIndex == column)
1080 return cell->ToBrowserAccessibilityCocoa();
1081 if (colIndex > column)
1088 // TODO(dtseng): support the following attributes.
1089 if ([attribute isEqualTo:
1090 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1091 [attribute isEqualTo:
1092 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1093 [attribute isEqualTo:
1094 NSAccessibilityBoundsForRangeParameterizedAttribute] ||
1095 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1096 [attribute isEqualTo:
1097 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1103 // Returns an array of parameterized attributes names that this object will
1105 - (NSArray*)accessibilityParameterizedAttributeNames {
1106 if (!browserAccessibility_)
1109 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1110 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1111 return [NSArray arrayWithObjects:
1112 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1115 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1116 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1117 return [NSArray arrayWithObjects:
1118 NSAccessibilityLineForIndexParameterizedAttribute,
1119 NSAccessibilityRangeForLineParameterizedAttribute,
1120 NSAccessibilityStringForRangeParameterizedAttribute,
1121 NSAccessibilityRangeForPositionParameterizedAttribute,
1122 NSAccessibilityRangeForIndexParameterizedAttribute,
1123 NSAccessibilityBoundsForRangeParameterizedAttribute,
1124 NSAccessibilityRTFForRangeParameterizedAttribute,
1125 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1126 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1132 // Returns an array of action names that this object will respond to.
1133 - (NSArray*)accessibilityActionNames {
1134 if (!browserAccessibility_)
1137 NSMutableArray* ret =
1138 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1139 NSString* role = [self role];
1140 // TODO(dtseng): this should only get set when there's a default action.
1141 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1142 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1143 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1144 [ret addObject:NSAccessibilityPressAction];
1150 // Returns a sub-array of values for the given attribute value, starting at
1151 // index, with up to maxCount items. If the given index is out of bounds,
1152 // or there are no values for the given attribute, it will return nil.
1153 // This method is used for querying subsets of values, without having to
1154 // return a large set of data, such as elements with a large number of
1156 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1157 index:(NSUInteger)index
1158 maxCount:(NSUInteger)maxCount {
1159 if (!browserAccessibility_)
1162 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1165 NSUInteger arrayCount = [fullArray count];
1166 if (index >= arrayCount)
1169 if ((index + maxCount) > arrayCount) {
1170 subRange = NSMakeRange(index, arrayCount - index);
1172 subRange = NSMakeRange(index, maxCount);
1174 return [fullArray subarrayWithRange:subRange];
1177 // Returns the count of the specified accessibility array attribute.
1178 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1179 if (!browserAccessibility_)
1182 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1183 return [fullArray count];
1186 // Returns the list of accessibility attributes that this object supports.
1187 - (NSArray*)accessibilityAttributeNames {
1188 if (!browserAccessibility_)
1191 // General attributes.
1192 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1193 NSAccessibilityChildrenAttribute,
1194 NSAccessibilityDescriptionAttribute,
1195 NSAccessibilityEnabledAttribute,
1196 NSAccessibilityFocusedAttribute,
1197 NSAccessibilityHelpAttribute,
1198 NSAccessibilityParentAttribute,
1199 NSAccessibilityPositionAttribute,
1200 NSAccessibilityRoleAttribute,
1201 NSAccessibilityRoleDescriptionAttribute,
1202 NSAccessibilitySizeAttribute,
1203 NSAccessibilitySubroleAttribute,
1204 NSAccessibilityTitleAttribute,
1205 NSAccessibilityTopLevelUIElementAttribute,
1206 NSAccessibilityValueAttribute,
1207 NSAccessibilityWindowAttribute,
1208 NSAccessibilityURLAttribute,
1215 // Specific role attributes.
1216 NSString* role = [self role];
1217 NSString* subrole = [self subrole];
1218 if ([role isEqualToString:NSAccessibilityTableRole] ||
1219 [role isEqualToString:NSAccessibilityGridRole]) {
1220 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1221 NSAccessibilityColumnsAttribute,
1222 NSAccessibilityVisibleColumnsAttribute,
1223 NSAccessibilityRowsAttribute,
1224 NSAccessibilityVisibleRowsAttribute,
1225 NSAccessibilityVisibleCellsAttribute,
1226 NSAccessibilityHeaderAttribute,
1227 NSAccessibilityColumnHeaderUIElementsAttribute,
1228 NSAccessibilityRowHeaderUIElementsAttribute,
1230 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1231 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1232 NSAccessibilityIndexAttribute,
1233 NSAccessibilityHeaderAttribute,
1234 NSAccessibilityRowsAttribute,
1235 NSAccessibilityVisibleRowsAttribute,
1237 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1238 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1239 NSAccessibilityColumnIndexRangeAttribute,
1240 NSAccessibilityRowIndexRangeAttribute,
1242 } else if ([role isEqualToString:@"AXWebArea"]) {
1243 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1245 @"AXLoadingProgress",
1247 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1248 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1249 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1250 NSAccessibilityInsertionPointLineNumberAttribute,
1251 NSAccessibilityNumberOfCharactersAttribute,
1252 NSAccessibilitySelectedTextAttribute,
1253 NSAccessibilitySelectedTextRangeAttribute,
1254 NSAccessibilityVisibleCharacterRangeAttribute,
1256 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1257 [ret addObject:NSAccessibilityTabsAttribute];
1258 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1259 [role isEqualToString:NSAccessibilitySliderRole] ||
1260 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1261 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1262 NSAccessibilityMaxValueAttribute,
1263 NSAccessibilityMinValueAttribute,
1264 NSAccessibilityOrientationAttribute,
1265 NSAccessibilityValueDescriptionAttribute,
1267 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1268 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1269 NSAccessibilityDisclosingAttribute,
1270 NSAccessibilityDisclosedByRowAttribute,
1271 NSAccessibilityDisclosureLevelAttribute,
1272 NSAccessibilityDisclosedRowsAttribute,
1274 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1275 if (browserAccessibility_->parent()) {
1276 string16 parentRole;
1277 browserAccessibility_->parent()->GetHtmlAttribute(
1278 "role", &parentRole);
1279 const string16 treegridRole(ASCIIToUTF16("treegrid"));
1280 if (parentRole == treegridRole) {
1281 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1282 NSAccessibilityDisclosingAttribute,
1283 NSAccessibilityDisclosedByRowAttribute,
1284 NSAccessibilityDisclosureLevelAttribute,
1285 NSAccessibilityDisclosedRowsAttribute,
1288 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1289 NSAccessibilityIndexAttribute,
1296 if (browserAccessibility_->HasStringAttribute(
1297 AccessibilityNodeData::ATTR_LIVE_STATUS)) {
1298 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1303 if (browserAccessibility_->HasStringAttribute(
1304 AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS)) {
1305 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1311 // Title UI Element.
1312 if (browserAccessibility_->HasIntAttribute(
1313 AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT)) {
1314 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1315 NSAccessibilityTitleUIElementAttribute,
1322 // Returns the index of the child in this objects array of children.
1323 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1324 if (!browserAccessibility_)
1327 NSUInteger index = 0;
1328 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1329 if ([child isEqual:childToCheck])
1336 // Returns whether or not the specified attribute can be set by the
1337 // accessibility API via |accessibilitySetValue:forAttribute:|.
1338 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1339 if (!browserAccessibility_)
1342 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1343 return GetState(browserAccessibility_,
1344 WebKit::WebAXStateFocusable);
1345 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1346 return browserAccessibility_->GetBoolAttribute(
1347 AccessibilityNodeData::ATTR_CAN_SET_VALUE);
1349 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1350 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1351 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1357 // Returns whether or not this object should be ignored in the accessibilty
1359 - (BOOL)accessibilityIsIgnored {
1360 if (!browserAccessibility_)
1363 return [self isIgnored];
1366 // Performs the given accessibilty action on the webkit accessibility object
1367 // that backs this object.
1368 - (void)accessibilityPerformAction:(NSString*)action {
1369 if (!browserAccessibility_)
1372 // TODO(feldstein): Support more actions.
1373 if ([action isEqualToString:NSAccessibilityPressAction])
1374 [delegate_ doDefaultAction:browserAccessibility_->renderer_id()];
1375 else if ([action isEqualToString:NSAccessibilityShowMenuAction])
1376 [delegate_ performShowMenuAction:self];
1379 // Returns the description of the given action.
1380 - (NSString*)accessibilityActionDescription:(NSString*)action {
1381 if (!browserAccessibility_)
1384 return NSAccessibilityActionDescription(action);
1387 // Sets an override value for a specific accessibility attribute.
1388 // This class does not support this.
1389 - (BOOL)accessibilitySetOverrideValue:(id)value
1390 forAttribute:(NSString*)attribute {
1394 // Sets the value for an accessibility attribute via the accessibility API.
1395 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1396 if (!browserAccessibility_)
1399 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1400 NSNumber* focusedNumber = value;
1401 BOOL focused = [focusedNumber intValue];
1402 [delegate_ setAccessibilityFocus:focused
1403 accessibilityId:browserAccessibility_->renderer_id()];
1405 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1406 NSRange range = [(NSValue*)value rangeValue];
1408 accessibilitySetTextSelection:browserAccessibility_->renderer_id()
1409 startOffset:range.location
1410 endOffset:range.location + range.length];
1414 // Returns the deepest accessibility child that should not be ignored.
1415 // It is assumed that the hit test has been narrowed down to this object
1416 // or one of its children, so this will never return nil unless this
1417 // object is invalid.
1418 - (id)accessibilityHitTest:(NSPoint)point {
1419 if (!browserAccessibility_)
1422 BrowserAccessibilityCocoa* hit = self;
1423 for (BrowserAccessibilityCocoa* child in [self children]) {
1424 NSPoint origin = [child origin];
1425 NSSize size = [[child size] sizeValue];
1427 rect.origin = origin;
1429 if (NSPointInRect(point, rect)) {
1431 id childResult = [child accessibilityHitTest:point];
1432 if (![childResult accessibilityIsIgnored]) {
1438 return NSAccessibilityUnignoredAncestor(hit);
1441 - (BOOL)isEqual:(id)object {
1442 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1444 return ([self hash] == [object hash]);
1447 - (NSUInteger)hash {
1448 // Potentially called during dealloc.
1449 if (!browserAccessibility_)
1450 return [super hash];
1451 return browserAccessibility_->renderer_id();
1454 - (BOOL)accessibilityShouldUseUniqueId {