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/app/strings/grit/content_strings.h"
16 #include "content/browser/accessibility/browser_accessibility_manager.h"
17 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
18 #include "content/public/common/content_client.h"
19 #import "ui/accessibility/platform/ax_platform_node_mac.h"
21 // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
22 // 10.6, and 10.7. It allows accessibility clients to observe events posted on
24 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
27 using content::BrowserAccessibility;
28 using content::BrowserAccessibilityDelegate;
29 using content::BrowserAccessibilityManager;
30 using content::BrowserAccessibilityManagerMac;
31 using content::ContentClient;
32 typedef ui::AXStringAttribute StringAttribute;
36 // Returns an autoreleased copy of the AXNodeData's attribute.
37 NSString* NSStringForStringAttribute(
38 BrowserAccessibility* browserAccessibility,
39 StringAttribute attribute) {
40 return base::SysUTF8ToNSString(
41 browserAccessibility->GetStringAttribute(attribute));
44 // GetState checks the bitmask used in AXNodeData to check
45 // if the given state was set on the accessibility object.
46 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
47 return ((accessibility->GetState() >> state) & 1);
50 // A mapping from an accessibility attribute to its method name.
51 NSDictionary* attributeToMethodNameMap = nil;
55 @implementation BrowserAccessibilityCocoa
61 } attributeToMethodNameContainer[] = {
62 { NSAccessibilityChildrenAttribute, @"children" },
63 { NSAccessibilityColumnsAttribute, @"columns" },
64 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
65 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
66 { NSAccessibilityContentsAttribute, @"contents" },
67 { NSAccessibilityDescriptionAttribute, @"description" },
68 { NSAccessibilityDisclosingAttribute, @"disclosing" },
69 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
70 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
71 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
72 { NSAccessibilityEnabledAttribute, @"enabled" },
73 { NSAccessibilityExpandedAttribute, @"expanded" },
74 { NSAccessibilityFocusedAttribute, @"focused" },
75 { NSAccessibilityHeaderAttribute, @"header" },
76 { NSAccessibilityHelpAttribute, @"help" },
77 { NSAccessibilityIndexAttribute, @"index" },
78 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
79 { NSAccessibilityMaxValueAttribute, @"maxValue" },
80 { NSAccessibilityMinValueAttribute, @"minValue" },
81 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
82 { NSAccessibilityOrientationAttribute, @"orientation" },
83 { NSAccessibilityParentAttribute, @"parent" },
84 { NSAccessibilityPositionAttribute, @"position" },
85 { NSAccessibilityRoleAttribute, @"role" },
86 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
87 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
88 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
89 { NSAccessibilityRowsAttribute, @"rows" },
90 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
91 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
92 { NSAccessibilitySizeAttribute, @"size" },
93 { NSAccessibilitySubroleAttribute, @"subrole" },
94 { NSAccessibilityTabsAttribute, @"tabs" },
95 { NSAccessibilityTitleAttribute, @"title" },
96 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
97 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
98 { NSAccessibilityURLAttribute, @"url" },
99 { NSAccessibilityValueAttribute, @"value" },
100 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
101 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
102 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
103 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
104 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
105 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
106 { NSAccessibilityWindowAttribute, @"window" },
107 { @"AXAccessKey", @"accessKey" },
108 { @"AXARIAAtomic", @"ariaAtomic" },
109 { @"AXARIABusy", @"ariaBusy" },
110 { @"AXARIALive", @"ariaLive" },
111 { @"AXARIARelevant", @"ariaRelevant" },
112 { @"AXGrabbed", @"grabbed" },
113 { @"AXInvalid", @"invalid" },
114 { @"AXLoaded", @"loaded" },
115 { @"AXLoadingProgress", @"loadingProgress" },
116 { @"AXPlaceholder", @"placeholder" },
117 { @"AXRequired", @"required" },
118 { @"AXVisited", @"visited" },
121 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
122 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
123 sizeof(attributeToMethodNameContainer[0]);
124 for (size_t i = 0; i < numAttributes; ++i) {
125 [dict setObject:attributeToMethodNameContainer[i].methodName
126 forKey:attributeToMethodNameContainer[i].attribute];
128 attributeToMethodNameMap = dict;
132 - (id)initWithObject:(BrowserAccessibility*)accessibility {
133 if ((self = [super init]))
134 browserAccessibility_ = accessibility;
139 if (browserAccessibility_) {
140 NSAccessibilityUnregisterUniqueIdForUIElement(self);
141 browserAccessibility_ = NULL;
145 - (NSString*)accessKey {
146 return NSStringForStringAttribute(
147 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
150 - (NSNumber*)ariaAtomic {
151 bool boolValue = browserAccessibility_->GetBoolAttribute(
152 ui::AX_ATTR_LIVE_ATOMIC);
153 return [NSNumber numberWithBool:boolValue];
156 - (NSNumber*)ariaBusy {
157 return [NSNumber numberWithBool:
158 GetState(browserAccessibility_, ui::AX_STATE_BUSY)];
161 - (NSString*)ariaLive {
162 return NSStringForStringAttribute(
163 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
166 - (NSString*)ariaRelevant {
167 return NSStringForStringAttribute(
168 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
171 // Returns an array of BrowserAccessibilityCocoa objects, representing the
172 // accessibility children of this object.
173 - (NSArray*)children {
175 uint32 childCount = browserAccessibility_->PlatformChildCount();
176 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
177 for (uint32 index = 0; index < childCount; ++index) {
178 BrowserAccessibilityCocoa* child =
179 browserAccessibility_->PlatformGetChild(index)->
180 ToBrowserAccessibilityCocoa();
181 if ([child isIgnored])
182 [children_ addObjectsFromArray:[child children]];
184 [children_ addObject:child];
187 // Also, add indirect children (if any).
188 const std::vector<int32>& indirectChildIds =
189 browserAccessibility_->GetIntListAttribute(
190 ui::AX_ATTR_INDIRECT_CHILD_IDS);
191 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
192 int32 child_id = indirectChildIds[i];
193 BrowserAccessibility* child =
194 browserAccessibility_->manager()->GetFromID(child_id);
196 // This only became necessary as a result of crbug.com/93095. It should be
197 // a DCHECK in the future.
199 BrowserAccessibilityCocoa* child_cocoa =
200 child->ToBrowserAccessibilityCocoa();
201 [children_ addObject:child_cocoa];
208 - (void)childrenChanged {
209 if (![self isIgnored]) {
212 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
217 - (NSArray*)columnHeaders {
218 if ([self internalRole] != ui::AX_ROLE_TABLE &&
219 [self internalRole] != ui::AX_ROLE_GRID) {
223 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
224 const std::vector<int32>& uniqueCellIds =
225 browserAccessibility_->GetIntListAttribute(
226 ui::AX_ATTR_UNIQUE_CELL_IDS);
227 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
228 int id = uniqueCellIds[i];
229 BrowserAccessibility* cell =
230 browserAccessibility_->manager()->GetFromID(id);
231 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
232 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
237 - (NSValue*)columnIndexRange {
238 if (!browserAccessibility_->IsCellOrTableHeaderRole())
243 browserAccessibility_->GetIntAttribute(
244 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
245 browserAccessibility_->GetIntAttribute(
246 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
247 if (column >= 0 && colspan >= 1)
248 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
252 - (NSArray*)columns {
253 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
254 for (BrowserAccessibilityCocoa* child in [self children]) {
255 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
256 [ret addObject:child];
261 - (NSString*)description {
262 std::string description;
263 if (browserAccessibility_->GetStringAttribute(
264 ui::AX_ATTR_DESCRIPTION, &description)) {
265 return base::SysUTF8ToNSString(description);
268 // If the role is anything other than an image, or if there's
269 // a title or title UI element, just return an empty string.
270 if (![[self role] isEqualToString:NSAccessibilityImageRole])
272 if (browserAccessibility_->HasStringAttribute(
276 if ([self titleUIElement])
279 // The remaining case is an image where there's no other title.
280 // Return the base part of the filename as the description.
282 if (browserAccessibility_->GetStringAttribute(
283 ui::AX_ATTR_URL, &url)) {
284 // Given a url like http://foo.com/bar/baz.png, just return the
285 // base name, e.g., "baz.png".
286 size_t leftIndex = url.rfind('/');
287 std::string basename =
288 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
289 return base::SysUTF8ToNSString(basename);
295 - (NSNumber*)disclosing {
296 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
297 return [NSNumber numberWithBool:
298 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
304 - (id)disclosedByRow {
305 // The row that contains this row.
306 // It should be the same as the first parent that is a treeitem.
310 - (NSNumber*)disclosureLevel {
311 ui::AXRole role = [self internalRole];
312 if (role == ui::AX_ROLE_ROW ||
313 role == ui::AX_ROLE_TREE_ITEM) {
314 int level = browserAccessibility_->GetIntAttribute(
315 ui::AX_ATTR_HIERARCHICAL_LEVEL);
316 // Mac disclosureLevel is 0-based, but web levels are 1-based.
319 return [NSNumber numberWithInt:level];
325 - (id)disclosedRows {
326 // The rows that are considered inside this row.
330 - (NSNumber*)enabled {
331 return [NSNumber numberWithBool:
332 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
335 - (NSNumber*)expanded {
336 return [NSNumber numberWithBool:
337 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
340 - (NSNumber*)focused {
341 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
342 NSNumber* ret = [NSNumber numberWithBool:
343 manager->GetFocus(NULL) == browserAccessibility_];
347 - (NSNumber*)grabbed {
348 bool boolValue = browserAccessibility_->GetBoolAttribute(ui::AX_ATTR_GRABBED);
349 return [NSNumber numberWithBool:boolValue];
353 int headerElementId = -1;
354 if ([self internalRole] == ui::AX_ROLE_TABLE ||
355 [self internalRole] == ui::AX_ROLE_GRID) {
356 browserAccessibility_->GetIntAttribute(
357 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
358 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
359 browserAccessibility_->GetIntAttribute(
360 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
361 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
362 browserAccessibility_->GetIntAttribute(
363 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
366 if (headerElementId > 0) {
367 BrowserAccessibility* headerObject =
368 browserAccessibility_->manager()->GetFromID(headerElementId);
370 return headerObject->ToBrowserAccessibilityCocoa();
376 return NSStringForStringAttribute(
377 browserAccessibility_, ui::AX_ATTR_HELP);
381 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
382 int columnIndex = browserAccessibility_->GetIntAttribute(
383 ui::AX_ATTR_TABLE_COLUMN_INDEX);
384 return [NSNumber numberWithInt:columnIndex];
385 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
386 int rowIndex = browserAccessibility_->GetIntAttribute(
387 ui::AX_ATTR_TABLE_ROW_INDEX);
388 return [NSNumber numberWithInt:rowIndex];
394 // Returns whether or not this node should be ignored in the
395 // accessibility tree.
397 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
400 - (NSString*)invalid {
402 if (!browserAccessibility_->GetIntAttribute(
403 ui::AX_ATTR_INVALID_STATE, &invalidState))
406 switch (invalidState) {
407 case ui::AX_INVALID_STATE_FALSE:
409 case ui::AX_INVALID_STATE_TRUE:
411 case ui::AX_INVALID_STATE_SPELLING:
413 case ui::AX_INVALID_STATE_GRAMMAR:
415 case ui::AX_INVALID_STATE_OTHER:
417 std::string ariaInvalidValue;
418 if (browserAccessibility_->GetStringAttribute(
419 ui::AX_ATTR_ARIA_INVALID_VALUE,
421 return base::SysUTF8ToNSString(ariaInvalidValue);
422 // Return @"true" since we cannot be more specific about the value.
432 - (NSString*)placeholder {
433 return NSStringForStringAttribute(
434 browserAccessibility_, ui::AX_ATTR_PLACEHOLDER);
437 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
438 addTo:(NSMutableArray*)outArray {
439 const std::vector<int32>& attributeValues =
440 browserAccessibility_->GetIntListAttribute(attribute);
441 for (size_t i = 0; i < attributeValues.size(); ++i) {
442 BrowserAccessibility* element =
443 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
445 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
449 - (NSArray*)linkedUIElements {
450 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
451 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
452 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
453 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
454 if ([ret count] == 0)
459 - (NSNumber*)loaded {
460 return [NSNumber numberWithBool:YES];
463 - (NSNumber*)loadingProgress {
464 float floatValue = browserAccessibility_->GetFloatAttribute(
465 ui::AX_ATTR_DOC_LOADING_PROGRESS);
466 return [NSNumber numberWithFloat:floatValue];
469 - (NSNumber*)maxValue {
470 float floatValue = browserAccessibility_->GetFloatAttribute(
471 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
472 return [NSNumber numberWithFloat:floatValue];
475 - (NSNumber*)minValue {
476 float floatValue = browserAccessibility_->GetFloatAttribute(
477 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
478 return [NSNumber numberWithFloat:floatValue];
481 - (NSString*)orientation {
482 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
483 return NSAccessibilityVerticalOrientationValue;
484 else if (GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL))
485 return NSAccessibilityHorizontalOrientationValue;
490 - (NSNumber*)numberOfCharacters {
491 std::string value = browserAccessibility_->GetStringAttribute(
493 return [NSNumber numberWithInt:value.size()];
496 // The origin of this accessibility object in the page's document.
497 // This is relative to webkit's top-left origin, not Cocoa's
498 // bottom-left origin.
500 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
501 return NSMakePoint(bounds.x(), bounds.y());
505 // A nil parent means we're the root.
506 if (browserAccessibility_->GetParent()) {
507 return NSAccessibilityUnignoredAncestor(
508 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
510 // Hook back up to RenderWidgetHostViewCocoa.
511 BrowserAccessibilityManagerMac* manager =
512 static_cast<BrowserAccessibilityManagerMac*>(
513 browserAccessibility_->manager());
514 return manager->parent_view();
518 - (NSValue*)position {
519 NSPoint origin = [self origin];
520 NSSize size = [[self size] sizeValue];
521 NSPoint pointInScreen = [self pointInScreen:origin size:size];
522 return [NSValue valueWithPoint:pointInScreen];
525 - (NSNumber*)required {
526 return [NSNumber numberWithBool:
527 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
530 // Returns an enum indicating the role from browserAccessibility_.
531 - (ui::AXRole)internalRole {
532 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
535 - (content::BrowserAccessibilityDelegate*)delegate {
536 return browserAccessibility_->manager() ?
537 browserAccessibility_->manager()->delegate() :
541 - (NSPoint)pointInScreen:(NSPoint)origin
543 if (!browserAccessibility_)
546 // Get the delegate for the topmost BrowserAccessibilityManager, because
547 // that's the only one that can convert points to their origin in the screen.
548 BrowserAccessibilityDelegate* delegate =
549 browserAccessibility_->manager()->GetDelegateFromRootManager();
551 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
552 gfx::Point point = delegate->AccessibilityOriginInScreen(bounds);
553 return NSMakePoint(point.x(), point.y());
559 // Returns a string indicating the NSAccessibility role of this object.
561 ui::AXRole role = [self internalRole];
562 if (role == ui::AX_ROLE_CANVAS &&
563 browserAccessibility_->GetBoolAttribute(
564 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
565 return NSAccessibilityGroupRole;
567 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
568 bool isAriaPressedDefined;
570 browserAccessibility_->GetAriaTristate("aria-pressed",
571 &isAriaPressedDefined,
573 if (isAriaPressedDefined)
574 return NSAccessibilityCheckBoxRole;
576 return NSAccessibilityButtonRole;
579 // If this is a web area for a presentational iframe, give it a role of
580 // something other than WebArea so that the fact that it's a separate doc
581 // is not exposed to AT.
582 if (browserAccessibility_->IsWebAreaForPresentationalIframe())
583 return NSAccessibilityGroupRole;
585 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
588 // Returns a string indicating the role description of this object.
589 - (NSString*)roleDescription {
590 NSString* role = [self role];
592 ContentClient* content_client = content::GetContentClient();
594 // The following descriptions are specific to webkit.
595 if ([role isEqualToString:@"AXWebArea"]) {
596 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
597 IDS_AX_ROLE_WEB_AREA));
600 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
601 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
605 if ([role isEqualToString:@"AXHeading"]) {
606 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
607 IDS_AX_ROLE_HEADING));
610 if (([role isEqualToString:NSAccessibilityGroupRole] ||
611 [role isEqualToString:NSAccessibilityRadioButtonRole]) &&
612 !browserAccessibility_->IsWebAreaForPresentationalIframe()) {
614 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
615 ui::AXRole internalRole = [self internalRole];
616 if ((internalRole != ui::AX_ROLE_GROUP &&
617 internalRole != ui::AX_ROLE_LIST_ITEM) ||
618 internalRole == ui::AX_ROLE_TAB) {
619 // TODO(dtseng): This is not localized; see crbug/84814.
620 return base::SysUTF8ToNSString(role);
625 switch([self internalRole]) {
626 case ui::AX_ROLE_ARTICLE:
627 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
628 IDS_AX_ROLE_ARTICLE));
629 case ui::AX_ROLE_BANNER:
630 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
631 IDS_AX_ROLE_BANNER));
632 case ui::AX_ROLE_COMPLEMENTARY:
633 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
634 IDS_AX_ROLE_COMPLEMENTARY));
635 case ui::AX_ROLE_CONTENT_INFO:
636 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
637 IDS_AX_ROLE_ADDRESS));
638 case ui::AX_ROLE_DESCRIPTION_LIST:
639 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
640 IDS_AX_ROLE_DESCRIPTION_LIST));
641 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
642 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
643 IDS_AX_ROLE_DESCRIPTION_DETAIL));
644 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
645 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
646 IDS_AX_ROLE_DESCRIPTION_TERM));
647 case ui::AX_ROLE_FIGURE:
648 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
649 IDS_AX_ROLE_FIGURE));
650 case ui::AX_ROLE_FOOTER:
651 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
652 IDS_AX_ROLE_FOOTER));
653 case ui::AX_ROLE_FORM:
654 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
656 case ui::AX_ROLE_MAIN:
657 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
658 IDS_AX_ROLE_MAIN_CONTENT));
659 case ui::AX_ROLE_MATH:
660 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
662 case ui::AX_ROLE_NAVIGATION:
663 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
664 IDS_AX_ROLE_NAVIGATIONAL_LINK));
665 case ui::AX_ROLE_REGION:
666 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
667 IDS_AX_ROLE_REGION));
668 case ui::AX_ROLE_SPIN_BUTTON:
669 // This control is similar to what VoiceOver calls a "stepper".
670 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
671 IDS_AX_ROLE_STEPPER));
672 case ui::AX_ROLE_STATUS:
673 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
674 IDS_AX_ROLE_STATUS));
675 case ui::AX_ROLE_TOGGLE_BUTTON:
676 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
677 IDS_AX_ROLE_TOGGLE_BUTTON));
682 return NSAccessibilityRoleDescription(role, nil);
685 - (NSArray*)rowHeaders {
686 if ([self internalRole] != ui::AX_ROLE_TABLE &&
687 [self internalRole] != ui::AX_ROLE_GRID) {
691 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
692 const std::vector<int32>& uniqueCellIds =
693 browserAccessibility_->GetIntListAttribute(
694 ui::AX_ATTR_UNIQUE_CELL_IDS);
695 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
696 int id = uniqueCellIds[i];
697 BrowserAccessibility* cell =
698 browserAccessibility_->manager()->GetFromID(id);
699 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
700 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
705 - (NSValue*)rowIndexRange {
706 if (!browserAccessibility_->IsCellOrTableHeaderRole())
711 browserAccessibility_->GetIntAttribute(
712 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
713 browserAccessibility_->GetIntAttribute(
714 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
715 if (row >= 0 && rowspan >= 1)
716 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
721 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
723 if ([self internalRole] == ui::AX_ROLE_TABLE||
724 [self internalRole] == ui::AX_ROLE_GRID) {
725 for (BrowserAccessibilityCocoa* child in [self children]) {
726 if ([[child role] isEqualToString:NSAccessibilityRowRole])
727 [ret addObject:child];
729 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
730 const std::vector<int32>& indirectChildIds =
731 browserAccessibility_->GetIntListAttribute(
732 ui::AX_ATTR_INDIRECT_CHILD_IDS);
733 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
734 int id = indirectChildIds[i];
735 BrowserAccessibility* rowElement =
736 browserAccessibility_->manager()->GetFromID(id);
738 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
745 - (NSArray*)selectedChildren {
746 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
748 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
749 BrowserAccessibility* focusedChild =
750 manager->GetFocus(browserAccessibility_);
751 if (focusedChild && focusedChild != browserAccessibility_) {
752 // First try the focused child.
753 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
755 // Next try the active descendant.
756 int activeDescendantId;
757 if (browserAccessibility_->GetIntAttribute(
758 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
759 BrowserAccessibility* activeDescendant =
760 manager->GetFromID(activeDescendantId);
761 if (activeDescendant)
762 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
764 // Otherwise return any children with the "selected" state, which
765 // may come from aria-selected.
766 uint32 childCount = browserAccessibility_->PlatformChildCount();
767 for (uint32 index = 0; index < childCount; ++index) {
768 BrowserAccessibility* child =
769 browserAccessibility_->PlatformGetChild(index);
770 if (child->HasState(ui::AX_STATE_SELECTED))
771 [ret addObject:child->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 ui::AXRole browserAccessibilityRole = [self internalRole];
788 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
789 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
790 return @"AXSecureTextField";
793 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
794 return @"AXDefinitionList";
796 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
797 return @"AXContentList";
799 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
802 // Returns all tabs in this subtree.
804 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
806 if ([self internalRole] == ui::AX_ROLE_TAB)
807 [tabSubtree addObject:self];
809 for (uint i=0; i < [[self children] count]; ++i) {
810 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
811 if ([tabChildren count] > 0)
812 [tabSubtree addObjectsFromArray:tabChildren];
819 return NSStringForStringAttribute(
820 browserAccessibility_, ui::AX_ATTR_NAME);
823 - (id)titleUIElement {
825 if (browserAccessibility_->GetIntAttribute(
826 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
827 BrowserAccessibility* titleElement =
828 browserAccessibility_->manager()->GetFromID(titleElementId);
830 return titleElement->ToBrowserAccessibilityCocoa();
832 std::vector<int32> labelledby_ids =
833 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
834 if (labelledby_ids.size() == 1) {
835 BrowserAccessibility* titleElement =
836 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
838 return titleElement->ToBrowserAccessibilityCocoa();
845 StringAttribute urlAttribute =
846 [[self role] isEqualToString:@"AXWebArea"] ?
847 ui::AX_ATTR_DOC_URL :
850 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
854 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
858 // WebCore uses an attachmentView to get the below behavior.
859 // We do not have any native views backing this object, so need
860 // to approximate Cocoa ax behavior best as we can.
861 NSString* role = [self role];
862 if ([role isEqualToString:@"AXHeading"]) {
864 if (browserAccessibility_->GetIntAttribute(
865 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
866 return [NSNumber numberWithInt:level];
868 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
869 // AXValue does not make sense for pure buttons.
871 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
873 bool isAriaPressedDefined;
875 value = browserAccessibility_->GetAriaTristate(
876 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
881 return [NSNumber numberWithInt:value];
883 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
884 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
887 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
889 browserAccessibility_, ui::AX_STATE_SELECTED) ?
893 if (browserAccessibility_->GetBoolAttribute(
894 ui::AX_ATTR_BUTTON_MIXED)) {
897 return [NSNumber numberWithInt:value];
898 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
899 [role isEqualToString:NSAccessibilitySliderRole] ||
900 [role isEqualToString:NSAccessibilityIncrementorRole] ||
901 [role isEqualToString:NSAccessibilityScrollBarRole]) {
903 if (browserAccessibility_->GetFloatAttribute(
904 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
905 return [NSNumber numberWithFloat:floatValue];
907 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
908 int r = browserAccessibility_->GetIntAttribute(
909 ui::AX_ATTR_COLOR_VALUE_RED);
910 int g = browserAccessibility_->GetIntAttribute(
911 ui::AX_ATTR_COLOR_VALUE_GREEN);
912 int b = browserAccessibility_->GetIntAttribute(
913 ui::AX_ATTR_COLOR_VALUE_BLUE);
914 // This string matches the one returned by a native Mac color well.
915 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
916 r / 255., g / 255., b / 255.];
919 return NSStringForStringAttribute(
920 browserAccessibility_, ui::AX_ATTR_VALUE);
923 - (NSString*)valueDescription {
924 return NSStringForStringAttribute(
925 browserAccessibility_, ui::AX_ATTR_VALUE);
928 - (NSValue*)visibleCharacterRange {
929 std::string value = browserAccessibility_->GetStringAttribute(
931 return [NSValue valueWithRange:NSMakeRange(0, value.size())];
934 - (NSArray*)visibleCells {
935 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
936 const std::vector<int32>& uniqueCellIds =
937 browserAccessibility_->GetIntListAttribute(
938 ui::AX_ATTR_UNIQUE_CELL_IDS);
939 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
940 int id = uniqueCellIds[i];
941 BrowserAccessibility* cell =
942 browserAccessibility_->manager()->GetFromID(id);
944 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
949 - (NSArray*)visibleChildren {
950 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
951 uint32 childCount = browserAccessibility_->PlatformChildCount();
952 for (uint32 index = 0; index < childCount; ++index) {
953 BrowserAccessibilityCocoa* child =
954 browserAccessibility_->PlatformGetChild(index)->
955 ToBrowserAccessibilityCocoa();
956 [ret addObject:child];
961 - (NSArray*)visibleColumns {
962 return [self columns];
965 - (NSArray*)visibleRows {
969 - (NSNumber*)visited {
970 return [NSNumber numberWithBool:
971 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
975 if (!browserAccessibility_)
978 BrowserAccessibilityManagerMac* manager =
979 static_cast<BrowserAccessibilityManagerMac*>(
980 browserAccessibility_->manager());
981 return [manager->parent_view() window];
984 - (NSString*)methodNameForAttribute:(NSString*)attribute {
985 return [attributeToMethodNameMap objectForKey:attribute];
988 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
989 children_.swap(*other);
992 // Returns the accessibility value for the given attribute. If the value isn't
993 // supported this will return nil.
994 - (id)accessibilityAttributeValue:(NSString*)attribute {
995 if (!browserAccessibility_)
999 NSSelectorFromString([self methodNameForAttribute:attribute]);
1001 return [self performSelector:selector];
1003 // TODO(dtseng): refactor remaining attributes.
1004 int selStart, selEnd;
1005 if (browserAccessibility_->GetIntAttribute(
1006 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1007 browserAccessibility_->
1008 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1009 if (selStart > selEnd)
1010 std::swap(selStart, selEnd);
1011 int selLength = selEnd - selStart;
1012 if ([attribute isEqualToString:
1013 NSAccessibilityInsertionPointLineNumberAttribute]) {
1014 const std::vector<int32>& line_breaks =
1015 browserAccessibility_->GetIntListAttribute(
1016 ui::AX_ATTR_LINE_BREAKS);
1017 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1018 if (line_breaks[i] > selStart)
1019 return [NSNumber numberWithInt:i];
1021 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1023 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1024 std::string value = browserAccessibility_->GetStringAttribute(
1026 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1028 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1029 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1035 // Returns the accessibility value for the given attribute and parameter. If the
1036 // value isn't supported this will return nil.
1037 - (id)accessibilityAttributeValue:(NSString*)attribute
1038 forParameter:(id)parameter {
1039 if (!browserAccessibility_)
1042 const std::vector<int32>& line_breaks =
1043 browserAccessibility_->GetIntListAttribute(
1044 ui::AX_ATTR_LINE_BREAKS);
1045 std::string value = browserAccessibility_->GetStringAttribute(
1047 int len = static_cast<int>(value.size());
1049 if ([attribute isEqualToString:
1050 NSAccessibilityStringForRangeParameterizedAttribute]) {
1051 NSRange range = [(NSValue*)parameter rangeValue];
1052 std::string value = browserAccessibility_->GetStringAttribute(
1054 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1057 if ([attribute isEqualToString:
1058 NSAccessibilityLineForIndexParameterizedAttribute]) {
1059 int index = [(NSNumber*)parameter intValue];
1060 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1061 if (line_breaks[i] > index)
1062 return [NSNumber numberWithInt:i];
1064 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1067 if ([attribute isEqualToString:
1068 NSAccessibilityRangeForLineParameterizedAttribute]) {
1069 int line_index = [(NSNumber*)parameter intValue];
1070 int line_count = static_cast<int>(line_breaks.size()) + 1;
1071 if (line_index < 0 || line_index >= line_count)
1073 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1074 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1075 return [NSValue valueWithRange:
1076 NSMakeRange(start, end - start)];
1079 if ([attribute isEqualToString:
1080 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1081 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1082 [self internalRole] != ui::AX_ROLE_GRID) {
1085 if (![parameter isKindOfClass:[NSArray self]])
1087 NSArray* array = parameter;
1088 int column = [[array objectAtIndex:0] intValue];
1089 int row = [[array objectAtIndex:1] intValue];
1090 int num_columns = browserAccessibility_->GetIntAttribute(
1091 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1092 int num_rows = browserAccessibility_->GetIntAttribute(
1093 ui::AX_ATTR_TABLE_ROW_COUNT);
1094 if (column < 0 || column >= num_columns ||
1095 row < 0 || row >= num_rows) {
1099 i < browserAccessibility_->PlatformChildCount();
1101 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1102 if (child->GetRole() != ui::AX_ROLE_ROW)
1105 if (!child->GetIntAttribute(
1106 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1114 j < child->PlatformChildCount();
1116 BrowserAccessibility* cell = child->PlatformGetChild(j);
1117 if (!browserAccessibility_->IsCellOrTableHeaderRole())
1120 if (!cell->GetIntAttribute(
1121 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1125 if (colIndex == column)
1126 return cell->ToBrowserAccessibilityCocoa();
1127 if (colIndex > column)
1134 if ([attribute isEqualToString:
1135 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1136 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1138 NSRange range = [(NSValue*)parameter rangeValue];
1139 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1140 range.location, range.length);
1141 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1142 NSSize size = NSMakeSize(rect.width(), rect.height());
1143 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1144 NSRect nsrect = NSMakeRect(
1145 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1146 return [NSValue valueWithRect:nsrect];
1149 // TODO(dtseng): support the following attributes.
1150 if ([attribute isEqualTo:
1151 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1152 [attribute isEqualTo:
1153 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1154 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1155 [attribute isEqualTo:
1156 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1162 // Returns an array of parameterized attributes names that this object will
1164 - (NSArray*)accessibilityParameterizedAttributeNames {
1165 if (!browserAccessibility_)
1168 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1169 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1170 return [NSArray arrayWithObjects:
1171 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1174 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1175 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1176 return [NSArray arrayWithObjects:
1177 NSAccessibilityLineForIndexParameterizedAttribute,
1178 NSAccessibilityRangeForLineParameterizedAttribute,
1179 NSAccessibilityStringForRangeParameterizedAttribute,
1180 NSAccessibilityRangeForPositionParameterizedAttribute,
1181 NSAccessibilityRangeForIndexParameterizedAttribute,
1182 NSAccessibilityBoundsForRangeParameterizedAttribute,
1183 NSAccessibilityRTFForRangeParameterizedAttribute,
1184 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1185 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1188 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1189 return [NSArray arrayWithObjects:
1190 NSAccessibilityBoundsForRangeParameterizedAttribute,
1196 // Returns an array of action names that this object will respond to.
1197 - (NSArray*)accessibilityActionNames {
1198 if (!browserAccessibility_)
1201 NSMutableArray* ret =
1202 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1203 NSString* role = [self role];
1204 // TODO(dtseng): this should only get set when there's a default action.
1205 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1206 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1207 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1208 [ret addObject:NSAccessibilityPressAction];
1214 // Returns a sub-array of values for the given attribute value, starting at
1215 // index, with up to maxCount items. If the given index is out of bounds,
1216 // or there are no values for the given attribute, it will return nil.
1217 // This method is used for querying subsets of values, without having to
1218 // return a large set of data, such as elements with a large number of
1220 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1221 index:(NSUInteger)index
1222 maxCount:(NSUInteger)maxCount {
1223 if (!browserAccessibility_)
1226 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1229 NSUInteger arrayCount = [fullArray count];
1230 if (index >= arrayCount)
1233 if ((index + maxCount) > arrayCount) {
1234 subRange = NSMakeRange(index, arrayCount - index);
1236 subRange = NSMakeRange(index, maxCount);
1238 return [fullArray subarrayWithRange:subRange];
1241 // Returns the count of the specified accessibility array attribute.
1242 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1243 if (!browserAccessibility_)
1246 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1247 return [fullArray count];
1250 // Returns the list of accessibility attributes that this object supports.
1251 - (NSArray*)accessibilityAttributeNames {
1252 if (!browserAccessibility_)
1255 // General attributes.
1256 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1257 NSAccessibilityChildrenAttribute,
1258 NSAccessibilityDescriptionAttribute,
1259 NSAccessibilityEnabledAttribute,
1260 NSAccessibilityFocusedAttribute,
1261 NSAccessibilityHelpAttribute,
1262 NSAccessibilityLinkedUIElementsAttribute,
1263 NSAccessibilityParentAttribute,
1264 NSAccessibilityPositionAttribute,
1265 NSAccessibilityRoleAttribute,
1266 NSAccessibilityRoleDescriptionAttribute,
1267 NSAccessibilitySizeAttribute,
1268 NSAccessibilitySubroleAttribute,
1269 NSAccessibilityTitleAttribute,
1270 NSAccessibilityTopLevelUIElementAttribute,
1271 NSAccessibilityValueAttribute,
1272 NSAccessibilityWindowAttribute,
1278 // Specific role attributes.
1279 NSString* role = [self role];
1280 NSString* subrole = [self subrole];
1281 if ([role isEqualToString:NSAccessibilityTableRole] ||
1282 [role isEqualToString:NSAccessibilityGridRole]) {
1283 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1284 NSAccessibilityColumnsAttribute,
1285 NSAccessibilityVisibleColumnsAttribute,
1286 NSAccessibilityRowsAttribute,
1287 NSAccessibilityVisibleRowsAttribute,
1288 NSAccessibilityVisibleCellsAttribute,
1289 NSAccessibilityHeaderAttribute,
1290 NSAccessibilityColumnHeaderUIElementsAttribute,
1291 NSAccessibilityRowHeaderUIElementsAttribute,
1293 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1294 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1295 NSAccessibilityIndexAttribute,
1296 NSAccessibilityHeaderAttribute,
1297 NSAccessibilityRowsAttribute,
1298 NSAccessibilityVisibleRowsAttribute,
1300 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1301 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1302 NSAccessibilityColumnIndexRangeAttribute,
1303 NSAccessibilityRowIndexRangeAttribute,
1305 } else if ([role isEqualToString:@"AXWebArea"]) {
1306 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1308 @"AXLoadingProgress",
1310 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1311 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1312 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1313 NSAccessibilityInsertionPointLineNumberAttribute,
1314 NSAccessibilityNumberOfCharactersAttribute,
1315 NSAccessibilitySelectedTextAttribute,
1316 NSAccessibilitySelectedTextRangeAttribute,
1317 NSAccessibilityVisibleCharacterRangeAttribute,
1319 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1320 [ret addObject:NSAccessibilityTabsAttribute];
1321 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1322 [role isEqualToString:NSAccessibilitySliderRole] ||
1323 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1324 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1325 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1326 NSAccessibilityMaxValueAttribute,
1327 NSAccessibilityMinValueAttribute,
1328 NSAccessibilityValueDescriptionAttribute,
1330 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1331 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1332 NSAccessibilityDisclosingAttribute,
1333 NSAccessibilityDisclosedByRowAttribute,
1334 NSAccessibilityDisclosureLevelAttribute,
1335 NSAccessibilityDisclosedRowsAttribute,
1337 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1338 if (browserAccessibility_->GetParent()) {
1339 base::string16 parentRole;
1340 browserAccessibility_->GetParent()->GetHtmlAttribute(
1341 "role", &parentRole);
1342 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1343 if (parentRole == treegridRole) {
1344 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1345 NSAccessibilityDisclosingAttribute,
1346 NSAccessibilityDisclosedByRowAttribute,
1347 NSAccessibilityDisclosureLevelAttribute,
1348 NSAccessibilityDisclosedRowsAttribute,
1351 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1352 NSAccessibilityIndexAttribute,
1356 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1357 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1358 NSAccessibilitySelectedChildrenAttribute,
1359 NSAccessibilityVisibleChildrenAttribute,
1363 // Add the url attribute only if it has a valid url.
1364 if ([self url] != nil) {
1365 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1366 NSAccessibilityURLAttribute,
1371 if (browserAccessibility_->HasStringAttribute(
1372 ui::AX_ATTR_LIVE_STATUS)) {
1373 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1377 if (browserAccessibility_->HasStringAttribute(
1378 ui::AX_ATTR_LIVE_RELEVANT)) {
1379 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1383 if (browserAccessibility_->HasBoolAttribute(
1384 ui::AX_ATTR_LIVE_ATOMIC)) {
1385 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1389 if (browserAccessibility_->HasBoolAttribute(
1390 ui::AX_ATTR_LIVE_BUSY)) {
1391 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1395 // Add aria-grabbed attribute only if it has true.
1396 if (browserAccessibility_->HasBoolAttribute(ui::AX_ATTR_GRABBED)) {
1397 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1402 // Add expanded attribute only if it has expanded or collapsed state.
1403 if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) ||
1404 GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) {
1405 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1406 NSAccessibilityExpandedAttribute,
1410 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)
1411 || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) {
1412 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1413 NSAccessibilityOrientationAttribute, nil]];
1416 if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) {
1417 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1418 @"AXPlaceholder", nil]];
1421 if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) {
1422 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1423 @"AXRequired", nil]];
1426 // Title UI Element.
1427 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1428 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1429 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1431 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1432 NSAccessibilityTitleUIElementAttribute,
1435 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1436 // for elements which are referred to by labelledby or are labels
1441 // Returns the index of the child in this objects array of children.
1442 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1443 if (!browserAccessibility_)
1446 NSUInteger index = 0;
1447 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1448 if ([child isEqual:childToCheck])
1455 // Returns whether or not the specified attribute can be set by the
1456 // accessibility API via |accessibilitySetValue:forAttribute:|.
1457 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1458 if (!browserAccessibility_)
1461 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1462 return GetState(browserAccessibility_,
1463 ui::AX_STATE_FOCUSABLE);
1464 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1465 return browserAccessibility_->GetBoolAttribute(
1466 ui::AX_ATTR_CAN_SET_VALUE);
1468 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1469 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1470 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1476 // Returns whether or not this object should be ignored in the accessibility
1478 - (BOOL)accessibilityIsIgnored {
1479 if (!browserAccessibility_)
1482 return [self isIgnored];
1485 // Performs the given accessibility action on the webkit accessibility object
1486 // that backs this object.
1487 - (void)accessibilityPerformAction:(NSString*)action {
1488 if (!browserAccessibility_)
1491 // TODO(dmazzoni): Support more actions.
1492 if ([action isEqualToString:NSAccessibilityPressAction]) {
1493 [self delegate]->AccessibilityDoDefaultAction(
1494 browserAccessibility_->GetId());
1495 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1496 NSPoint objOrigin = [self origin];
1497 NSSize size = [[self size] sizeValue];
1498 gfx::Point origin = [self delegate]->AccessibilityOriginInScreen(
1499 gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
1500 origin.Offset(size.width / 2, size.height / 2);
1501 [self delegate]->AccessibilityShowMenu(origin);
1505 // Returns the description of the given action.
1506 - (NSString*)accessibilityActionDescription:(NSString*)action {
1507 if (!browserAccessibility_)
1510 return NSAccessibilityActionDescription(action);
1513 // Sets an override value for a specific accessibility attribute.
1514 // This class does not support this.
1515 - (BOOL)accessibilitySetOverrideValue:(id)value
1516 forAttribute:(NSString*)attribute {
1520 // Sets the value for an accessibility attribute via the accessibility API.
1521 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1522 if (!browserAccessibility_)
1525 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1526 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1527 NSNumber* focusedNumber = value;
1528 BOOL focused = [focusedNumber intValue];
1530 manager->SetFocus(browserAccessibility_, true);
1532 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1533 NSRange range = [(NSValue*)value rangeValue];
1534 [self delegate]->AccessibilitySetTextSelection(
1535 browserAccessibility_->GetId(),
1536 range.location, range.location + range.length);
1540 // Returns the deepest accessibility child that should not be ignored.
1541 // It is assumed that the hit test has been narrowed down to this object
1542 // or one of its children, so this will never return nil unless this
1543 // object is invalid.
1544 - (id)accessibilityHitTest:(NSPoint)point {
1545 if (!browserAccessibility_)
1548 BrowserAccessibilityCocoa* hit = self;
1549 for (BrowserAccessibilityCocoa* child in [self children]) {
1550 if (!child->browserAccessibility_)
1552 NSPoint origin = [child origin];
1553 NSSize size = [[child size] sizeValue];
1555 rect.origin = origin;
1557 if (NSPointInRect(point, rect)) {
1559 id childResult = [child accessibilityHitTest:point];
1560 if (![childResult accessibilityIsIgnored]) {
1566 return NSAccessibilityUnignoredAncestor(hit);
1569 - (BOOL)isEqual:(id)object {
1570 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1572 return ([self hash] == [object hash]);
1575 - (NSUInteger)hash {
1576 // Potentially called during dealloc.
1577 if (!browserAccessibility_)
1578 return [super hash];
1579 return browserAccessibility_->GetId();
1582 - (BOOL)accessibilityShouldUseUniqueId {