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::BrowserAccessibilityManager;
29 using content::BrowserAccessibilityManagerMac;
30 using content::ContentClient;
31 typedef ui::AXStringAttribute StringAttribute;
35 // Returns an autoreleased copy of the AXNodeData's attribute.
36 NSString* NSStringForStringAttribute(
37 BrowserAccessibility* browserAccessibility,
38 StringAttribute attribute) {
39 return base::SysUTF8ToNSString(
40 browserAccessibility->GetStringAttribute(attribute));
43 // GetState checks the bitmask used in AXNodeData to check
44 // if the given state was set on the accessibility object.
45 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
46 return ((accessibility->GetState() >> state) & 1);
49 // A mapping from an accessibility attribute to its method name.
50 NSDictionary* attributeToMethodNameMap = nil;
54 @implementation BrowserAccessibilityCocoa
60 } attributeToMethodNameContainer[] = {
61 { NSAccessibilityChildrenAttribute, @"children" },
62 { NSAccessibilityColumnsAttribute, @"columns" },
63 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
64 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
65 { NSAccessibilityContentsAttribute, @"contents" },
66 { NSAccessibilityDescriptionAttribute, @"description" },
67 { NSAccessibilityDisclosingAttribute, @"disclosing" },
68 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
69 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
70 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
71 { NSAccessibilityEnabledAttribute, @"enabled" },
72 { NSAccessibilityFocusedAttribute, @"focused" },
73 { NSAccessibilityHeaderAttribute, @"header" },
74 { NSAccessibilityHelpAttribute, @"help" },
75 { NSAccessibilityIndexAttribute, @"index" },
76 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
77 { NSAccessibilityMaxValueAttribute, @"maxValue" },
78 { NSAccessibilityMinValueAttribute, @"minValue" },
79 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
80 { NSAccessibilityOrientationAttribute, @"orientation" },
81 { NSAccessibilityParentAttribute, @"parent" },
82 { NSAccessibilityPositionAttribute, @"position" },
83 { NSAccessibilityRoleAttribute, @"role" },
84 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
85 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
86 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
87 { NSAccessibilityRowsAttribute, @"rows" },
88 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
89 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
90 { NSAccessibilitySizeAttribute, @"size" },
91 { NSAccessibilitySubroleAttribute, @"subrole" },
92 { NSAccessibilityTabsAttribute, @"tabs" },
93 { NSAccessibilityTitleAttribute, @"title" },
94 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
95 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
96 { NSAccessibilityURLAttribute, @"url" },
97 { NSAccessibilityValueAttribute, @"value" },
98 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
99 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
100 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
101 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
102 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
103 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
104 { NSAccessibilityWindowAttribute, @"window" },
105 { @"AXAccessKey", @"accessKey" },
106 { @"AXARIAAtomic", @"ariaAtomic" },
107 { @"AXARIABusy", @"ariaBusy" },
108 { @"AXARIALive", @"ariaLive" },
109 { @"AXARIARelevant", @"ariaRelevant" },
110 { @"AXInvalid", @"invalid" },
111 { @"AXLoaded", @"loaded" },
112 { @"AXLoadingProgress", @"loadingProgress" },
113 { @"AXRequired", @"required" },
114 { @"AXVisited", @"visited" },
117 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
118 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
119 sizeof(attributeToMethodNameContainer[0]);
120 for (size_t i = 0; i < numAttributes; ++i) {
121 [dict setObject:attributeToMethodNameContainer[i].methodName
122 forKey:attributeToMethodNameContainer[i].attribute];
124 attributeToMethodNameMap = dict;
128 - (id)initWithObject:(BrowserAccessibility*)accessibility {
129 if ((self = [super init]))
130 browserAccessibility_ = accessibility;
135 if (browserAccessibility_) {
136 NSAccessibilityUnregisterUniqueIdForUIElement(self);
137 browserAccessibility_ = NULL;
141 - (NSString*)accessKey {
142 return NSStringForStringAttribute(
143 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
146 - (NSNumber*)ariaAtomic {
147 bool boolValue = browserAccessibility_->GetBoolAttribute(
148 ui::AX_ATTR_LIVE_ATOMIC);
149 return [NSNumber numberWithBool:boolValue];
152 - (NSNumber*)ariaBusy {
153 bool boolValue = browserAccessibility_->GetBoolAttribute(
154 ui::AX_ATTR_LIVE_BUSY);
155 return [NSNumber numberWithBool:boolValue];
158 - (NSString*)ariaLive {
159 return NSStringForStringAttribute(
160 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
163 - (NSString*)ariaRelevant {
164 return NSStringForStringAttribute(
165 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
168 // Returns an array of BrowserAccessibilityCocoa objects, representing the
169 // accessibility children of this object.
170 - (NSArray*)children {
172 uint32 childCount = browserAccessibility_->PlatformChildCount();
173 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
174 for (uint32 index = 0; index < childCount; ++index) {
175 BrowserAccessibilityCocoa* child =
176 browserAccessibility_->PlatformGetChild(index)->
177 ToBrowserAccessibilityCocoa();
178 if ([child isIgnored])
179 [children_ addObjectsFromArray:[child children]];
181 [children_ addObject:child];
184 // Also, add indirect children (if any).
185 const std::vector<int32>& indirectChildIds =
186 browserAccessibility_->GetIntListAttribute(
187 ui::AX_ATTR_INDIRECT_CHILD_IDS);
188 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
189 int32 child_id = indirectChildIds[i];
190 BrowserAccessibility* child =
191 browserAccessibility_->manager()->GetFromID(child_id);
193 // This only became necessary as a result of crbug.com/93095. It should be
194 // a DCHECK in the future.
196 BrowserAccessibilityCocoa* child_cocoa =
197 child->ToBrowserAccessibilityCocoa();
198 [children_ addObject:child_cocoa];
205 - (void)childrenChanged {
206 if (![self isIgnored]) {
209 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
214 - (NSArray*)columnHeaders {
215 if ([self internalRole] != ui::AX_ROLE_TABLE &&
216 [self internalRole] != ui::AX_ROLE_GRID) {
220 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
221 const std::vector<int32>& uniqueCellIds =
222 browserAccessibility_->GetIntListAttribute(
223 ui::AX_ATTR_UNIQUE_CELL_IDS);
224 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
225 int id = uniqueCellIds[i];
226 BrowserAccessibility* cell =
227 browserAccessibility_->manager()->GetFromID(id);
228 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
229 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
234 - (NSValue*)columnIndexRange {
235 if ([self internalRole] != ui::AX_ROLE_CELL)
240 browserAccessibility_->GetIntAttribute(
241 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
242 browserAccessibility_->GetIntAttribute(
243 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
244 if (column >= 0 && colspan >= 1)
245 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
249 - (NSArray*)columns {
250 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
251 for (BrowserAccessibilityCocoa* child in [self children]) {
252 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
253 [ret addObject:child];
258 - (NSString*)description {
259 std::string description;
260 if (browserAccessibility_->GetStringAttribute(
261 ui::AX_ATTR_DESCRIPTION, &description)) {
262 return base::SysUTF8ToNSString(description);
265 // If the role is anything other than an image, or if there's
266 // a title or title UI element, just return an empty string.
267 if (![[self role] isEqualToString:NSAccessibilityImageRole])
269 if (browserAccessibility_->HasStringAttribute(
273 if ([self titleUIElement])
276 // The remaining case is an image where there's no other title.
277 // Return the base part of the filename as the description.
279 if (browserAccessibility_->GetStringAttribute(
280 ui::AX_ATTR_URL, &url)) {
281 // Given a url like http://foo.com/bar/baz.png, just return the
282 // base name, e.g., "baz.png".
283 size_t leftIndex = url.rfind('/');
284 std::string basename =
285 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
286 return base::SysUTF8ToNSString(basename);
292 - (NSNumber*)disclosing {
293 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
294 return [NSNumber numberWithBool:
295 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
301 - (id)disclosedByRow {
302 // The row that contains this row.
303 // It should be the same as the first parent that is a treeitem.
307 - (NSNumber*)disclosureLevel {
308 ui::AXRole role = [self internalRole];
309 if (role == ui::AX_ROLE_ROW ||
310 role == ui::AX_ROLE_TREE_ITEM) {
311 int level = browserAccessibility_->GetIntAttribute(
312 ui::AX_ATTR_HIERARCHICAL_LEVEL);
313 // Mac disclosureLevel is 0-based, but web levels are 1-based.
316 return [NSNumber numberWithInt:level];
322 - (id)disclosedRows {
323 // The rows that are considered inside this row.
327 - (NSNumber*)enabled {
328 return [NSNumber numberWithBool:
329 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
332 - (NSNumber*)focused {
333 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
334 NSNumber* ret = [NSNumber numberWithBool:
335 manager->GetFocus(NULL) == browserAccessibility_];
340 int headerElementId = -1;
341 if ([self internalRole] == ui::AX_ROLE_TABLE ||
342 [self internalRole] == ui::AX_ROLE_GRID) {
343 browserAccessibility_->GetIntAttribute(
344 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
345 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
346 browserAccessibility_->GetIntAttribute(
347 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
348 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
349 browserAccessibility_->GetIntAttribute(
350 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
353 if (headerElementId > 0) {
354 BrowserAccessibility* headerObject =
355 browserAccessibility_->manager()->GetFromID(headerElementId);
357 return headerObject->ToBrowserAccessibilityCocoa();
363 return NSStringForStringAttribute(
364 browserAccessibility_, ui::AX_ATTR_HELP);
368 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
369 int columnIndex = browserAccessibility_->GetIntAttribute(
370 ui::AX_ATTR_TABLE_COLUMN_INDEX);
371 return [NSNumber numberWithInt:columnIndex];
372 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
373 int rowIndex = browserAccessibility_->GetIntAttribute(
374 ui::AX_ATTR_TABLE_ROW_INDEX);
375 return [NSNumber numberWithInt:rowIndex];
381 // Returns whether or not this node should be ignored in the
382 // accessibility tree.
384 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
387 - (NSString*)invalid {
388 base::string16 invalidUTF;
389 if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
391 NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
392 if ([invalid isEqualToString:@"false"] ||
393 [invalid isEqualToString:@""]) {
399 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
400 addTo:(NSMutableArray*)outArray {
401 const std::vector<int32>& attributeValues =
402 browserAccessibility_->GetIntListAttribute(attribute);
403 for (size_t i = 0; i < attributeValues.size(); ++i) {
404 BrowserAccessibility* element =
405 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
407 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
411 - (NSArray*)linkedUIElements {
412 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
413 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
414 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
415 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
416 if ([ret count] == 0)
421 - (NSNumber*)loaded {
422 return [NSNumber numberWithBool:YES];
425 - (NSNumber*)loadingProgress {
426 float floatValue = browserAccessibility_->GetFloatAttribute(
427 ui::AX_ATTR_DOC_LOADING_PROGRESS);
428 return [NSNumber numberWithFloat:floatValue];
431 - (NSNumber*)maxValue {
432 float floatValue = browserAccessibility_->GetFloatAttribute(
433 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
434 return [NSNumber numberWithFloat:floatValue];
437 - (NSNumber*)minValue {
438 float floatValue = browserAccessibility_->GetFloatAttribute(
439 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
440 return [NSNumber numberWithFloat:floatValue];
443 - (NSString*)orientation {
444 // We present a spin button as a vertical slider, with a role description
446 if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
447 return NSAccessibilityVerticalOrientationValue;
449 if ([self internalRole] == ui::AX_ROLE_LIST ||
450 [self internalRole] == ui::AX_ROLE_LIST_BOX) {
451 return NSAccessibilityVerticalOrientationValue;
454 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
455 return NSAccessibilityVerticalOrientationValue;
457 return NSAccessibilityHorizontalOrientationValue;
460 - (NSNumber*)numberOfCharacters {
461 return [NSNumber numberWithInt:browserAccessibility_->value().length()];
464 // The origin of this accessibility object in the page's document.
465 // This is relative to webkit's top-left origin, not Cocoa's
466 // bottom-left origin.
468 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
469 return NSMakePoint(bounds.x(), bounds.y());
473 // A nil parent means we're the root.
474 if (browserAccessibility_->GetParent()) {
475 return NSAccessibilityUnignoredAncestor(
476 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
478 // Hook back up to RenderWidgetHostViewCocoa.
479 BrowserAccessibilityManagerMac* manager =
480 static_cast<BrowserAccessibilityManagerMac*>(
481 browserAccessibility_->manager());
482 return manager->parent_view();
486 - (NSValue*)position {
487 NSPoint origin = [self origin];
488 NSSize size = [[self size] sizeValue];
489 NSPoint pointInScreen = [self pointInScreen:origin size:size];
490 return [NSValue valueWithPoint:pointInScreen];
493 - (NSNumber*)required {
494 return [NSNumber numberWithBool:
495 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
498 // Returns an enum indicating the role from browserAccessibility_.
499 - (ui::AXRole)internalRole {
500 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
503 - (content::BrowserAccessibilityDelegate*)delegate {
504 return browserAccessibility_->manager() ?
505 browserAccessibility_->manager()->delegate() :
509 - (NSPoint)pointInScreen:(NSPoint)origin
511 if (!browserAccessibility_)
514 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
515 gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
516 return NSMakePoint(point.x(), point.y());
519 // Returns a string indicating the NSAccessibility role of this object.
521 ui::AXRole role = [self internalRole];
522 if (role == ui::AX_ROLE_CANVAS &&
523 browserAccessibility_->GetBoolAttribute(
524 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
525 return NSAccessibilityGroupRole;
527 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
528 bool isAriaPressedDefined;
530 browserAccessibility_->GetAriaTristate("aria-pressed",
531 &isAriaPressedDefined,
533 if (isAriaPressedDefined)
534 return NSAccessibilityCheckBoxRole;
536 return NSAccessibilityButtonRole;
538 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
541 // Returns a string indicating the role description of this object.
542 - (NSString*)roleDescription {
543 NSString* role = [self role];
545 ContentClient* content_client = content::GetContentClient();
547 // The following descriptions are specific to webkit.
548 if ([role isEqualToString:@"AXWebArea"]) {
549 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
550 IDS_AX_ROLE_WEB_AREA));
553 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
554 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
558 if ([role isEqualToString:@"AXHeading"]) {
559 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
560 IDS_AX_ROLE_HEADING));
563 if ([role isEqualToString:NSAccessibilityGroupRole] ||
564 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
566 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
567 ui::AXRole internalRole = [self internalRole];
568 if ((internalRole != ui::AX_ROLE_GROUP &&
569 internalRole != ui::AX_ROLE_LIST_ITEM) ||
570 internalRole == ui::AX_ROLE_TAB) {
571 // TODO(dtseng): This is not localized; see crbug/84814.
572 return base::SysUTF8ToNSString(role);
577 switch([self internalRole]) {
578 case ui::AX_ROLE_FOOTER:
579 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
580 IDS_AX_ROLE_FOOTER));
581 case ui::AX_ROLE_SPIN_BUTTON:
582 // This control is similar to what VoiceOver calls a "stepper".
583 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
584 IDS_AX_ROLE_STEPPER));
585 case ui::AX_ROLE_TOGGLE_BUTTON:
586 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
587 IDS_AX_ROLE_TOGGLE_BUTTON));
592 return NSAccessibilityRoleDescription(role, nil);
595 - (NSArray*)rowHeaders {
596 if ([self internalRole] != ui::AX_ROLE_TABLE &&
597 [self internalRole] != ui::AX_ROLE_GRID) {
601 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
602 const std::vector<int32>& uniqueCellIds =
603 browserAccessibility_->GetIntListAttribute(
604 ui::AX_ATTR_UNIQUE_CELL_IDS);
605 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
606 int id = uniqueCellIds[i];
607 BrowserAccessibility* cell =
608 browserAccessibility_->manager()->GetFromID(id);
609 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
610 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
615 - (NSValue*)rowIndexRange {
616 if ([self internalRole] != ui::AX_ROLE_CELL)
621 browserAccessibility_->GetIntAttribute(
622 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
623 browserAccessibility_->GetIntAttribute(
624 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
625 if (row >= 0 && rowspan >= 1)
626 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
631 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
633 if ([self internalRole] == ui::AX_ROLE_TABLE||
634 [self internalRole] == ui::AX_ROLE_GRID) {
635 for (BrowserAccessibilityCocoa* child in [self children]) {
636 if ([[child role] isEqualToString:NSAccessibilityRowRole])
637 [ret addObject:child];
639 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
640 const std::vector<int32>& indirectChildIds =
641 browserAccessibility_->GetIntListAttribute(
642 ui::AX_ATTR_INDIRECT_CHILD_IDS);
643 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
644 int id = indirectChildIds[i];
645 BrowserAccessibility* rowElement =
646 browserAccessibility_->manager()->GetFromID(id);
648 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
655 - (NSArray*)selectedChildren {
656 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
658 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
659 BrowserAccessibility* focusedChild =
660 manager->GetFocus(browserAccessibility_);
661 if (focusedChild && focusedChild != browserAccessibility_) {
662 // First try the focused child.
663 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
665 // Next try the active descendant.
666 int activeDescendantId;
667 if (browserAccessibility_->GetIntAttribute(
668 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
669 BrowserAccessibility* activeDescendant =
670 manager->GetFromID(activeDescendantId);
671 if (activeDescendant)
672 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
674 // Otherwise return any children with the "selected" state, which
675 // may come from aria-selected.
676 uint32 childCount = browserAccessibility_->PlatformChildCount();
677 for (uint32 index = 0; index < childCount; ++index) {
678 BrowserAccessibility* child =
679 browserAccessibility_->PlatformGetChild(index);
680 if (child->HasState(ui::AX_STATE_SELECTED))
681 [ret addObject:child->ToBrowserAccessibilityCocoa()];
689 // Returns the size of this object.
691 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
692 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
695 // Returns a subrole based upon the role.
696 - (NSString*) subrole {
697 ui::AXRole browserAccessibilityRole = [self internalRole];
698 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
699 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
700 return @"AXSecureTextField";
703 NSString* htmlTag = NSStringForStringAttribute(
704 browserAccessibility_, ui::AX_ATTR_HTML_TAG);
706 if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
707 if ([htmlTag isEqualToString:@"dl"]) {
708 return @"AXDescriptionList";
710 return @"AXContentList";
714 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
717 // Returns all tabs in this subtree.
719 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
721 if ([self internalRole] == ui::AX_ROLE_TAB)
722 [tabSubtree addObject:self];
724 for (uint i=0; i < [[self children] count]; ++i) {
725 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
726 if ([tabChildren count] > 0)
727 [tabSubtree addObjectsFromArray:tabChildren];
734 return NSStringForStringAttribute(
735 browserAccessibility_, ui::AX_ATTR_NAME);
738 - (id)titleUIElement {
740 if (browserAccessibility_->GetIntAttribute(
741 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
742 BrowserAccessibility* titleElement =
743 browserAccessibility_->manager()->GetFromID(titleElementId);
745 return titleElement->ToBrowserAccessibilityCocoa();
747 std::vector<int32> labelledby_ids =
748 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
749 if (labelledby_ids.size() == 1) {
750 BrowserAccessibility* titleElement =
751 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
753 return titleElement->ToBrowserAccessibilityCocoa();
760 StringAttribute urlAttribute =
761 [[self role] isEqualToString:@"AXWebArea"] ?
762 ui::AX_ATTR_DOC_URL :
765 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
769 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
773 // WebCore uses an attachmentView to get the below behavior.
774 // We do not have any native views backing this object, so need
775 // to approximate Cocoa ax behavior best as we can.
776 NSString* role = [self role];
777 if ([role isEqualToString:@"AXHeading"]) {
779 if (browserAccessibility_->GetIntAttribute(
780 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
781 return [NSNumber numberWithInt:level];
783 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
784 // AXValue does not make sense for pure buttons.
786 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
788 bool isAriaPressedDefined;
790 value = browserAccessibility_->GetAriaTristate(
791 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
796 return [NSNumber numberWithInt:value];
798 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
799 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
802 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
804 browserAccessibility_, ui::AX_STATE_SELECTED) ?
808 if (browserAccessibility_->GetBoolAttribute(
809 ui::AX_ATTR_BUTTON_MIXED)) {
812 return [NSNumber numberWithInt:value];
813 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
814 [role isEqualToString:NSAccessibilitySliderRole] ||
815 [role isEqualToString:NSAccessibilityScrollBarRole]) {
817 if (browserAccessibility_->GetFloatAttribute(
818 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
819 return [NSNumber numberWithFloat:floatValue];
821 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
822 int r = browserAccessibility_->GetIntAttribute(
823 ui::AX_ATTR_COLOR_VALUE_RED);
824 int g = browserAccessibility_->GetIntAttribute(
825 ui::AX_ATTR_COLOR_VALUE_GREEN);
826 int b = browserAccessibility_->GetIntAttribute(
827 ui::AX_ATTR_COLOR_VALUE_BLUE);
828 // This string matches the one returned by a native Mac color well.
829 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
830 r / 255., g / 255., b / 255.];
833 return NSStringForStringAttribute(
834 browserAccessibility_, ui::AX_ATTR_VALUE);
837 - (NSString*)valueDescription {
838 return NSStringForStringAttribute(
839 browserAccessibility_, ui::AX_ATTR_VALUE);
842 - (NSValue*)visibleCharacterRange {
843 return [NSValue valueWithRange:
844 NSMakeRange(0, browserAccessibility_->value().length())];
847 - (NSArray*)visibleCells {
848 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
849 const std::vector<int32>& uniqueCellIds =
850 browserAccessibility_->GetIntListAttribute(
851 ui::AX_ATTR_UNIQUE_CELL_IDS);
852 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
853 int id = uniqueCellIds[i];
854 BrowserAccessibility* cell =
855 browserAccessibility_->manager()->GetFromID(id);
857 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
862 - (NSArray*)visibleChildren {
863 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
864 uint32 childCount = browserAccessibility_->PlatformChildCount();
865 for (uint32 index = 0; index < childCount; ++index) {
866 BrowserAccessibilityCocoa* child =
867 browserAccessibility_->PlatformGetChild(index)->
868 ToBrowserAccessibilityCocoa();
869 [ret addObject:child];
874 - (NSArray*)visibleColumns {
875 return [self columns];
878 - (NSArray*)visibleRows {
882 - (NSNumber*)visited {
883 return [NSNumber numberWithBool:
884 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
888 if (!browserAccessibility_)
891 BrowserAccessibilityManagerMac* manager =
892 static_cast<BrowserAccessibilityManagerMac*>(
893 browserAccessibility_->manager());
894 return [manager->parent_view() window];
897 - (NSString*)methodNameForAttribute:(NSString*)attribute {
898 return [attributeToMethodNameMap objectForKey:attribute];
901 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
902 children_.swap(*other);
905 // Returns the accessibility value for the given attribute. If the value isn't
906 // supported this will return nil.
907 - (id)accessibilityAttributeValue:(NSString*)attribute {
908 if (!browserAccessibility_)
912 NSSelectorFromString([self methodNameForAttribute:attribute]);
914 return [self performSelector:selector];
916 // TODO(dtseng): refactor remaining attributes.
917 int selStart, selEnd;
918 if (browserAccessibility_->GetIntAttribute(
919 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
920 browserAccessibility_->
921 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
922 if (selStart > selEnd)
923 std::swap(selStart, selEnd);
924 int selLength = selEnd - selStart;
925 if ([attribute isEqualToString:
926 NSAccessibilityInsertionPointLineNumberAttribute]) {
927 const std::vector<int32>& line_breaks =
928 browserAccessibility_->GetIntListAttribute(
929 ui::AX_ATTR_LINE_BREAKS);
930 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
931 if (line_breaks[i] > selStart)
932 return [NSNumber numberWithInt:i];
934 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
936 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
937 std::string value = browserAccessibility_->GetStringAttribute(
939 return base::SysUTF8ToNSString(value.substr(selStart, selLength));
941 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
942 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
948 // Returns the accessibility value for the given attribute and parameter. If the
949 // value isn't supported this will return nil.
950 - (id)accessibilityAttributeValue:(NSString*)attribute
951 forParameter:(id)parameter {
952 if (!browserAccessibility_)
955 const std::vector<int32>& line_breaks =
956 browserAccessibility_->GetIntListAttribute(
957 ui::AX_ATTR_LINE_BREAKS);
958 int len = static_cast<int>(browserAccessibility_->value().size());
960 if ([attribute isEqualToString:
961 NSAccessibilityStringForRangeParameterizedAttribute]) {
962 NSRange range = [(NSValue*)parameter rangeValue];
963 std::string value = browserAccessibility_->GetStringAttribute(
965 return base::SysUTF8ToNSString(value.substr(range.location, range.length));
968 if ([attribute isEqualToString:
969 NSAccessibilityLineForIndexParameterizedAttribute]) {
970 int index = [(NSNumber*)parameter intValue];
971 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
972 if (line_breaks[i] > index)
973 return [NSNumber numberWithInt:i];
975 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
978 if ([attribute isEqualToString:
979 NSAccessibilityRangeForLineParameterizedAttribute]) {
980 int line_index = [(NSNumber*)parameter intValue];
981 int line_count = static_cast<int>(line_breaks.size()) + 1;
982 if (line_index < 0 || line_index >= line_count)
984 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
985 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
986 return [NSValue valueWithRange:
987 NSMakeRange(start, end - start)];
990 if ([attribute isEqualToString:
991 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
992 if ([self internalRole] != ui::AX_ROLE_TABLE &&
993 [self internalRole] != ui::AX_ROLE_GRID) {
996 if (![parameter isKindOfClass:[NSArray self]])
998 NSArray* array = parameter;
999 int column = [[array objectAtIndex:0] intValue];
1000 int row = [[array objectAtIndex:1] intValue];
1001 int num_columns = browserAccessibility_->GetIntAttribute(
1002 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1003 int num_rows = browserAccessibility_->GetIntAttribute(
1004 ui::AX_ATTR_TABLE_ROW_COUNT);
1005 if (column < 0 || column >= num_columns ||
1006 row < 0 || row >= num_rows) {
1010 i < browserAccessibility_->PlatformChildCount();
1012 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1013 if (child->GetRole() != ui::AX_ROLE_ROW)
1016 if (!child->GetIntAttribute(
1017 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1025 j < child->PlatformChildCount();
1027 BrowserAccessibility* cell = child->PlatformGetChild(j);
1028 if (cell->GetRole() != ui::AX_ROLE_CELL)
1031 if (!cell->GetIntAttribute(
1032 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1036 if (colIndex == column)
1037 return cell->ToBrowserAccessibilityCocoa();
1038 if (colIndex > column)
1045 if ([attribute isEqualToString:
1046 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1047 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1049 NSRange range = [(NSValue*)parameter rangeValue];
1050 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1051 range.location, range.length);
1052 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1053 NSSize size = NSMakeSize(rect.width(), rect.height());
1054 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1055 NSRect nsrect = NSMakeRect(
1056 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1057 return [NSValue valueWithRect:nsrect];
1060 // TODO(dtseng): support the following attributes.
1061 if ([attribute isEqualTo:
1062 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1063 [attribute isEqualTo:
1064 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1065 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1066 [attribute isEqualTo:
1067 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1073 // Returns an array of parameterized attributes names that this object will
1075 - (NSArray*)accessibilityParameterizedAttributeNames {
1076 if (!browserAccessibility_)
1079 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1080 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1081 return [NSArray arrayWithObjects:
1082 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1085 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1086 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1087 return [NSArray arrayWithObjects:
1088 NSAccessibilityLineForIndexParameterizedAttribute,
1089 NSAccessibilityRangeForLineParameterizedAttribute,
1090 NSAccessibilityStringForRangeParameterizedAttribute,
1091 NSAccessibilityRangeForPositionParameterizedAttribute,
1092 NSAccessibilityRangeForIndexParameterizedAttribute,
1093 NSAccessibilityBoundsForRangeParameterizedAttribute,
1094 NSAccessibilityRTFForRangeParameterizedAttribute,
1095 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1096 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1099 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1100 return [NSArray arrayWithObjects:
1101 NSAccessibilityBoundsForRangeParameterizedAttribute,
1107 // Returns an array of action names that this object will respond to.
1108 - (NSArray*)accessibilityActionNames {
1109 if (!browserAccessibility_)
1112 NSMutableArray* ret =
1113 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1114 NSString* role = [self role];
1115 // TODO(dtseng): this should only get set when there's a default action.
1116 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1117 ![role isEqualToString:NSAccessibilityTextAreaRole] &&
1118 ![role isEqualToString:NSAccessibilityTextFieldRole]) {
1119 [ret addObject:NSAccessibilityPressAction];
1125 // Returns a sub-array of values for the given attribute value, starting at
1126 // index, with up to maxCount items. If the given index is out of bounds,
1127 // or there are no values for the given attribute, it will return nil.
1128 // This method is used for querying subsets of values, without having to
1129 // return a large set of data, such as elements with a large number of
1131 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1132 index:(NSUInteger)index
1133 maxCount:(NSUInteger)maxCount {
1134 if (!browserAccessibility_)
1137 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1140 NSUInteger arrayCount = [fullArray count];
1141 if (index >= arrayCount)
1144 if ((index + maxCount) > arrayCount) {
1145 subRange = NSMakeRange(index, arrayCount - index);
1147 subRange = NSMakeRange(index, maxCount);
1149 return [fullArray subarrayWithRange:subRange];
1152 // Returns the count of the specified accessibility array attribute.
1153 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1154 if (!browserAccessibility_)
1157 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1158 return [fullArray count];
1161 // Returns the list of accessibility attributes that this object supports.
1162 - (NSArray*)accessibilityAttributeNames {
1163 if (!browserAccessibility_)
1166 // General attributes.
1167 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1168 NSAccessibilityChildrenAttribute,
1169 NSAccessibilityDescriptionAttribute,
1170 NSAccessibilityEnabledAttribute,
1171 NSAccessibilityFocusedAttribute,
1172 NSAccessibilityHelpAttribute,
1173 NSAccessibilityLinkedUIElementsAttribute,
1174 NSAccessibilityParentAttribute,
1175 NSAccessibilityPositionAttribute,
1176 NSAccessibilityRoleAttribute,
1177 NSAccessibilityRoleDescriptionAttribute,
1178 NSAccessibilitySizeAttribute,
1179 NSAccessibilitySubroleAttribute,
1180 NSAccessibilityTitleAttribute,
1181 NSAccessibilityTopLevelUIElementAttribute,
1182 NSAccessibilityValueAttribute,
1183 NSAccessibilityWindowAttribute,
1190 // Specific role attributes.
1191 NSString* role = [self role];
1192 NSString* subrole = [self subrole];
1193 if ([role isEqualToString:NSAccessibilityTableRole] ||
1194 [role isEqualToString:NSAccessibilityGridRole]) {
1195 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1196 NSAccessibilityColumnsAttribute,
1197 NSAccessibilityVisibleColumnsAttribute,
1198 NSAccessibilityRowsAttribute,
1199 NSAccessibilityVisibleRowsAttribute,
1200 NSAccessibilityVisibleCellsAttribute,
1201 NSAccessibilityHeaderAttribute,
1202 NSAccessibilityColumnHeaderUIElementsAttribute,
1203 NSAccessibilityRowHeaderUIElementsAttribute,
1205 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1206 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1207 NSAccessibilityIndexAttribute,
1208 NSAccessibilityHeaderAttribute,
1209 NSAccessibilityRowsAttribute,
1210 NSAccessibilityVisibleRowsAttribute,
1212 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1213 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1214 NSAccessibilityColumnIndexRangeAttribute,
1215 NSAccessibilityRowIndexRangeAttribute,
1217 } else if ([role isEqualToString:@"AXWebArea"]) {
1218 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1220 @"AXLoadingProgress",
1222 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1223 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1224 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1225 NSAccessibilityInsertionPointLineNumberAttribute,
1226 NSAccessibilityNumberOfCharactersAttribute,
1227 NSAccessibilitySelectedTextAttribute,
1228 NSAccessibilitySelectedTextRangeAttribute,
1229 NSAccessibilityVisibleCharacterRangeAttribute,
1231 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1232 [ret addObject:NSAccessibilityTabsAttribute];
1233 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1234 [role isEqualToString:NSAccessibilitySliderRole] ||
1235 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1236 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1237 NSAccessibilityMaxValueAttribute,
1238 NSAccessibilityMinValueAttribute,
1239 NSAccessibilityOrientationAttribute,
1240 NSAccessibilityValueDescriptionAttribute,
1242 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1243 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1244 NSAccessibilityDisclosingAttribute,
1245 NSAccessibilityDisclosedByRowAttribute,
1246 NSAccessibilityDisclosureLevelAttribute,
1247 NSAccessibilityDisclosedRowsAttribute,
1249 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1250 if (browserAccessibility_->GetParent()) {
1251 base::string16 parentRole;
1252 browserAccessibility_->GetParent()->GetHtmlAttribute(
1253 "role", &parentRole);
1254 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1255 if (parentRole == treegridRole) {
1256 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1257 NSAccessibilityDisclosingAttribute,
1258 NSAccessibilityDisclosedByRowAttribute,
1259 NSAccessibilityDisclosureLevelAttribute,
1260 NSAccessibilityDisclosedRowsAttribute,
1263 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1264 NSAccessibilityIndexAttribute,
1268 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1269 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1270 NSAccessibilityOrientationAttribute,
1271 NSAccessibilitySelectedChildrenAttribute,
1272 NSAccessibilityVisibleChildrenAttribute,
1276 // Add the url attribute only if it has a valid url.
1277 if ([self url] != nil) {
1278 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1279 NSAccessibilityURLAttribute,
1284 if (browserAccessibility_->HasStringAttribute(
1285 ui::AX_ATTR_LIVE_STATUS)) {
1286 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1291 if (browserAccessibility_->HasStringAttribute(
1292 ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
1293 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1299 // Title UI Element.
1300 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1301 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1302 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1304 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1305 NSAccessibilityTitleUIElementAttribute,
1308 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1309 // for elements which are referred to by labelledby or are labels
1314 // Returns the index of the child in this objects array of children.
1315 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1316 if (!browserAccessibility_)
1319 NSUInteger index = 0;
1320 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1321 if ([child isEqual:childToCheck])
1328 // Returns whether or not the specified attribute can be set by the
1329 // accessibility API via |accessibilitySetValue:forAttribute:|.
1330 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1331 if (!browserAccessibility_)
1334 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1335 return GetState(browserAccessibility_,
1336 ui::AX_STATE_FOCUSABLE);
1337 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1338 return browserAccessibility_->GetBoolAttribute(
1339 ui::AX_ATTR_CAN_SET_VALUE);
1341 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1342 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1343 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1349 // Returns whether or not this object should be ignored in the accessibilty
1351 - (BOOL)accessibilityIsIgnored {
1352 if (!browserAccessibility_)
1355 return [self isIgnored];
1358 // Performs the given accessibilty action on the webkit accessibility object
1359 // that backs this object.
1360 - (void)accessibilityPerformAction:(NSString*)action {
1361 if (!browserAccessibility_)
1364 // TODO(dmazzoni): Support more actions.
1365 if ([action isEqualToString:NSAccessibilityPressAction]) {
1366 [self delegate]->AccessibilityDoDefaultAction(
1367 browserAccessibility_->GetId());
1368 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1369 NSPoint objOrigin = [self origin];
1370 NSSize size = [[self size] sizeValue];
1371 gfx::Point origin = [self delegate]->AccessibilityOriginInScreen(
1372 gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
1373 origin.Offset(size.width / 2, size.height / 2);
1374 [self delegate]->AccessibilityShowMenu(origin);
1378 // Returns the description of the given action.
1379 - (NSString*)accessibilityActionDescription:(NSString*)action {
1380 if (!browserAccessibility_)
1383 return NSAccessibilityActionDescription(action);
1386 // Sets an override value for a specific accessibility attribute.
1387 // This class does not support this.
1388 - (BOOL)accessibilitySetOverrideValue:(id)value
1389 forAttribute:(NSString*)attribute {
1393 // Sets the value for an accessibility attribute via the accessibility API.
1394 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1395 if (!browserAccessibility_)
1398 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1399 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1400 NSNumber* focusedNumber = value;
1401 BOOL focused = [focusedNumber intValue];
1403 manager->SetFocus(browserAccessibility_, true);
1405 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1406 NSRange range = [(NSValue*)value rangeValue];
1407 [self delegate]->AccessibilitySetTextSelection(
1408 browserAccessibility_->GetId(),
1409 range.location, range.location + range.length);
1413 // Returns the deepest accessibility child that should not be ignored.
1414 // It is assumed that the hit test has been narrowed down to this object
1415 // or one of its children, so this will never return nil unless this
1416 // object is invalid.
1417 - (id)accessibilityHitTest:(NSPoint)point {
1418 if (!browserAccessibility_)
1421 BrowserAccessibilityCocoa* hit = self;
1422 for (BrowserAccessibilityCocoa* child in [self children]) {
1423 if (!child->browserAccessibility_)
1425 NSPoint origin = [child origin];
1426 NSSize size = [[child size] sizeValue];
1428 rect.origin = origin;
1430 if (NSPointInRect(point, rect)) {
1432 id childResult = [child accessibilityHitTest:point];
1433 if (![childResult accessibilityIsIgnored]) {
1439 return NSAccessibilityUnignoredAncestor(hit);
1442 - (BOOL)isEqual:(id)object {
1443 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1445 return ([self hash] == [object hash]);
1448 - (NSUInteger)hash {
1449 // Potentially called during dealloc.
1450 if (!browserAccessibility_)
1451 return [super hash];
1452 return browserAccessibility_->GetId();
1455 - (BOOL)accessibilityShouldUseUniqueId {