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 { @"AXARIASetSize", @"ariaSetSize" },
112 { @"AXARIAPosInSet", @"ariaPosInSet" },
113 { @"AXARIARelevant", @"ariaRelevant" },
114 { @"AXDropEffects", @"dropeffect" },
115 { @"AXGrabbed", @"grabbed" },
116 { @"AXInvalid", @"invalid" },
117 { @"AXLoaded", @"loaded" },
118 { @"AXLoadingProgress", @"loadingProgress" },
119 { @"AXPlaceholder", @"placeholder" },
120 { @"AXRequired", @"required" },
121 { @"AXSortDirection", @"sortDirection" },
122 { @"AXVisited", @"visited" },
125 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
126 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
127 sizeof(attributeToMethodNameContainer[0]);
128 for (size_t i = 0; i < numAttributes; ++i) {
129 [dict setObject:attributeToMethodNameContainer[i].methodName
130 forKey:attributeToMethodNameContainer[i].attribute];
132 attributeToMethodNameMap = dict;
136 - (id)initWithObject:(BrowserAccessibility*)accessibility {
137 if ((self = [super init]))
138 browserAccessibility_ = accessibility;
143 if (browserAccessibility_) {
144 NSAccessibilityUnregisterUniqueIdForUIElement(self);
145 browserAccessibility_ = NULL;
149 - (NSString*)accessKey {
150 return NSStringForStringAttribute(
151 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
154 - (NSNumber*)ariaAtomic {
155 bool boolValue = browserAccessibility_->GetBoolAttribute(
156 ui::AX_ATTR_LIVE_ATOMIC);
157 return [NSNumber numberWithBool:boolValue];
160 - (NSNumber*)ariaBusy {
161 return [NSNumber numberWithBool:
162 GetState(browserAccessibility_, ui::AX_STATE_BUSY)];
165 - (NSString*)ariaLive {
166 return NSStringForStringAttribute(
167 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
170 - (NSString*)ariaRelevant {
171 return NSStringForStringAttribute(
172 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
175 - (NSNumber*)ariaPosInSet {
176 return [NSNumber numberWithInt:
177 browserAccessibility_->GetIntAttribute(ui::AX_ATTR_POS_IN_SET)];
180 - (NSNumber*)ariaSetSize {
181 return [NSNumber numberWithInt:
182 browserAccessibility_->GetIntAttribute(ui::AX_ATTR_SET_SIZE)];
185 // Returns an array of BrowserAccessibilityCocoa objects, representing the
186 // accessibility children of this object.
187 - (NSArray*)children {
189 uint32 childCount = browserAccessibility_->PlatformChildCount();
190 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
191 for (uint32 index = 0; index < childCount; ++index) {
192 BrowserAccessibilityCocoa* child =
193 browserAccessibility_->PlatformGetChild(index)->
194 ToBrowserAccessibilityCocoa();
195 if ([child isIgnored])
196 [children_ addObjectsFromArray:[child children]];
198 [children_ addObject:child];
201 // Also, add indirect children (if any).
202 const std::vector<int32>& indirectChildIds =
203 browserAccessibility_->GetIntListAttribute(
204 ui::AX_ATTR_INDIRECT_CHILD_IDS);
205 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
206 int32 child_id = indirectChildIds[i];
207 BrowserAccessibility* child =
208 browserAccessibility_->manager()->GetFromID(child_id);
210 // This only became necessary as a result of crbug.com/93095. It should be
211 // a DCHECK in the future.
213 BrowserAccessibilityCocoa* child_cocoa =
214 child->ToBrowserAccessibilityCocoa();
215 [children_ addObject:child_cocoa];
222 - (void)childrenChanged {
223 if (![self isIgnored]) {
226 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
231 - (NSArray*)columnHeaders {
232 if ([self internalRole] != ui::AX_ROLE_TABLE &&
233 [self internalRole] != ui::AX_ROLE_GRID) {
237 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
238 const std::vector<int32>& uniqueCellIds =
239 browserAccessibility_->GetIntListAttribute(
240 ui::AX_ATTR_UNIQUE_CELL_IDS);
241 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
242 int id = uniqueCellIds[i];
243 BrowserAccessibility* cell =
244 browserAccessibility_->manager()->GetFromID(id);
245 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
246 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
251 - (NSValue*)columnIndexRange {
252 if (!browserAccessibility_->IsCellOrTableHeaderRole())
257 browserAccessibility_->GetIntAttribute(
258 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
259 browserAccessibility_->GetIntAttribute(
260 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
261 if (column >= 0 && colspan >= 1)
262 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
266 - (NSArray*)columns {
267 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
268 for (BrowserAccessibilityCocoa* child in [self children]) {
269 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
270 [ret addObject:child];
275 - (NSString*)description {
276 std::string description;
277 if (browserAccessibility_->GetStringAttribute(
278 ui::AX_ATTR_DESCRIPTION, &description)) {
279 return base::SysUTF8ToNSString(description);
282 // If the role is anything other than an image, or if there's
283 // a title or title UI element, just return an empty string.
284 if (![[self role] isEqualToString:NSAccessibilityImageRole])
286 if (browserAccessibility_->HasStringAttribute(
290 if ([self titleUIElement])
293 // The remaining case is an image where there's no other title.
294 // Return the base part of the filename as the description.
296 if (browserAccessibility_->GetStringAttribute(
297 ui::AX_ATTR_URL, &url)) {
298 // Given a url like http://foo.com/bar/baz.png, just return the
299 // base name, e.g., "baz.png".
300 size_t leftIndex = url.rfind('/');
301 std::string basename =
302 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
303 return base::SysUTF8ToNSString(basename);
309 - (NSNumber*)disclosing {
310 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
311 return [NSNumber numberWithBool:
312 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
318 - (id)disclosedByRow {
319 // The row that contains this row.
320 // It should be the same as the first parent that is a treeitem.
324 - (NSNumber*)disclosureLevel {
325 ui::AXRole role = [self internalRole];
326 if (role == ui::AX_ROLE_ROW ||
327 role == ui::AX_ROLE_TREE_ITEM) {
328 int level = browserAccessibility_->GetIntAttribute(
329 ui::AX_ATTR_HIERARCHICAL_LEVEL);
330 // Mac disclosureLevel is 0-based, but web levels are 1-based.
333 return [NSNumber numberWithInt:level];
339 - (id)disclosedRows {
340 // The rows that are considered inside this row.
344 - (NSString*)dropeffect {
345 return NSStringForStringAttribute(
346 browserAccessibility_, ui::AX_ATTR_DROPEFFECT);
349 - (NSNumber*)enabled {
350 return [NSNumber numberWithBool:
351 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
354 - (NSNumber*)expanded {
355 return [NSNumber numberWithBool:
356 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
359 - (NSNumber*)focused {
360 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
361 NSNumber* ret = [NSNumber numberWithBool:
362 manager->GetFocus(NULL) == browserAccessibility_];
366 - (NSNumber*)grabbed {
367 bool boolValue = browserAccessibility_->GetBoolAttribute(ui::AX_ATTR_GRABBED);
368 return [NSNumber numberWithBool:boolValue];
372 int headerElementId = -1;
373 if ([self internalRole] == ui::AX_ROLE_TABLE ||
374 [self internalRole] == ui::AX_ROLE_GRID) {
375 browserAccessibility_->GetIntAttribute(
376 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
377 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
378 browserAccessibility_->GetIntAttribute(
379 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
380 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
381 browserAccessibility_->GetIntAttribute(
382 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
385 if (headerElementId > 0) {
386 BrowserAccessibility* headerObject =
387 browserAccessibility_->manager()->GetFromID(headerElementId);
389 return headerObject->ToBrowserAccessibilityCocoa();
395 return NSStringForStringAttribute(
396 browserAccessibility_, ui::AX_ATTR_HELP);
400 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
401 int columnIndex = browserAccessibility_->GetIntAttribute(
402 ui::AX_ATTR_TABLE_COLUMN_INDEX);
403 return [NSNumber numberWithInt:columnIndex];
404 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
405 int rowIndex = browserAccessibility_->GetIntAttribute(
406 ui::AX_ATTR_TABLE_ROW_INDEX);
407 return [NSNumber numberWithInt:rowIndex];
413 // Returns whether or not this node should be ignored in the
414 // accessibility tree.
416 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
419 - (NSString*)invalid {
421 if (!browserAccessibility_->GetIntAttribute(
422 ui::AX_ATTR_INVALID_STATE, &invalidState))
425 switch (invalidState) {
426 case ui::AX_INVALID_STATE_FALSE:
428 case ui::AX_INVALID_STATE_TRUE:
430 case ui::AX_INVALID_STATE_SPELLING:
432 case ui::AX_INVALID_STATE_GRAMMAR:
434 case ui::AX_INVALID_STATE_OTHER:
436 std::string ariaInvalidValue;
437 if (browserAccessibility_->GetStringAttribute(
438 ui::AX_ATTR_ARIA_INVALID_VALUE,
440 return base::SysUTF8ToNSString(ariaInvalidValue);
441 // Return @"true" since we cannot be more specific about the value.
451 - (NSString*)placeholder {
452 return NSStringForStringAttribute(
453 browserAccessibility_, ui::AX_ATTR_PLACEHOLDER);
456 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
457 addTo:(NSMutableArray*)outArray {
458 const std::vector<int32>& attributeValues =
459 browserAccessibility_->GetIntListAttribute(attribute);
460 for (size_t i = 0; i < attributeValues.size(); ++i) {
461 BrowserAccessibility* element =
462 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
464 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
468 - (NSArray*)linkedUIElements {
469 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
470 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
471 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
472 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
473 if ([ret count] == 0)
478 - (NSNumber*)loaded {
479 return [NSNumber numberWithBool:YES];
482 - (NSNumber*)loadingProgress {
483 float floatValue = browserAccessibility_->GetFloatAttribute(
484 ui::AX_ATTR_DOC_LOADING_PROGRESS);
485 return [NSNumber numberWithFloat:floatValue];
488 - (NSNumber*)maxValue {
489 float floatValue = browserAccessibility_->GetFloatAttribute(
490 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
491 return [NSNumber numberWithFloat:floatValue];
494 - (NSNumber*)minValue {
495 float floatValue = browserAccessibility_->GetFloatAttribute(
496 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
497 return [NSNumber numberWithFloat:floatValue];
500 - (NSString*)orientation {
501 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
502 return NSAccessibilityVerticalOrientationValue;
503 else if (GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL))
504 return NSAccessibilityHorizontalOrientationValue;
509 - (NSNumber*)numberOfCharacters {
510 std::string value = browserAccessibility_->GetStringAttribute(
512 return [NSNumber numberWithInt:value.size()];
515 // The origin of this accessibility object in the page's document.
516 // This is relative to webkit's top-left origin, not Cocoa's
517 // bottom-left origin.
519 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
520 return NSMakePoint(bounds.x(), bounds.y());
524 // A nil parent means we're the root.
525 if (browserAccessibility_->GetParent()) {
526 return NSAccessibilityUnignoredAncestor(
527 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
529 // Hook back up to RenderWidgetHostViewCocoa.
530 BrowserAccessibilityManagerMac* manager =
531 static_cast<BrowserAccessibilityManagerMac*>(
532 browserAccessibility_->manager());
533 return manager->parent_view();
537 - (NSValue*)position {
538 NSPoint origin = [self origin];
539 NSSize size = [[self size] sizeValue];
540 NSPoint pointInScreen = [self pointInScreen:origin size:size];
541 return [NSValue valueWithPoint:pointInScreen];
544 - (NSNumber*)required {
545 return [NSNumber numberWithBool:
546 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
549 // Returns an enum indicating the role from browserAccessibility_.
550 - (ui::AXRole)internalRole {
551 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
554 - (content::BrowserAccessibilityDelegate*)delegate {
555 return browserAccessibility_->manager() ?
556 browserAccessibility_->manager()->delegate() :
560 - (NSPoint)pointInScreen:(NSPoint)origin
562 if (!browserAccessibility_)
565 // Get the delegate for the topmost BrowserAccessibilityManager, because
566 // that's the only one that can convert points to their origin in the screen.
567 BrowserAccessibilityDelegate* delegate =
568 browserAccessibility_->manager()->GetDelegateFromRootManager();
570 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
571 gfx::Point point = delegate->AccessibilityOriginInScreen(bounds);
572 return NSMakePoint(point.x(), point.y());
578 // Returns a string indicating the NSAccessibility role of this object.
580 ui::AXRole role = [self internalRole];
581 if (role == ui::AX_ROLE_CANVAS &&
582 browserAccessibility_->GetBoolAttribute(
583 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
584 return NSAccessibilityGroupRole;
586 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
587 bool isAriaPressedDefined;
589 browserAccessibility_->GetAriaTristate("aria-pressed",
590 &isAriaPressedDefined,
592 if (isAriaPressedDefined)
593 return NSAccessibilityCheckBoxRole;
595 return NSAccessibilityButtonRole;
597 if (role == ui::AX_ROLE_TEXT_FIELD &&
598 browserAccessibility_->HasState(ui::AX_STATE_MULTILINE)) {
599 return NSAccessibilityTextAreaRole;
602 // If this is a web area for a presentational iframe, give it a role of
603 // something other than WebArea so that the fact that it's a separate doc
604 // is not exposed to AT.
605 if (browserAccessibility_->IsWebAreaForPresentationalIframe())
606 return NSAccessibilityGroupRole;
608 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
611 // Returns a string indicating the role description of this object.
612 - (NSString*)roleDescription {
613 NSString* role = [self role];
615 ContentClient* content_client = content::GetContentClient();
617 // The following descriptions are specific to webkit.
618 if ([role isEqualToString:@"AXWebArea"]) {
619 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
620 IDS_AX_ROLE_WEB_AREA));
623 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
624 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
628 if ([role isEqualToString:@"AXHeading"]) {
629 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
630 IDS_AX_ROLE_HEADING));
633 if (([role isEqualToString:NSAccessibilityGroupRole] ||
634 [role isEqualToString:NSAccessibilityRadioButtonRole]) &&
635 !browserAccessibility_->IsWebAreaForPresentationalIframe()) {
637 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
638 ui::AXRole internalRole = [self internalRole];
639 if ((internalRole != ui::AX_ROLE_GROUP &&
640 internalRole != ui::AX_ROLE_LIST_ITEM) ||
641 internalRole == ui::AX_ROLE_TAB) {
642 // TODO(dtseng): This is not localized; see crbug/84814.
643 return base::SysUTF8ToNSString(role);
648 switch([self internalRole]) {
649 case ui::AX_ROLE_ARTICLE:
650 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
651 IDS_AX_ROLE_ARTICLE));
652 case ui::AX_ROLE_BANNER:
653 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
654 IDS_AX_ROLE_BANNER));
655 case ui::AX_ROLE_COMPLEMENTARY:
656 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
657 IDS_AX_ROLE_COMPLEMENTARY));
658 case ui::AX_ROLE_CONTENT_INFO:
659 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
660 IDS_AX_ROLE_ADDRESS));
661 case ui::AX_ROLE_DESCRIPTION_LIST:
662 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
663 IDS_AX_ROLE_DESCRIPTION_LIST));
664 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
665 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
666 IDS_AX_ROLE_DESCRIPTION_DETAIL));
667 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
668 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
669 IDS_AX_ROLE_DESCRIPTION_TERM));
670 case ui::AX_ROLE_FIGURE:
671 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
672 IDS_AX_ROLE_FIGURE));
673 case ui::AX_ROLE_FOOTER:
674 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
675 IDS_AX_ROLE_FOOTER));
676 case ui::AX_ROLE_FORM:
677 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
679 case ui::AX_ROLE_MAIN:
680 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
681 IDS_AX_ROLE_MAIN_CONTENT));
682 case ui::AX_ROLE_MATH:
683 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
685 case ui::AX_ROLE_NAVIGATION:
686 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
687 IDS_AX_ROLE_NAVIGATIONAL_LINK));
688 case ui::AX_ROLE_REGION:
689 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
690 IDS_AX_ROLE_REGION));
691 case ui::AX_ROLE_SPIN_BUTTON:
692 // This control is similar to what VoiceOver calls a "stepper".
693 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
694 IDS_AX_ROLE_STEPPER));
695 case ui::AX_ROLE_STATUS:
696 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
697 IDS_AX_ROLE_STATUS));
698 case ui::AX_ROLE_SEARCH_BOX:
699 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
700 IDS_AX_ROLE_SEARCH_BOX));
701 case ui::AX_ROLE_SWITCH:
702 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
703 IDS_AX_ROLE_SWITCH));
704 case ui::AX_ROLE_TOGGLE_BUTTON:
705 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
706 IDS_AX_ROLE_TOGGLE_BUTTON));
711 return NSAccessibilityRoleDescription(role, nil);
714 - (NSArray*)rowHeaders {
715 if ([self internalRole] != ui::AX_ROLE_TABLE &&
716 [self internalRole] != ui::AX_ROLE_GRID) {
720 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
721 const std::vector<int32>& uniqueCellIds =
722 browserAccessibility_->GetIntListAttribute(
723 ui::AX_ATTR_UNIQUE_CELL_IDS);
724 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
725 int id = uniqueCellIds[i];
726 BrowserAccessibility* cell =
727 browserAccessibility_->manager()->GetFromID(id);
728 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
729 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
734 - (NSValue*)rowIndexRange {
735 if (!browserAccessibility_->IsCellOrTableHeaderRole())
740 browserAccessibility_->GetIntAttribute(
741 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
742 browserAccessibility_->GetIntAttribute(
743 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
744 if (row >= 0 && rowspan >= 1)
745 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
750 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
752 if ([self internalRole] == ui::AX_ROLE_TABLE||
753 [self internalRole] == ui::AX_ROLE_GRID) {
754 for (BrowserAccessibilityCocoa* child in [self children]) {
755 if ([[child role] isEqualToString:NSAccessibilityRowRole])
756 [ret addObject:child];
758 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
759 const std::vector<int32>& indirectChildIds =
760 browserAccessibility_->GetIntListAttribute(
761 ui::AX_ATTR_INDIRECT_CHILD_IDS);
762 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
763 int id = indirectChildIds[i];
764 BrowserAccessibility* rowElement =
765 browserAccessibility_->manager()->GetFromID(id);
767 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
774 - (NSArray*)selectedChildren {
775 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
777 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
778 BrowserAccessibility* focusedChild =
779 manager->GetFocus(browserAccessibility_);
780 if (focusedChild && focusedChild != browserAccessibility_) {
781 // First try the focused child.
782 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
784 // Next try the active descendant.
785 int activeDescendantId;
786 if (browserAccessibility_->GetIntAttribute(
787 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
788 BrowserAccessibility* activeDescendant =
789 manager->GetFromID(activeDescendantId);
790 if (activeDescendant)
791 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
793 // Otherwise return any children with the "selected" state, which
794 // may come from aria-selected.
795 uint32 childCount = browserAccessibility_->PlatformChildCount();
796 for (uint32 index = 0; index < childCount; ++index) {
797 BrowserAccessibility* child =
798 browserAccessibility_->PlatformGetChild(index);
799 if (child->HasState(ui::AX_STATE_SELECTED))
800 [ret addObject:child->ToBrowserAccessibilityCocoa()];
808 // Returns the size of this object.
810 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
811 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
814 - (NSString*)sortDirection {
816 if (!browserAccessibility_->GetIntAttribute(
817 ui::AX_ATTR_SORT_DIRECTION, &sortDirection))
820 switch (sortDirection) {
821 case ui::AX_SORT_DIRECTION_UNSORTED:
823 case ui::AX_SORT_DIRECTION_ASCENDING:
824 return @"AXSortDirectionAscending";
825 case ui::AX_SORT_DIRECTION_DESCENDING:
826 return @"AXSortDirectionDescending";
827 case ui::AX_SORT_DIRECTION_OTHER:
828 return @"AXSortDirectionUnknown";
836 // Returns a subrole based upon the role.
837 - (NSString*) subrole {
838 ui::AXRole browserAccessibilityRole = [self internalRole];
839 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
840 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
841 return @"AXSecureTextField";
844 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
845 return @"AXDefinitionList";
847 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
848 return @"AXContentList";
850 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
853 // Returns all tabs in this subtree.
855 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
857 if ([self internalRole] == ui::AX_ROLE_TAB)
858 [tabSubtree addObject:self];
860 for (uint i=0; i < [[self children] count]; ++i) {
861 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
862 if ([tabChildren count] > 0)
863 [tabSubtree addObjectsFromArray:tabChildren];
870 return NSStringForStringAttribute(
871 browserAccessibility_, ui::AX_ATTR_NAME);
874 - (id)titleUIElement {
876 if (browserAccessibility_->GetIntAttribute(
877 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
878 BrowserAccessibility* titleElement =
879 browserAccessibility_->manager()->GetFromID(titleElementId);
881 return titleElement->ToBrowserAccessibilityCocoa();
883 std::vector<int32> labelledby_ids =
884 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
885 if (labelledby_ids.size() == 1) {
886 BrowserAccessibility* titleElement =
887 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
889 return titleElement->ToBrowserAccessibilityCocoa();
896 StringAttribute urlAttribute =
897 [[self role] isEqualToString:@"AXWebArea"] ?
898 ui::AX_ATTR_DOC_URL :
901 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
905 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
909 // WebCore uses an attachmentView to get the below behavior.
910 // We do not have any native views backing this object, so need
911 // to approximate Cocoa ax behavior best as we can.
912 NSString* role = [self role];
913 if ([role isEqualToString:@"AXHeading"]) {
915 if (browserAccessibility_->GetIntAttribute(
916 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
917 return [NSNumber numberWithInt:level];
919 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
920 // AXValue does not make sense for pure buttons.
922 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
924 bool isAriaPressedDefined;
926 value = browserAccessibility_->GetAriaTristate(
927 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
932 return [NSNumber numberWithInt:value];
934 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
935 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
938 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
940 browserAccessibility_, ui::AX_STATE_SELECTED) ?
944 if (browserAccessibility_->GetBoolAttribute(
945 ui::AX_ATTR_BUTTON_MIXED)) {
948 return [NSNumber numberWithInt:value];
949 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
950 [role isEqualToString:NSAccessibilitySliderRole] ||
951 [role isEqualToString:NSAccessibilityIncrementorRole] ||
952 [role isEqualToString:NSAccessibilityScrollBarRole]) {
954 if (browserAccessibility_->GetFloatAttribute(
955 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
956 return [NSNumber numberWithFloat:floatValue];
958 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
959 int r = browserAccessibility_->GetIntAttribute(
960 ui::AX_ATTR_COLOR_VALUE_RED);
961 int g = browserAccessibility_->GetIntAttribute(
962 ui::AX_ATTR_COLOR_VALUE_GREEN);
963 int b = browserAccessibility_->GetIntAttribute(
964 ui::AX_ATTR_COLOR_VALUE_BLUE);
965 // This string matches the one returned by a native Mac color well.
966 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
967 r / 255., g / 255., b / 255.];
970 return NSStringForStringAttribute(
971 browserAccessibility_, ui::AX_ATTR_VALUE);
974 - (NSString*)valueDescription {
975 return NSStringForStringAttribute(
976 browserAccessibility_, ui::AX_ATTR_VALUE);
979 - (NSValue*)visibleCharacterRange {
980 std::string value = browserAccessibility_->GetStringAttribute(
982 return [NSValue valueWithRange:NSMakeRange(0, value.size())];
985 - (NSArray*)visibleCells {
986 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
987 const std::vector<int32>& uniqueCellIds =
988 browserAccessibility_->GetIntListAttribute(
989 ui::AX_ATTR_UNIQUE_CELL_IDS);
990 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
991 int id = uniqueCellIds[i];
992 BrowserAccessibility* cell =
993 browserAccessibility_->manager()->GetFromID(id);
995 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1000 - (NSArray*)visibleChildren {
1001 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1002 uint32 childCount = browserAccessibility_->PlatformChildCount();
1003 for (uint32 index = 0; index < childCount; ++index) {
1004 BrowserAccessibilityCocoa* child =
1005 browserAccessibility_->PlatformGetChild(index)->
1006 ToBrowserAccessibilityCocoa();
1007 [ret addObject:child];
1012 - (NSArray*)visibleColumns {
1013 return [self columns];
1016 - (NSArray*)visibleRows {
1020 - (NSNumber*)visited {
1021 return [NSNumber numberWithBool:
1022 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1026 if (!browserAccessibility_)
1029 BrowserAccessibilityManagerMac* manager =
1030 static_cast<BrowserAccessibilityManagerMac*>(
1031 browserAccessibility_->manager());
1032 if (!manager || !manager->parent_view())
1035 return [manager->parent_view() window];
1038 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1039 return [attributeToMethodNameMap objectForKey:attribute];
1042 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
1043 children_.swap(*other);
1046 // Returns the accessibility value for the given attribute. If the value isn't
1047 // supported this will return nil.
1048 - (id)accessibilityAttributeValue:(NSString*)attribute {
1049 if (!browserAccessibility_)
1053 NSSelectorFromString([self methodNameForAttribute:attribute]);
1055 return [self performSelector:selector];
1057 // TODO(dtseng): refactor remaining attributes.
1058 int selStart, selEnd;
1059 if (browserAccessibility_->GetIntAttribute(
1060 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1061 browserAccessibility_->
1062 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1063 if (selStart > selEnd)
1064 std::swap(selStart, selEnd);
1065 int selLength = selEnd - selStart;
1066 if ([attribute isEqualToString:
1067 NSAccessibilityInsertionPointLineNumberAttribute]) {
1068 const std::vector<int32>& line_breaks =
1069 browserAccessibility_->GetIntListAttribute(
1070 ui::AX_ATTR_LINE_BREAKS);
1071 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1072 if (line_breaks[i] > selStart)
1073 return [NSNumber numberWithInt:i];
1075 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1077 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1078 std::string value = browserAccessibility_->GetStringAttribute(
1080 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
1082 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1083 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1089 // Returns the accessibility value for the given attribute and parameter. If the
1090 // value isn't supported this will return nil.
1091 - (id)accessibilityAttributeValue:(NSString*)attribute
1092 forParameter:(id)parameter {
1093 if (!browserAccessibility_)
1096 const std::vector<int32>& line_breaks =
1097 browserAccessibility_->GetIntListAttribute(
1098 ui::AX_ATTR_LINE_BREAKS);
1099 std::string value = browserAccessibility_->GetStringAttribute(
1101 int len = static_cast<int>(value.size());
1103 if ([attribute isEqualToString:
1104 NSAccessibilityStringForRangeParameterizedAttribute]) {
1105 NSRange range = [(NSValue*)parameter rangeValue];
1106 std::string value = browserAccessibility_->GetStringAttribute(
1108 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
1111 if ([attribute isEqualToString:
1112 NSAccessibilityLineForIndexParameterizedAttribute]) {
1113 int index = [(NSNumber*)parameter intValue];
1114 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1115 if (line_breaks[i] > index)
1116 return [NSNumber numberWithInt:i];
1118 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1121 if ([attribute isEqualToString:
1122 NSAccessibilityRangeForLineParameterizedAttribute]) {
1123 int line_index = [(NSNumber*)parameter intValue];
1124 int line_count = static_cast<int>(line_breaks.size()) + 1;
1125 if (line_index < 0 || line_index >= line_count)
1127 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1128 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1129 return [NSValue valueWithRange:
1130 NSMakeRange(start, end - start)];
1133 if ([attribute isEqualToString:
1134 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1135 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1136 [self internalRole] != ui::AX_ROLE_GRID) {
1139 if (![parameter isKindOfClass:[NSArray self]])
1141 NSArray* array = parameter;
1142 int column = [[array objectAtIndex:0] intValue];
1143 int row = [[array objectAtIndex:1] intValue];
1144 int num_columns = browserAccessibility_->GetIntAttribute(
1145 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1146 int num_rows = browserAccessibility_->GetIntAttribute(
1147 ui::AX_ATTR_TABLE_ROW_COUNT);
1148 if (column < 0 || column >= num_columns ||
1149 row < 0 || row >= num_rows) {
1153 i < browserAccessibility_->PlatformChildCount();
1155 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1156 if (child->GetRole() != ui::AX_ROLE_ROW)
1159 if (!child->GetIntAttribute(
1160 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1168 j < child->PlatformChildCount();
1170 BrowserAccessibility* cell = child->PlatformGetChild(j);
1171 if (!cell->IsCellOrTableHeaderRole())
1174 if (!cell->GetIntAttribute(
1175 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1179 if (colIndex == column)
1180 return cell->ToBrowserAccessibilityCocoa();
1181 if (colIndex > column)
1188 if ([attribute isEqualToString:
1189 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1190 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1192 NSRange range = [(NSValue*)parameter rangeValue];
1193 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1194 range.location, range.length);
1195 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1196 NSSize size = NSMakeSize(rect.width(), rect.height());
1197 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1198 NSRect nsrect = NSMakeRect(
1199 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1200 return [NSValue valueWithRect:nsrect];
1203 // TODO(dtseng): support the following attributes.
1204 if ([attribute isEqualTo:
1205 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1206 [attribute isEqualTo:
1207 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1208 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1209 [attribute isEqualTo:
1210 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1216 // Returns an array of parameterized attributes names that this object will
1218 - (NSArray*)accessibilityParameterizedAttributeNames {
1219 if (!browserAccessibility_)
1222 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1223 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1224 return [NSArray arrayWithObjects:
1225 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1228 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole]) {
1229 return [NSArray arrayWithObjects:
1230 NSAccessibilityLineForIndexParameterizedAttribute,
1231 NSAccessibilityRangeForLineParameterizedAttribute,
1232 NSAccessibilityStringForRangeParameterizedAttribute,
1233 NSAccessibilityRangeForPositionParameterizedAttribute,
1234 NSAccessibilityRangeForIndexParameterizedAttribute,
1235 NSAccessibilityBoundsForRangeParameterizedAttribute,
1236 NSAccessibilityRTFForRangeParameterizedAttribute,
1237 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1238 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1241 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1242 return [NSArray arrayWithObjects:
1243 NSAccessibilityBoundsForRangeParameterizedAttribute,
1249 // Returns an array of action names that this object will respond to.
1250 - (NSArray*)accessibilityActionNames {
1251 if (!browserAccessibility_)
1254 NSMutableArray* ret =
1255 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1256 NSString* role = [self role];
1257 // TODO(dtseng): this should only get set when there's a default action.
1258 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1259 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1260 [ret addObject:NSAccessibilityPressAction];
1266 // Returns a sub-array of values for the given attribute value, starting at
1267 // index, with up to maxCount items. If the given index is out of bounds,
1268 // or there are no values for the given attribute, it will return nil.
1269 // This method is used for querying subsets of values, without having to
1270 // return a large set of data, such as elements with a large number of
1272 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1273 index:(NSUInteger)index
1274 maxCount:(NSUInteger)maxCount {
1275 if (!browserAccessibility_)
1278 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1281 NSUInteger arrayCount = [fullArray count];
1282 if (index >= arrayCount)
1285 if ((index + maxCount) > arrayCount) {
1286 subRange = NSMakeRange(index, arrayCount - index);
1288 subRange = NSMakeRange(index, maxCount);
1290 return [fullArray subarrayWithRange:subRange];
1293 // Returns the count of the specified accessibility array attribute.
1294 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1295 if (!browserAccessibility_)
1298 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1299 return [fullArray count];
1302 // Returns the list of accessibility attributes that this object supports.
1303 - (NSArray*)accessibilityAttributeNames {
1304 if (!browserAccessibility_)
1307 // General attributes.
1308 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1309 NSAccessibilityChildrenAttribute,
1310 NSAccessibilityDescriptionAttribute,
1311 NSAccessibilityEnabledAttribute,
1312 NSAccessibilityFocusedAttribute,
1313 NSAccessibilityHelpAttribute,
1314 NSAccessibilityLinkedUIElementsAttribute,
1315 NSAccessibilityParentAttribute,
1316 NSAccessibilityPositionAttribute,
1317 NSAccessibilityRoleAttribute,
1318 NSAccessibilityRoleDescriptionAttribute,
1319 NSAccessibilitySizeAttribute,
1320 NSAccessibilitySubroleAttribute,
1321 NSAccessibilityTitleAttribute,
1322 NSAccessibilityTopLevelUIElementAttribute,
1323 NSAccessibilityValueAttribute,
1324 NSAccessibilityWindowAttribute,
1330 // Specific role attributes.
1331 NSString* role = [self role];
1332 NSString* subrole = [self subrole];
1333 if ([role isEqualToString:NSAccessibilityTableRole] ||
1334 [role isEqualToString:NSAccessibilityGridRole]) {
1335 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1336 NSAccessibilityColumnsAttribute,
1337 NSAccessibilityVisibleColumnsAttribute,
1338 NSAccessibilityRowsAttribute,
1339 NSAccessibilityVisibleRowsAttribute,
1340 NSAccessibilityVisibleCellsAttribute,
1341 NSAccessibilityHeaderAttribute,
1342 NSAccessibilityColumnHeaderUIElementsAttribute,
1343 NSAccessibilityRowHeaderUIElementsAttribute,
1345 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1346 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1347 NSAccessibilityIndexAttribute,
1348 NSAccessibilityHeaderAttribute,
1349 NSAccessibilityRowsAttribute,
1350 NSAccessibilityVisibleRowsAttribute,
1352 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1353 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1354 NSAccessibilityColumnIndexRangeAttribute,
1355 NSAccessibilityRowIndexRangeAttribute,
1358 } else if ([role isEqualToString:@"AXWebArea"]) {
1359 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1361 @"AXLoadingProgress",
1363 } else if ([role isEqualToString:NSAccessibilityTextFieldRole]) {
1364 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1365 NSAccessibilityInsertionPointLineNumberAttribute,
1366 NSAccessibilityNumberOfCharactersAttribute,
1367 NSAccessibilitySelectedTextAttribute,
1368 NSAccessibilitySelectedTextRangeAttribute,
1369 NSAccessibilityVisibleCharacterRangeAttribute,
1371 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1372 [ret addObject:NSAccessibilityTabsAttribute];
1373 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1374 [role isEqualToString:NSAccessibilitySliderRole] ||
1375 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1376 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1377 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1378 NSAccessibilityMaxValueAttribute,
1379 NSAccessibilityMinValueAttribute,
1380 NSAccessibilityValueDescriptionAttribute,
1382 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1383 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1384 NSAccessibilityDisclosingAttribute,
1385 NSAccessibilityDisclosedByRowAttribute,
1386 NSAccessibilityDisclosureLevelAttribute,
1387 NSAccessibilityDisclosedRowsAttribute,
1389 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1390 if (browserAccessibility_->GetParent()) {
1391 base::string16 parentRole;
1392 browserAccessibility_->GetParent()->GetHtmlAttribute(
1393 "role", &parentRole);
1394 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1395 if (parentRole == treegridRole) {
1396 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1397 NSAccessibilityDisclosingAttribute,
1398 NSAccessibilityDisclosedByRowAttribute,
1399 NSAccessibilityDisclosureLevelAttribute,
1400 NSAccessibilityDisclosedRowsAttribute,
1403 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1404 NSAccessibilityIndexAttribute,
1408 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1409 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1410 NSAccessibilitySelectedChildrenAttribute,
1411 NSAccessibilityVisibleChildrenAttribute,
1415 // Add the url attribute only if it has a valid url.
1416 if ([self url] != nil) {
1417 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1418 NSAccessibilityURLAttribute,
1422 // Position in set and Set size
1423 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_POS_IN_SET)) {
1424 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1428 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_SET_SIZE)) {
1429 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1435 if (browserAccessibility_->HasStringAttribute(
1436 ui::AX_ATTR_LIVE_STATUS)) {
1437 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1441 if (browserAccessibility_->HasStringAttribute(
1442 ui::AX_ATTR_LIVE_RELEVANT)) {
1443 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1447 if (browserAccessibility_->HasBoolAttribute(
1448 ui::AX_ATTR_LIVE_ATOMIC)) {
1449 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1453 if (browserAccessibility_->HasBoolAttribute(
1454 ui::AX_ATTR_LIVE_BUSY)) {
1455 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1460 if (browserAccessibility_->HasStringAttribute(
1461 ui::AX_ATTR_DROPEFFECT)) {
1462 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1467 // Add aria-grabbed attribute only if it has true.
1468 if (browserAccessibility_->HasBoolAttribute(ui::AX_ATTR_GRABBED)) {
1469 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1474 // Add expanded attribute only if it has expanded or collapsed state.
1475 if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) ||
1476 GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) {
1477 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1478 NSAccessibilityExpandedAttribute,
1482 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)
1483 || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) {
1484 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1485 NSAccessibilityOrientationAttribute, nil]];
1488 if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) {
1489 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1490 @"AXPlaceholder", nil]];
1493 if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) {
1494 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1495 @"AXRequired", nil]];
1498 // Title UI Element.
1499 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1500 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1501 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1503 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1504 NSAccessibilityTitleUIElementAttribute,
1507 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1508 // for elements which are referred to by labelledby or are labels
1513 // Returns the index of the child in this objects array of children.
1514 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1515 if (!browserAccessibility_)
1518 NSUInteger index = 0;
1519 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1520 if ([child isEqual:childToCheck])
1527 // Returns whether or not the specified attribute can be set by the
1528 // accessibility API via |accessibilitySetValue:forAttribute:|.
1529 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1530 if (!browserAccessibility_)
1533 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1534 return GetState(browserAccessibility_,
1535 ui::AX_STATE_FOCUSABLE);
1536 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1537 return browserAccessibility_->GetBoolAttribute(
1538 ui::AX_ATTR_CAN_SET_VALUE);
1540 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1541 ([[self role] isEqualToString:NSAccessibilityTextFieldRole]))
1547 // Returns whether or not this object should be ignored in the accessibility
1549 - (BOOL)accessibilityIsIgnored {
1550 if (!browserAccessibility_)
1553 return [self isIgnored];
1556 // Performs the given accessibility action on the webkit accessibility object
1557 // that backs this object.
1558 - (void)accessibilityPerformAction:(NSString*)action {
1559 if (!browserAccessibility_)
1562 // TODO(dmazzoni): Support more actions.
1563 if ([action isEqualToString:NSAccessibilityPressAction]) {
1564 [self delegate]->AccessibilityDoDefaultAction(
1565 browserAccessibility_->GetId());
1566 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1567 NSPoint objOrigin = [self origin];
1568 NSSize size = [[self size] sizeValue];
1569 gfx::Point origin = [self delegate]->AccessibilityOriginInScreen(
1570 gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
1571 origin.Offset(size.width / 2, size.height / 2);
1572 [self delegate]->AccessibilityShowMenu(origin);
1576 // Returns the description of the given action.
1577 - (NSString*)accessibilityActionDescription:(NSString*)action {
1578 if (!browserAccessibility_)
1581 return NSAccessibilityActionDescription(action);
1584 // Sets an override value for a specific accessibility attribute.
1585 // This class does not support this.
1586 - (BOOL)accessibilitySetOverrideValue:(id)value
1587 forAttribute:(NSString*)attribute {
1591 // Sets the value for an accessibility attribute via the accessibility API.
1592 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1593 if (!browserAccessibility_)
1596 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1597 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1598 NSNumber* focusedNumber = value;
1599 BOOL focused = [focusedNumber intValue];
1601 manager->SetFocus(browserAccessibility_, true);
1603 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1604 NSRange range = [(NSValue*)value rangeValue];
1605 [self delegate]->AccessibilitySetTextSelection(
1606 browserAccessibility_->GetId(),
1607 range.location, range.location + range.length);
1611 // Returns the deepest accessibility child that should not be ignored.
1612 // It is assumed that the hit test has been narrowed down to this object
1613 // or one of its children, so this will never return nil unless this
1614 // object is invalid.
1615 - (id)accessibilityHitTest:(NSPoint)point {
1616 if (!browserAccessibility_)
1619 BrowserAccessibilityCocoa* hit = self;
1620 for (BrowserAccessibilityCocoa* child in [self children]) {
1621 if (!child->browserAccessibility_)
1623 NSPoint origin = [child origin];
1624 NSSize size = [[child size] sizeValue];
1626 rect.origin = origin;
1628 if (NSPointInRect(point, rect)) {
1630 id childResult = [child accessibilityHitTest:point];
1631 if (![childResult accessibilityIsIgnored]) {
1637 return NSAccessibilityUnignoredAncestor(hit);
1640 - (BOOL)isEqual:(id)object {
1641 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1643 return ([self hash] == [object hash]);
1646 - (NSUInteger)hash {
1647 // Potentially called during dealloc.
1648 if (!browserAccessibility_)
1649 return [super hash];
1650 return browserAccessibility_->GetId();
1653 - (BOOL)accessibilityShouldUseUniqueId {