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/browser/accessibility/one_shot_accessibility_tree_search.h"
19 #include "content/public/common/content_client.h"
20 #import "ui/accessibility/platform/ax_platform_node_mac.h"
22 // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
23 // 10.6, and 10.7. It allows accessibility clients to observe events posted on
25 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
28 using content::AccessibilityMatchPredicate;
29 using content::BrowserAccessibility;
30 using content::BrowserAccessibilityDelegate;
31 using content::BrowserAccessibilityManager;
32 using content::BrowserAccessibilityManagerMac;
33 using content::ContentClient;
34 using content::OneShotAccessibilityTreeSearch;
35 typedef ui::AXStringAttribute StringAttribute;
39 // VoiceOver uses -1 to mean "no limit" for AXResultsLimit.
40 const int kAXResultsLimitNoLimit = -1;
42 // Returns an autoreleased copy of the AXNodeData's attribute.
43 NSString* NSStringForStringAttribute(
44 BrowserAccessibility* browserAccessibility,
45 StringAttribute attribute) {
46 return base::SysUTF8ToNSString(
47 browserAccessibility->GetStringAttribute(attribute));
50 // GetState checks the bitmask used in AXNodeData to check
51 // if the given state was set on the accessibility object.
52 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
53 return ((accessibility->GetState() >> state) & 1);
56 // A mapping from an accessibility attribute to its method name.
57 NSDictionary* attributeToMethodNameMap = nil;
59 // Given a search key provided to AXUIElementCountForSearchPredicate or
60 // AXUIElementsForSearchPredicate, return a predicate that can be added
61 // to OneShotAccessibilityTreeSearch.
62 AccessibilityMatchPredicate PredicateForSearchKey(NSString* searchKey) {
63 if ([searchKey isEqualToString:@"AXAnyTypeSearchKey"]) {
64 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
67 } else if ([searchKey isEqualToString:@"AXBlockquoteSameLevelSearchKey"] ||
68 [searchKey isEqualToString:@"AXBlockquoteSearchKey"]) {
69 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
70 // TODO(dmazzoni): implement the "same level" part.
71 return current->GetRole() == ui::AX_ROLE_BLOCKQUOTE;
73 } else if ([searchKey isEqualToString:@"AXBoldFontSearchKey"]) {
74 // TODO(dmazzoni): implement this.
76 } else if ([searchKey isEqualToString:@"AXButtonSearchKey"]) {
77 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
78 return (current->GetRole() == ui::AX_ROLE_BUTTON ||
79 current->GetRole() == ui::AX_ROLE_MENU_BUTTON ||
80 current->GetRole() == ui::AX_ROLE_POP_UP_BUTTON ||
81 current->GetRole() == ui::AX_ROLE_SWITCH ||
82 current->GetRole() == ui::AX_ROLE_TOGGLE_BUTTON);
84 } else if ([searchKey isEqualToString:@"AXCheckBoxSearchKey"]) {
85 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
86 return (current->GetRole() == ui::AX_ROLE_CHECK_BOX ||
87 current->GetRole() == ui::AX_ROLE_MENU_ITEM_CHECK_BOX);
89 } else if ([searchKey isEqualToString:@"AXControlSearchKey"]) {
90 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
91 if (current->IsControl())
93 if (current->HasState(ui::AX_STATE_FOCUSABLE) &&
94 current->GetRole() != ui::AX_ROLE_IMAGE_MAP_LINK &&
95 current->GetRole() != ui::AX_ROLE_LINK) {
100 } else if ([searchKey isEqualToString:@"AXDifferentTypeSearchKey"]) {
101 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
102 return current->GetRole() != start->GetRole();
104 } else if ([searchKey isEqualToString:@"AXFontChangeSearchKey"]) {
105 // TODO(dmazzoni): implement this.
107 } else if ([searchKey isEqualToString:@"AXFontColorChangeSearchKey"]) {
108 // TODO(dmazzoni): implement this.
110 } else if ([searchKey isEqualToString:@"AXFrameSearchKey"]) {
111 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
112 if (current->IsWebAreaForPresentationalIframe())
114 if (!current->GetParent())
116 return (current->GetRole() == ui::AX_ROLE_WEB_AREA ||
117 current->GetRole() == ui::AX_ROLE_ROOT_WEB_AREA);
119 } else if ([searchKey isEqualToString:@"AXGraphicSearchKey"]) {
120 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
121 return current->GetRole() == ui::AX_ROLE_IMAGE;
123 } else if ([searchKey isEqualToString:@"AXHeadingLevel1SearchKey"]) {
124 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
125 return (current->GetRole() == ui::AX_ROLE_HEADING &&
126 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 1);
128 } else if ([searchKey isEqualToString:@"AXHeadingLevel2SearchKey"]) {
129 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
130 return (current->GetRole() == ui::AX_ROLE_HEADING &&
131 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 2);
133 } else if ([searchKey isEqualToString:@"AXHeadingLevel3SearchKey"]) {
134 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
135 return (current->GetRole() == ui::AX_ROLE_HEADING &&
136 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 3);
138 } else if ([searchKey isEqualToString:@"AXHeadingLevel4SearchKey"]) {
139 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
140 return (current->GetRole() == ui::AX_ROLE_HEADING &&
141 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 4);
143 } else if ([searchKey isEqualToString:@"AXHeadingLevel5SearchKey"]) {
144 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
145 return (current->GetRole() == ui::AX_ROLE_HEADING &&
146 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 5);
148 } else if ([searchKey isEqualToString:@"AXHeadingLevel6SearchKey"]) {
149 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
150 return (current->GetRole() == ui::AX_ROLE_HEADING &&
151 current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) == 6);
153 } else if ([searchKey isEqualToString:@"AXHeadingSameLevelSearchKey"]) {
154 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
155 return (current->GetRole() == ui::AX_ROLE_HEADING &&
156 start->GetRole() == ui::AX_ROLE_HEADING &&
157 (current->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL) ==
158 start->GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL)));
160 } else if ([searchKey isEqualToString:@"AXHeadingSearchKey"]) {
161 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
162 return current->GetRole() == ui::AX_ROLE_HEADING;
164 } else if ([searchKey isEqualToString:@"AXHighlightedSearchKey"]) {
165 // TODO(dmazzoni): implement this.
167 } else if ([searchKey isEqualToString:@"AXItalicFontSearchKey"]) {
168 // TODO(dmazzoni): implement this.
170 } else if ([searchKey isEqualToString:@"AXLandmarkSearchKey"]) {
171 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
172 return (current->GetRole() == ui::AX_ROLE_APPLICATION ||
173 current->GetRole() == ui::AX_ROLE_BANNER ||
174 current->GetRole() == ui::AX_ROLE_COMPLEMENTARY ||
175 current->GetRole() == ui::AX_ROLE_CONTENT_INFO ||
176 current->GetRole() == ui::AX_ROLE_FORM ||
177 current->GetRole() == ui::AX_ROLE_MAIN ||
178 current->GetRole() == ui::AX_ROLE_NAVIGATION ||
179 current->GetRole() == ui::AX_ROLE_SEARCH);
181 } else if ([searchKey isEqualToString:@"AXLinkSearchKey"]) {
182 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
183 return current->GetRole() == ui::AX_ROLE_LINK;
185 } else if ([searchKey isEqualToString:@"AXListSearchKey"]) {
186 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
187 return current->GetRole() == ui::AX_ROLE_LIST;
189 } else if ([searchKey isEqualToString:@"AXLiveRegionSearchKey"]) {
190 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
191 return current->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS);
193 } else if ([searchKey isEqualToString:@"AXMisspelledWordSearchKey"]) {
194 // TODO(dmazzoni): implement this.
196 } else if ([searchKey isEqualToString:@"AXOutlineSearchKey"]) {
197 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
198 return current->GetRole() == ui::AX_ROLE_TREE;
200 } else if ([searchKey isEqualToString:@"AXPlainTextSearchKey"]) {
201 // TODO(dmazzoni): implement this.
203 } else if ([searchKey isEqualToString:@"AXRadioGroupSearchKey"]) {
204 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
205 return current->GetRole() == ui::AX_ROLE_RADIO_GROUP;
207 } else if ([searchKey isEqualToString:@"AXSameTypeSearchKey"]) {
208 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
209 return current->GetRole() == start->GetRole();
211 } else if ([searchKey isEqualToString:@"AXStaticTextSearchKey"]) {
212 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
213 return current->GetRole() == ui::AX_ROLE_STATIC_TEXT;
215 } else if ([searchKey isEqualToString:@"AXStyleChangeSearchKey"]) {
216 // TODO(dmazzoni): implement this.
218 } else if ([searchKey isEqualToString:@"AXTableSameLevelSearchKey"]) {
219 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
220 // TODO(dmazzoni): implement the "same level" part.
221 return current->GetRole() == ui::AX_ROLE_GRID ||
222 current->GetRole() == ui::AX_ROLE_TABLE;
224 } else if ([searchKey isEqualToString:@"AXTableSearchKey"]) {
225 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
226 return current->GetRole() == ui::AX_ROLE_GRID ||
227 current->GetRole() == ui::AX_ROLE_TABLE;
229 } else if ([searchKey isEqualToString:@"AXTextFieldSearchKey"]) {
230 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
231 return current->GetRole() == ui::AX_ROLE_TEXT_FIELD;
233 } else if ([searchKey isEqualToString:@"AXUnderlineSearchKey"]) {
234 // TODO(dmazzoni): implement this.
236 } else if ([searchKey isEqualToString:@"AXUnvisitedLinkSearchKey"]) {
237 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
238 return (current->GetRole() == ui::AX_ROLE_LINK &&
239 !current->HasState(ui::AX_STATE_VISITED));
241 } else if ([searchKey isEqualToString:@"AXVisitedLinkSearchKey"]) {
242 return [](BrowserAccessibility* start, BrowserAccessibility* current) {
243 return (current->GetRole() == ui::AX_ROLE_LINK &&
244 current->HasState(ui::AX_STATE_VISITED));
251 // Initialize a OneShotAccessibilityTreeSearch object given the parameters
252 // passed to AXUIElementCountForSearchPredicate or
253 // AXUIElementsForSearchPredicate. Return true on success.
254 bool InitializeAccessibilityTreeSearch(
255 OneShotAccessibilityTreeSearch* search,
257 if (![parameter isKindOfClass:[NSDictionary class]])
259 NSDictionary* dictionary = parameter;
261 id startElementParameter = [dictionary objectForKey:@"AXStartElement"];
262 BrowserAccessibility* startNode = nullptr;
263 if ([startElementParameter isKindOfClass:[BrowserAccessibilityCocoa class]]) {
264 BrowserAccessibilityCocoa* startNodeCocoa =
265 (BrowserAccessibilityCocoa*)startElementParameter;
266 startNode = [startNodeCocoa browserAccessibility];
269 bool immediateDescendantsOnly = false;
270 NSNumber *immediateDescendantsOnlyParameter =
271 [dictionary objectForKey:@"AXImmediateDescendantsOnly"];
272 if ([immediateDescendantsOnlyParameter isKindOfClass:[NSNumber class]])
273 immediateDescendantsOnly = [immediateDescendantsOnlyParameter boolValue];
275 bool visibleOnly = false;
276 NSNumber *visibleOnlyParameter = [dictionary objectForKey:@"AXVisibleOnly"];
277 if ([visibleOnlyParameter isKindOfClass:[NSNumber class]])
278 visibleOnly = [visibleOnlyParameter boolValue];
280 content::OneShotAccessibilityTreeSearch::Direction direction =
281 content::OneShotAccessibilityTreeSearch::FORWARDS;
282 NSString* directionParameter = [dictionary objectForKey:@"AXDirection"];
283 if ([directionParameter isKindOfClass:[NSString class]]) {
284 if ([directionParameter isEqualToString:@"AXDirectionNext"])
285 direction = content::OneShotAccessibilityTreeSearch::FORWARDS;
286 else if ([directionParameter isEqualToString:@"AXDirectionPrevious"])
287 direction = content::OneShotAccessibilityTreeSearch::BACKWARDS;
290 int resultsLimit = kAXResultsLimitNoLimit;
291 NSNumber* resultsLimitParameter = [dictionary objectForKey:@"AXResultsLimit"];
292 if ([resultsLimitParameter isKindOfClass:[NSNumber class]])
293 resultsLimit = [resultsLimitParameter intValue];
295 std::string searchText;
296 NSString* searchTextParameter = [dictionary objectForKey:@"AXSearchText"];
297 if ([searchTextParameter isKindOfClass:[NSString class]])
298 searchText = base::SysNSStringToUTF8(searchTextParameter);
300 search->SetStartNode(startNode);
301 search->SetDirection(direction);
302 search->SetImmediateDescendantsOnly(immediateDescendantsOnly);
303 search->SetVisibleOnly(visibleOnly);
304 search->SetSearchText(searchText);
306 // Mac uses resultsLimit == -1 for unlimited, that that's
307 // the default for OneShotAccessibilityTreeSearch already.
308 // Only set the results limit if it's nonnegative.
309 if (resultsLimit >= 0)
310 search->SetResultLimit(resultsLimit);
312 id searchKey = [dictionary objectForKey:@"AXSearchKey"];
313 if ([searchKey isKindOfClass:[NSString class]]) {
314 AccessibilityMatchPredicate predicate =
315 PredicateForSearchKey((NSString*)searchKey);
317 search->AddPredicate(predicate);
318 } else if ([searchKey isKindOfClass:[NSArray class]]) {
319 size_t searchKeyCount = static_cast<size_t>([searchKey count]);
320 for (size_t i = 0; i < searchKeyCount; ++i) {
321 id key = [searchKey objectAtIndex:i];
322 if ([key isKindOfClass:[NSString class]]) {
323 AccessibilityMatchPredicate predicate =
324 PredicateForSearchKey((NSString*)key);
326 search->AddPredicate(predicate);
336 @implementation BrowserAccessibilityCocoa
341 NSString* methodName;
342 } attributeToMethodNameContainer[] = {
343 { NSAccessibilityChildrenAttribute, @"children" },
344 { NSAccessibilityColumnsAttribute, @"columns" },
345 { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
346 { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
347 { NSAccessibilityContentsAttribute, @"contents" },
348 { NSAccessibilityDescriptionAttribute, @"description" },
349 { NSAccessibilityDisclosingAttribute, @"disclosing" },
350 { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
351 { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
352 { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
353 { NSAccessibilityEnabledAttribute, @"enabled" },
354 { NSAccessibilityExpandedAttribute, @"expanded" },
355 { NSAccessibilityFocusedAttribute, @"focused" },
356 { NSAccessibilityHeaderAttribute, @"header" },
357 { NSAccessibilityHelpAttribute, @"help" },
358 { NSAccessibilityIndexAttribute, @"index" },
359 { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
360 { NSAccessibilityMaxValueAttribute, @"maxValue" },
361 { NSAccessibilityMinValueAttribute, @"minValue" },
362 { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
363 { NSAccessibilityOrientationAttribute, @"orientation" },
364 { NSAccessibilityParentAttribute, @"parent" },
365 { NSAccessibilityPlaceholderValueAttribute, @"placeholderValue" },
366 { NSAccessibilityPositionAttribute, @"position" },
367 { NSAccessibilityRoleAttribute, @"role" },
368 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
369 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
370 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
371 { NSAccessibilityRowsAttribute, @"rows" },
372 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
373 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
374 { NSAccessibilitySizeAttribute, @"size" },
375 { NSAccessibilitySubroleAttribute, @"subrole" },
376 { NSAccessibilityTabsAttribute, @"tabs" },
377 { NSAccessibilityTitleAttribute, @"title" },
378 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
379 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
380 { NSAccessibilityURLAttribute, @"url" },
381 { NSAccessibilityValueAttribute, @"value" },
382 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
383 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
384 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
385 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
386 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
387 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
388 { NSAccessibilityWindowAttribute, @"window" },
389 { @"AXAccessKey", @"accessKey" },
390 { @"AXARIAAtomic", @"ariaAtomic" },
391 { @"AXARIABusy", @"ariaBusy" },
392 { @"AXARIALive", @"ariaLive" },
393 { @"AXARIASetSize", @"ariaSetSize" },
394 { @"AXARIAPosInSet", @"ariaPosInSet" },
395 { @"AXARIARelevant", @"ariaRelevant" },
396 { @"AXDropEffects", @"dropeffect" },
397 { @"AXGrabbed", @"grabbed" },
398 { @"AXInvalid", @"invalid" },
399 { @"AXLoaded", @"loaded" },
400 { @"AXLoadingProgress", @"loadingProgress" },
401 { @"AXRequired", @"required" },
402 { @"AXSortDirection", @"sortDirection" },
403 { @"AXVisited", @"visited" },
406 NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
407 const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
408 sizeof(attributeToMethodNameContainer[0]);
409 for (size_t i = 0; i < numAttributes; ++i) {
410 [dict setObject:attributeToMethodNameContainer[i].methodName
411 forKey:attributeToMethodNameContainer[i].attribute];
413 attributeToMethodNameMap = dict;
417 - (id)initWithObject:(BrowserAccessibility*)accessibility {
418 if ((self = [super init]))
419 browserAccessibility_ = accessibility;
424 if (browserAccessibility_) {
425 NSAccessibilityUnregisterUniqueIdForUIElement(self);
426 browserAccessibility_ = NULL;
430 - (NSString*)accessKey {
431 return NSStringForStringAttribute(
432 browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
435 - (NSNumber*)ariaAtomic {
436 bool boolValue = browserAccessibility_->GetBoolAttribute(
437 ui::AX_ATTR_LIVE_ATOMIC);
438 return [NSNumber numberWithBool:boolValue];
441 - (NSNumber*)ariaBusy {
442 return [NSNumber numberWithBool:
443 GetState(browserAccessibility_, ui::AX_STATE_BUSY)];
446 - (NSString*)ariaLive {
447 return NSStringForStringAttribute(
448 browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
451 - (NSString*)ariaRelevant {
452 return NSStringForStringAttribute(
453 browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
456 - (NSNumber*)ariaPosInSet {
457 return [NSNumber numberWithInt:
458 browserAccessibility_->GetIntAttribute(ui::AX_ATTR_POS_IN_SET)];
461 - (NSNumber*)ariaSetSize {
462 return [NSNumber numberWithInt:
463 browserAccessibility_->GetIntAttribute(ui::AX_ATTR_SET_SIZE)];
466 // Returns an array of BrowserAccessibilityCocoa objects, representing the
467 // accessibility children of this object.
468 - (NSArray*)children {
470 uint32 childCount = browserAccessibility_->PlatformChildCount();
471 children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
472 for (uint32 index = 0; index < childCount; ++index) {
473 BrowserAccessibilityCocoa* child =
474 browserAccessibility_->PlatformGetChild(index)->
475 ToBrowserAccessibilityCocoa();
476 if ([child isIgnored])
477 [children_ addObjectsFromArray:[child children]];
479 [children_ addObject:child];
482 // Also, add indirect children (if any).
483 const std::vector<int32>& indirectChildIds =
484 browserAccessibility_->GetIntListAttribute(
485 ui::AX_ATTR_INDIRECT_CHILD_IDS);
486 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
487 int32 child_id = indirectChildIds[i];
488 BrowserAccessibility* child =
489 browserAccessibility_->manager()->GetFromID(child_id);
491 // This only became necessary as a result of crbug.com/93095. It should be
492 // a DCHECK in the future.
494 BrowserAccessibilityCocoa* child_cocoa =
495 child->ToBrowserAccessibilityCocoa();
496 [children_ addObject:child_cocoa];
503 - (void)childrenChanged {
504 if (![self isIgnored]) {
507 [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
512 - (NSArray*)columnHeaders {
513 if ([self internalRole] != ui::AX_ROLE_TABLE &&
514 [self internalRole] != ui::AX_ROLE_GRID) {
518 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
519 const std::vector<int32>& uniqueCellIds =
520 browserAccessibility_->GetIntListAttribute(
521 ui::AX_ATTR_UNIQUE_CELL_IDS);
522 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
523 int id = uniqueCellIds[i];
524 BrowserAccessibility* cell =
525 browserAccessibility_->manager()->GetFromID(id);
526 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
527 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
532 - (NSValue*)columnIndexRange {
533 if (!browserAccessibility_->IsCellOrTableHeaderRole())
538 browserAccessibility_->GetIntAttribute(
539 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
540 browserAccessibility_->GetIntAttribute(
541 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
542 if (column >= 0 && colspan >= 1)
543 return [NSValue valueWithRange:NSMakeRange(column, colspan)];
547 - (NSArray*)columns {
548 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
549 for (BrowserAccessibilityCocoa* child in [self children]) {
550 if ([[child role] isEqualToString:NSAccessibilityColumnRole])
551 [ret addObject:child];
556 - (NSString*)description {
557 std::string description;
558 if (browserAccessibility_->GetStringAttribute(
559 ui::AX_ATTR_DESCRIPTION, &description)) {
560 return base::SysUTF8ToNSString(description);
563 // If the role is anything other than an image, or if there's
564 // a title or title UI element, just return an empty string.
565 if (![[self role] isEqualToString:NSAccessibilityImageRole])
567 if (browserAccessibility_->HasStringAttribute(
571 if ([self titleUIElement])
574 // The remaining case is an image where there's no other title.
575 // Return the base part of the filename as the description.
577 if (browserAccessibility_->GetStringAttribute(
578 ui::AX_ATTR_URL, &url)) {
579 // Given a url like http://foo.com/bar/baz.png, just return the
580 // base name, e.g., "baz.png".
581 size_t leftIndex = url.rfind('/');
582 std::string basename =
583 leftIndex != std::string::npos ? url.substr(leftIndex) : url;
584 return base::SysUTF8ToNSString(basename);
590 - (NSNumber*)disclosing {
591 if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
592 return [NSNumber numberWithBool:
593 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
599 - (id)disclosedByRow {
600 // The row that contains this row.
601 // It should be the same as the first parent that is a treeitem.
605 - (NSNumber*)disclosureLevel {
606 ui::AXRole role = [self internalRole];
607 if (role == ui::AX_ROLE_ROW ||
608 role == ui::AX_ROLE_TREE_ITEM) {
609 int level = browserAccessibility_->GetIntAttribute(
610 ui::AX_ATTR_HIERARCHICAL_LEVEL);
611 // Mac disclosureLevel is 0-based, but web levels are 1-based.
614 return [NSNumber numberWithInt:level];
620 - (id)disclosedRows {
621 // The rows that are considered inside this row.
625 - (NSString*)dropeffect {
626 std::string dropEffect;
627 if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffect))
628 return base::SysUTF8ToNSString(dropEffect);
633 - (NSNumber*)enabled {
634 return [NSNumber numberWithBool:
635 GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
638 - (NSNumber*)expanded {
639 return [NSNumber numberWithBool:
640 GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
643 - (NSNumber*)focused {
644 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
645 NSNumber* ret = [NSNumber numberWithBool:
646 manager->GetFocus(NULL) == browserAccessibility_];
650 - (NSNumber*)grabbed {
652 if (browserAccessibility_->GetHtmlAttribute("aria-grabbed", &grabbed) &&
654 return [NSNumber numberWithBool:YES];
656 return [NSNumber numberWithBool:NO];
660 int headerElementId = -1;
661 if ([self internalRole] == ui::AX_ROLE_TABLE ||
662 [self internalRole] == ui::AX_ROLE_GRID) {
663 browserAccessibility_->GetIntAttribute(
664 ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
665 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
666 browserAccessibility_->GetIntAttribute(
667 ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
668 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
669 browserAccessibility_->GetIntAttribute(
670 ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
673 if (headerElementId > 0) {
674 BrowserAccessibility* headerObject =
675 browserAccessibility_->manager()->GetFromID(headerElementId);
677 return headerObject->ToBrowserAccessibilityCocoa();
683 return NSStringForStringAttribute(
684 browserAccessibility_, ui::AX_ATTR_HELP);
688 if ([self internalRole] == ui::AX_ROLE_COLUMN) {
689 int columnIndex = browserAccessibility_->GetIntAttribute(
690 ui::AX_ATTR_TABLE_COLUMN_INDEX);
691 return [NSNumber numberWithInt:columnIndex];
692 } else if ([self internalRole] == ui::AX_ROLE_ROW) {
693 int rowIndex = browserAccessibility_->GetIntAttribute(
694 ui::AX_ATTR_TABLE_ROW_INDEX);
695 return [NSNumber numberWithInt:rowIndex];
701 // Returns whether or not this node should be ignored in the
702 // accessibility tree.
704 return [[self role] isEqualToString:NSAccessibilityUnknownRole];
707 - (NSString*)invalid {
709 if (!browserAccessibility_->GetIntAttribute(
710 ui::AX_ATTR_INVALID_STATE, &invalidState))
713 switch (invalidState) {
714 case ui::AX_INVALID_STATE_FALSE:
716 case ui::AX_INVALID_STATE_TRUE:
718 case ui::AX_INVALID_STATE_SPELLING:
720 case ui::AX_INVALID_STATE_GRAMMAR:
722 case ui::AX_INVALID_STATE_OTHER:
724 std::string ariaInvalidValue;
725 if (browserAccessibility_->GetStringAttribute(
726 ui::AX_ATTR_ARIA_INVALID_VALUE,
728 return base::SysUTF8ToNSString(ariaInvalidValue);
729 // Return @"true" since we cannot be more specific about the value.
739 - (NSString*)placeholderValue {
740 return NSStringForStringAttribute(
741 browserAccessibility_, ui::AX_ATTR_PLACEHOLDER);
744 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
745 addTo:(NSMutableArray*)outArray {
746 const std::vector<int32>& attributeValues =
747 browserAccessibility_->GetIntListAttribute(attribute);
748 for (size_t i = 0; i < attributeValues.size(); ++i) {
749 BrowserAccessibility* element =
750 browserAccessibility_->manager()->GetFromID(attributeValues[i]);
752 [outArray addObject:element->ToBrowserAccessibilityCocoa()];
756 - (NSArray*)linkedUIElements {
757 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
758 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
759 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
760 [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
761 if ([ret count] == 0)
766 - (NSNumber*)loaded {
767 return [NSNumber numberWithBool:YES];
770 - (NSNumber*)loadingProgress {
771 float floatValue = browserAccessibility_->GetFloatAttribute(
772 ui::AX_ATTR_DOC_LOADING_PROGRESS);
773 return [NSNumber numberWithFloat:floatValue];
776 - (NSNumber*)maxValue {
777 float floatValue = browserAccessibility_->GetFloatAttribute(
778 ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
779 return [NSNumber numberWithFloat:floatValue];
782 - (NSNumber*)minValue {
783 float floatValue = browserAccessibility_->GetFloatAttribute(
784 ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
785 return [NSNumber numberWithFloat:floatValue];
788 - (NSString*)orientation {
789 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
790 return NSAccessibilityVerticalOrientationValue;
791 else if (GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL))
792 return NSAccessibilityHorizontalOrientationValue;
797 - (NSNumber*)numberOfCharacters {
798 std::string value = browserAccessibility_->GetStringAttribute(
800 return [NSNumber numberWithInt:value.size()];
803 // The origin of this accessibility object in the page's document.
804 // This is relative to webkit's top-left origin, not Cocoa's
805 // bottom-left origin.
807 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
808 return NSMakePoint(bounds.x(), bounds.y());
812 // A nil parent means we're the root.
813 if (browserAccessibility_->GetParent()) {
814 return NSAccessibilityUnignoredAncestor(
815 browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
817 // Hook back up to RenderWidgetHostViewCocoa.
818 BrowserAccessibilityManagerMac* manager =
819 static_cast<BrowserAccessibilityManagerMac*>(
820 browserAccessibility_->manager());
821 return manager->parent_view();
825 - (NSValue*)position {
826 NSPoint origin = [self origin];
827 NSSize size = [[self size] sizeValue];
828 NSPoint pointInScreen = [self pointInScreen:origin size:size];
829 return [NSValue valueWithPoint:pointInScreen];
832 - (NSNumber*)required {
833 return [NSNumber numberWithBool:
834 GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
837 // Returns an enum indicating the role from browserAccessibility_.
838 - (ui::AXRole)internalRole {
839 return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
842 - (content::BrowserAccessibilityDelegate*)delegate {
843 return browserAccessibility_->manager() ?
844 browserAccessibility_->manager()->delegate() :
848 - (content::BrowserAccessibility*)browserAccessibility {
849 return browserAccessibility_;
852 - (NSPoint)pointInScreen:(NSPoint)origin
854 if (!browserAccessibility_)
857 // Get the delegate for the topmost BrowserAccessibilityManager, because
858 // that's the only one that can convert points to their origin in the screen.
859 BrowserAccessibilityDelegate* delegate =
860 browserAccessibility_->manager()->GetDelegateFromRootManager();
862 gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
863 gfx::Point point = delegate->AccessibilityOriginInScreen(bounds);
864 return NSMakePoint(point.x(), point.y());
870 // Returns a string indicating the NSAccessibility role of this object.
872 ui::AXRole role = [self internalRole];
873 if (role == ui::AX_ROLE_CANVAS &&
874 browserAccessibility_->GetBoolAttribute(
875 ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
876 return NSAccessibilityGroupRole;
878 if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
879 bool isAriaPressedDefined;
881 browserAccessibility_->GetAriaTristate("aria-pressed",
882 &isAriaPressedDefined,
884 if (isAriaPressedDefined)
885 return NSAccessibilityCheckBoxRole;
887 return NSAccessibilityButtonRole;
889 if (role == ui::AX_ROLE_TEXT_FIELD &&
890 browserAccessibility_->HasState(ui::AX_STATE_MULTILINE)) {
891 return NSAccessibilityTextAreaRole;
894 // If this is a web area for a presentational iframe, give it a role of
895 // something other than WebArea so that the fact that it's a separate doc
896 // is not exposed to AT.
897 if (browserAccessibility_->IsWebAreaForPresentationalIframe())
898 return NSAccessibilityGroupRole;
900 return [AXPlatformNodeCocoa nativeRoleFromAXRole:role];
903 // Returns a string indicating the role description of this object.
904 - (NSString*)roleDescription {
905 NSString* role = [self role];
907 ContentClient* content_client = content::GetContentClient();
909 // The following descriptions are specific to webkit.
910 if ([role isEqualToString:@"AXWebArea"]) {
911 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
912 IDS_AX_ROLE_WEB_AREA));
915 if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
916 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
920 if ([role isEqualToString:@"AXHeading"]) {
921 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
922 IDS_AX_ROLE_HEADING));
925 if (([role isEqualToString:NSAccessibilityGroupRole] ||
926 [role isEqualToString:NSAccessibilityRadioButtonRole]) &&
927 !browserAccessibility_->IsWebAreaForPresentationalIframe()) {
929 if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
930 ui::AXRole internalRole = [self internalRole];
931 if ((internalRole != ui::AX_ROLE_GROUP &&
932 internalRole != ui::AX_ROLE_LIST_ITEM) ||
933 internalRole == ui::AX_ROLE_TAB) {
934 // TODO(dtseng): This is not localized; see crbug/84814.
935 return base::SysUTF8ToNSString(role);
940 switch([self internalRole]) {
941 case ui::AX_ROLE_ARTICLE:
942 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
943 IDS_AX_ROLE_ARTICLE));
944 case ui::AX_ROLE_BANNER:
945 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
946 IDS_AX_ROLE_BANNER));
947 case ui::AX_ROLE_CHECK_BOX:
948 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
949 IDS_AX_ROLE_CHECK_BOX));
950 case ui::AX_ROLE_COMPLEMENTARY:
951 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
952 IDS_AX_ROLE_COMPLEMENTARY));
953 case ui::AX_ROLE_CONTENT_INFO:
954 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
955 IDS_AX_ROLE_ADDRESS));
956 case ui::AX_ROLE_DESCRIPTION_LIST:
957 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
958 IDS_AX_ROLE_DESCRIPTION_LIST));
959 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
960 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
961 IDS_AX_ROLE_DESCRIPTION_DETAIL));
962 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
963 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
964 IDS_AX_ROLE_DESCRIPTION_TERM));
965 case ui::AX_ROLE_FIGURE:
966 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
967 IDS_AX_ROLE_FIGURE));
968 case ui::AX_ROLE_FOOTER:
969 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
970 IDS_AX_ROLE_FOOTER));
971 case ui::AX_ROLE_FORM:
972 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
974 case ui::AX_ROLE_MAIN:
975 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
976 IDS_AX_ROLE_MAIN_CONTENT));
977 case ui::AX_ROLE_MARK:
978 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
980 case ui::AX_ROLE_MATH:
981 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
983 case ui::AX_ROLE_NAVIGATION:
984 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
985 IDS_AX_ROLE_NAVIGATIONAL_LINK));
986 case ui::AX_ROLE_REGION:
987 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
988 IDS_AX_ROLE_REGION));
989 case ui::AX_ROLE_SPIN_BUTTON:
990 // This control is similar to what VoiceOver calls a "stepper".
991 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
992 IDS_AX_ROLE_STEPPER));
993 case ui::AX_ROLE_STATUS:
994 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
995 IDS_AX_ROLE_STATUS));
996 case ui::AX_ROLE_SEARCH_BOX:
997 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
998 IDS_AX_ROLE_SEARCH_BOX));
999 case ui::AX_ROLE_SWITCH:
1000 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
1001 IDS_AX_ROLE_SWITCH));
1002 case ui::AX_ROLE_TOGGLE_BUTTON:
1003 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
1004 IDS_AX_ROLE_TOGGLE_BUTTON));
1009 return NSAccessibilityRoleDescription(role, nil);
1012 - (NSArray*)rowHeaders {
1013 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1014 [self internalRole] != ui::AX_ROLE_GRID) {
1018 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1019 const std::vector<int32>& uniqueCellIds =
1020 browserAccessibility_->GetIntListAttribute(
1021 ui::AX_ATTR_UNIQUE_CELL_IDS);
1022 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
1023 int id = uniqueCellIds[i];
1024 BrowserAccessibility* cell =
1025 browserAccessibility_->manager()->GetFromID(id);
1026 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1027 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1032 - (NSValue*)rowIndexRange {
1033 if (!browserAccessibility_->IsCellOrTableHeaderRole())
1038 browserAccessibility_->GetIntAttribute(
1039 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1040 browserAccessibility_->GetIntAttribute(
1041 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
1042 if (row >= 0 && rowspan >= 1)
1043 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
1048 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1050 if ([self internalRole] == ui::AX_ROLE_TABLE||
1051 [self internalRole] == ui::AX_ROLE_GRID) {
1052 for (BrowserAccessibilityCocoa* child in [self children]) {
1053 if ([[child role] isEqualToString:NSAccessibilityRowRole])
1054 [ret addObject:child];
1056 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
1057 const std::vector<int32>& indirectChildIds =
1058 browserAccessibility_->GetIntListAttribute(
1059 ui::AX_ATTR_INDIRECT_CHILD_IDS);
1060 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
1061 int id = indirectChildIds[i];
1062 BrowserAccessibility* rowElement =
1063 browserAccessibility_->manager()->GetFromID(id);
1065 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
1072 - (NSArray*)selectedChildren {
1073 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1074 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1075 BrowserAccessibility* focusedChild = manager->GetFocus(browserAccessibility_);
1077 // If it's not multiselectable, try to skip iterating over the
1079 if (!GetState(browserAccessibility_, ui::AX_STATE_MULTISELECTABLE)) {
1080 // First try the focused child.
1081 if (focusedChild && focusedChild != browserAccessibility_) {
1082 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
1086 // Next try the active descendant.
1087 int activeDescendantId;
1088 if (browserAccessibility_->GetIntAttribute(
1089 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
1090 BrowserAccessibility* activeDescendant =
1091 manager->GetFromID(activeDescendantId);
1092 if (activeDescendant) {
1093 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
1099 // If it's multiselectable or if the previous attempts failed,
1100 // return any children with the "selected" state, which may
1101 // come from aria-selected.
1102 uint32 childCount = browserAccessibility_->PlatformChildCount();
1103 for (uint32 index = 0; index < childCount; ++index) {
1104 BrowserAccessibility* child =
1105 browserAccessibility_->PlatformGetChild(index);
1106 if (child->HasState(ui::AX_STATE_SELECTED))
1107 [ret addObject:child->ToBrowserAccessibilityCocoa()];
1110 // And if nothing's selected but one has focus, use the focused one.
1111 if ([ret count] == 0 &&
1113 focusedChild != browserAccessibility_) {
1114 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
1120 // Returns the size of this object.
1122 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
1123 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
1126 - (NSString*)sortDirection {
1128 if (!browserAccessibility_->GetIntAttribute(
1129 ui::AX_ATTR_SORT_DIRECTION, &sortDirection))
1132 switch (sortDirection) {
1133 case ui::AX_SORT_DIRECTION_UNSORTED:
1135 case ui::AX_SORT_DIRECTION_ASCENDING:
1136 return @"AXSortDirectionAscending";
1137 case ui::AX_SORT_DIRECTION_DESCENDING:
1138 return @"AXSortDirectionDescending";
1139 case ui::AX_SORT_DIRECTION_OTHER:
1140 return @"AXSortDirectionUnknown";
1148 // Returns a subrole based upon the role.
1149 - (NSString*) subrole {
1150 ui::AXRole browserAccessibilityRole = [self internalRole];
1151 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
1152 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
1153 return @"AXSecureTextField";
1156 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
1157 return @"AXDefinitionList";
1159 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
1160 return @"AXContentList";
1162 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
1165 // Returns all tabs in this subtree.
1167 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
1169 if ([self internalRole] == ui::AX_ROLE_TAB)
1170 [tabSubtree addObject:self];
1172 for (uint i=0; i < [[self children] count]; ++i) {
1173 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
1174 if ([tabChildren count] > 0)
1175 [tabSubtree addObjectsFromArray:tabChildren];
1181 - (NSString*)title {
1182 return NSStringForStringAttribute(
1183 browserAccessibility_, ui::AX_ATTR_NAME);
1186 - (id)titleUIElement {
1188 if (browserAccessibility_->GetIntAttribute(
1189 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
1190 BrowserAccessibility* titleElement =
1191 browserAccessibility_->manager()->GetFromID(titleElementId);
1193 return titleElement->ToBrowserAccessibilityCocoa();
1195 std::vector<int32> labelledby_ids =
1196 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
1197 if (labelledby_ids.size() == 1) {
1198 BrowserAccessibility* titleElement =
1199 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
1201 return titleElement->ToBrowserAccessibilityCocoa();
1208 StringAttribute urlAttribute =
1209 [[self role] isEqualToString:@"AXWebArea"] ?
1210 ui::AX_ATTR_DOC_URL :
1213 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
1217 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
1221 // WebCore uses an attachmentView to get the below behavior.
1222 // We do not have any native views backing this object, so need
1223 // to approximate Cocoa ax behavior best as we can.
1224 NSString* role = [self role];
1225 if ([role isEqualToString:@"AXHeading"]) {
1227 if (browserAccessibility_->GetIntAttribute(
1228 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
1229 return [NSNumber numberWithInt:level];
1231 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
1232 // AXValue does not make sense for pure buttons.
1234 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
1236 bool isAriaPressedDefined;
1238 value = browserAccessibility_->GetAriaTristate(
1239 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
1244 return [NSNumber numberWithInt:value];
1246 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
1247 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
1250 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
1252 browserAccessibility_, ui::AX_STATE_SELECTED) ?
1256 if (browserAccessibility_->GetBoolAttribute(
1257 ui::AX_ATTR_BUTTON_MIXED)) {
1260 return [NSNumber numberWithInt:value];
1261 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1262 [role isEqualToString:NSAccessibilitySliderRole] ||
1263 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1264 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1266 if (browserAccessibility_->GetFloatAttribute(
1267 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
1268 return [NSNumber numberWithFloat:floatValue];
1270 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
1271 int color = browserAccessibility_->GetIntAttribute(
1272 ui::AX_ATTR_COLOR_VALUE);
1273 int red = (color >> 16) & 0xFF;
1274 int green = (color >> 8) & 0xFF;
1275 int blue = color & 0xFF;
1276 // This string matches the one returned by a native Mac color well.
1277 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
1278 red / 255., green / 255., blue / 255.];
1281 return NSStringForStringAttribute(
1282 browserAccessibility_, ui::AX_ATTR_VALUE);
1285 - (NSString*)valueDescription {
1286 return NSStringForStringAttribute(
1287 browserAccessibility_, ui::AX_ATTR_VALUE);
1290 - (NSValue*)visibleCharacterRange {
1291 std::string value = browserAccessibility_->GetStringAttribute(
1293 return [NSValue valueWithRange:NSMakeRange(0, value.size())];
1296 - (NSArray*)visibleCells {
1297 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1298 const std::vector<int32>& uniqueCellIds =
1299 browserAccessibility_->GetIntListAttribute(
1300 ui::AX_ATTR_UNIQUE_CELL_IDS);
1301 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
1302 int id = uniqueCellIds[i];
1303 BrowserAccessibility* cell =
1304 browserAccessibility_->manager()->GetFromID(id);
1306 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1311 - (NSArray*)visibleChildren {
1312 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1313 uint32 childCount = browserAccessibility_->PlatformChildCount();
1314 for (uint32 index = 0; index < childCount; ++index) {
1315 BrowserAccessibilityCocoa* child =
1316 browserAccessibility_->PlatformGetChild(index)->
1317 ToBrowserAccessibilityCocoa();
1318 [ret addObject:child];
1323 - (NSArray*)visibleColumns {
1324 return [self columns];
1327 - (NSArray*)visibleRows {
1331 - (NSNumber*)visited {
1332 return [NSNumber numberWithBool:
1333 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1337 if (!browserAccessibility_)
1340 BrowserAccessibilityManagerMac* manager =
1341 static_cast<BrowserAccessibilityManagerMac*>(
1342 browserAccessibility_->manager());
1343 if (!manager || !manager->parent_view())
1346 return [manager->parent_view() window];
1349 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1350 return [attributeToMethodNameMap objectForKey:attribute];
1353 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
1354 children_.swap(*other);
1357 // Returns the accessibility value for the given attribute. If the value isn't
1358 // supported this will return nil.
1359 - (id)accessibilityAttributeValue:(NSString*)attribute {
1360 if (!browserAccessibility_)
1364 NSSelectorFromString([self methodNameForAttribute:attribute]);
1366 return [self performSelector:selector];
1368 // TODO(dtseng): refactor remaining attributes.
1369 int selStart, selEnd;
1370 if (browserAccessibility_->GetIntAttribute(
1371 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1372 browserAccessibility_->
1373 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1374 if (selStart > selEnd)
1375 std::swap(selStart, selEnd);
1376 int selLength = selEnd - selStart;
1377 if ([attribute isEqualToString:
1378 NSAccessibilityInsertionPointLineNumberAttribute]) {
1379 const std::vector<int32>& line_breaks =
1380 browserAccessibility_->GetIntListAttribute(
1381 ui::AX_ATTR_LINE_BREAKS);
1382 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1383 if (line_breaks[i] > selStart)
1384 return [NSNumber numberWithInt:i];
1386 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1388 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1389 base::string16 value = browserAccessibility_->GetString16Attribute(
1391 return base::SysUTF16ToNSString(value.substr(selStart, selLength));
1393 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1394 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1400 // Returns the accessibility value for the given attribute and parameter. If the
1401 // value isn't supported this will return nil.
1402 - (id)accessibilityAttributeValue:(NSString*)attribute
1403 forParameter:(id)parameter {
1404 if (!browserAccessibility_)
1407 const std::vector<int32>& line_breaks =
1408 browserAccessibility_->GetIntListAttribute(
1409 ui::AX_ATTR_LINE_BREAKS);
1410 std::string value = browserAccessibility_->GetStringAttribute(
1412 int len = static_cast<int>(value.size());
1414 if ([attribute isEqualToString:
1415 NSAccessibilityStringForRangeParameterizedAttribute]) {
1416 NSRange range = [(NSValue*)parameter rangeValue];
1417 base::string16 value = browserAccessibility_->GetString16Attribute(
1419 return base::SysUTF16ToNSString(value.substr(range.location, range.length));
1422 if ([attribute isEqualToString:
1423 NSAccessibilityLineForIndexParameterizedAttribute]) {
1424 int index = [(NSNumber*)parameter intValue];
1425 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1426 if (line_breaks[i] > index)
1427 return [NSNumber numberWithInt:i];
1429 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1432 if ([attribute isEqualToString:
1433 NSAccessibilityRangeForLineParameterizedAttribute]) {
1434 int line_index = [(NSNumber*)parameter intValue];
1435 int line_count = static_cast<int>(line_breaks.size()) + 1;
1436 if (line_index < 0 || line_index >= line_count)
1438 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1439 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1440 return [NSValue valueWithRange:
1441 NSMakeRange(start, end - start)];
1444 if ([attribute isEqualToString:
1445 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1446 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1447 [self internalRole] != ui::AX_ROLE_GRID) {
1450 if (![parameter isKindOfClass:[NSArray self]])
1452 NSArray* array = parameter;
1453 int column = [[array objectAtIndex:0] intValue];
1454 int row = [[array objectAtIndex:1] intValue];
1455 int num_columns = browserAccessibility_->GetIntAttribute(
1456 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1457 int num_rows = browserAccessibility_->GetIntAttribute(
1458 ui::AX_ATTR_TABLE_ROW_COUNT);
1459 if (column < 0 || column >= num_columns ||
1460 row < 0 || row >= num_rows) {
1464 i < browserAccessibility_->PlatformChildCount();
1466 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1467 if (child->GetRole() != ui::AX_ROLE_ROW)
1470 if (!child->GetIntAttribute(
1471 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1479 j < child->PlatformChildCount();
1481 BrowserAccessibility* cell = child->PlatformGetChild(j);
1482 if (!cell->IsCellOrTableHeaderRole())
1485 if (!cell->GetIntAttribute(
1486 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1490 if (colIndex == column)
1491 return cell->ToBrowserAccessibilityCocoa();
1492 if (colIndex > column)
1499 if ([attribute isEqualToString:
1500 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1501 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1503 NSRange range = [(NSValue*)parameter rangeValue];
1504 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1505 range.location, range.length);
1506 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1507 NSSize size = NSMakeSize(rect.width(), rect.height());
1508 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1509 NSRect nsrect = NSMakeRect(
1510 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1511 return [NSValue valueWithRect:nsrect];
1513 if ([attribute isEqualToString:@"AXUIElementCountForSearchPredicate"]) {
1514 OneShotAccessibilityTreeSearch search(browserAccessibility_->manager());
1515 if (InitializeAccessibilityTreeSearch(&search, parameter))
1516 return [NSNumber numberWithInt:search.CountMatches()];
1520 if ([attribute isEqualToString:@"AXUIElementsForSearchPredicate"]) {
1521 OneShotAccessibilityTreeSearch search(browserAccessibility_->manager());
1522 if (InitializeAccessibilityTreeSearch(&search, parameter)) {
1523 size_t count = search.CountMatches();
1524 NSMutableArray* result = [NSMutableArray arrayWithCapacity:count];
1525 for (size_t i = 0; i < count; ++i) {
1526 BrowserAccessibility* match = search.GetMatchAtIndex(i);
1527 [result addObject:match->ToBrowserAccessibilityCocoa()];
1534 // TODO(dtseng): support the following attributes.
1535 if ([attribute isEqualTo:
1536 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1537 [attribute isEqualTo:
1538 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1539 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1540 [attribute isEqualTo:
1541 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1547 // Returns an array of parameterized attributes names that this object will
1549 - (NSArray*)accessibilityParameterizedAttributeNames {
1550 if (!browserAccessibility_)
1553 // General attributes.
1554 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1555 @"AXUIElementCountForSearchPredicate",
1556 @"AXUIElementsForSearchPredicate",
1559 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1560 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1561 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1562 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1565 if (browserAccessibility_->IsEditableText()) {
1566 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1567 NSAccessibilityLineForIndexParameterizedAttribute,
1568 NSAccessibilityRangeForLineParameterizedAttribute,
1569 NSAccessibilityStringForRangeParameterizedAttribute,
1570 NSAccessibilityRangeForPositionParameterizedAttribute,
1571 NSAccessibilityRangeForIndexParameterizedAttribute,
1572 NSAccessibilityBoundsForRangeParameterizedAttribute,
1573 NSAccessibilityRTFForRangeParameterizedAttribute,
1574 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1575 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1578 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1579 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1580 NSAccessibilityBoundsForRangeParameterizedAttribute,
1586 // Returns an array of action names that this object will respond to.
1587 - (NSArray*)accessibilityActionNames {
1588 if (!browserAccessibility_)
1591 NSMutableArray* ret =
1592 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1593 NSString* role = [self role];
1594 // TODO(dtseng): this should only get set when there's a default action.
1595 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1596 ![role isEqualToString:NSAccessibilityTextFieldRole] &&
1597 ![role isEqualToString:NSAccessibilityTextAreaRole]) {
1598 [ret addObject:NSAccessibilityPressAction];
1604 // Returns a sub-array of values for the given attribute value, starting at
1605 // index, with up to maxCount items. If the given index is out of bounds,
1606 // or there are no values for the given attribute, it will return nil.
1607 // This method is used for querying subsets of values, without having to
1608 // return a large set of data, such as elements with a large number of
1610 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1611 index:(NSUInteger)index
1612 maxCount:(NSUInteger)maxCount {
1613 if (!browserAccessibility_)
1616 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1619 NSUInteger arrayCount = [fullArray count];
1620 if (index >= arrayCount)
1623 if ((index + maxCount) > arrayCount) {
1624 subRange = NSMakeRange(index, arrayCount - index);
1626 subRange = NSMakeRange(index, maxCount);
1628 return [fullArray subarrayWithRange:subRange];
1631 // Returns the count of the specified accessibility array attribute.
1632 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1633 if (!browserAccessibility_)
1636 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1637 return [fullArray count];
1640 // Returns the list of accessibility attributes that this object supports.
1641 - (NSArray*)accessibilityAttributeNames {
1642 if (!browserAccessibility_)
1645 // General attributes.
1646 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1647 NSAccessibilityChildrenAttribute,
1648 NSAccessibilityDescriptionAttribute,
1649 NSAccessibilityEnabledAttribute,
1650 NSAccessibilityFocusedAttribute,
1651 NSAccessibilityHelpAttribute,
1652 NSAccessibilityLinkedUIElementsAttribute,
1653 NSAccessibilityParentAttribute,
1654 NSAccessibilityPositionAttribute,
1655 NSAccessibilityRoleAttribute,
1656 NSAccessibilityRoleDescriptionAttribute,
1657 NSAccessibilitySizeAttribute,
1658 NSAccessibilitySubroleAttribute,
1659 NSAccessibilityTitleAttribute,
1660 NSAccessibilityTopLevelUIElementAttribute,
1661 NSAccessibilityValueAttribute,
1662 NSAccessibilityWindowAttribute,
1668 // Specific role attributes.
1669 NSString* role = [self role];
1670 NSString* subrole = [self subrole];
1671 if ([role isEqualToString:NSAccessibilityTableRole] ||
1672 [role isEqualToString:NSAccessibilityGridRole]) {
1673 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1674 NSAccessibilityColumnsAttribute,
1675 NSAccessibilityVisibleColumnsAttribute,
1676 NSAccessibilityRowsAttribute,
1677 NSAccessibilityVisibleRowsAttribute,
1678 NSAccessibilityVisibleCellsAttribute,
1679 NSAccessibilityHeaderAttribute,
1680 NSAccessibilityColumnHeaderUIElementsAttribute,
1681 NSAccessibilityRowHeaderUIElementsAttribute,
1683 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1684 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1685 NSAccessibilityIndexAttribute,
1686 NSAccessibilityHeaderAttribute,
1687 NSAccessibilityRowsAttribute,
1688 NSAccessibilityVisibleRowsAttribute,
1690 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1691 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1692 NSAccessibilityColumnIndexRangeAttribute,
1693 NSAccessibilityRowIndexRangeAttribute,
1696 } else if ([role isEqualToString:@"AXWebArea"]) {
1697 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1699 @"AXLoadingProgress",
1701 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1702 [ret addObject:NSAccessibilityTabsAttribute];
1703 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1704 [role isEqualToString:NSAccessibilitySliderRole] ||
1705 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1706 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1707 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1708 NSAccessibilityMaxValueAttribute,
1709 NSAccessibilityMinValueAttribute,
1710 NSAccessibilityValueDescriptionAttribute,
1712 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1713 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1714 NSAccessibilityDisclosingAttribute,
1715 NSAccessibilityDisclosedByRowAttribute,
1716 NSAccessibilityDisclosureLevelAttribute,
1717 NSAccessibilityDisclosedRowsAttribute,
1719 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1720 if (browserAccessibility_->GetParent()) {
1721 base::string16 parentRole;
1722 browserAccessibility_->GetParent()->GetHtmlAttribute(
1723 "role", &parentRole);
1724 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1725 if (parentRole == treegridRole) {
1726 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1727 NSAccessibilityDisclosingAttribute,
1728 NSAccessibilityDisclosedByRowAttribute,
1729 NSAccessibilityDisclosureLevelAttribute,
1730 NSAccessibilityDisclosedRowsAttribute,
1733 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1734 NSAccessibilityIndexAttribute,
1738 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1739 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1740 NSAccessibilitySelectedChildrenAttribute,
1741 NSAccessibilityVisibleChildrenAttribute,
1745 // Caret navigation and text selection attributes.
1746 if (browserAccessibility_->IsEditableText()) {
1747 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1748 NSAccessibilityInsertionPointLineNumberAttribute,
1749 NSAccessibilityNumberOfCharactersAttribute,
1750 NSAccessibilitySelectedTextAttribute,
1751 NSAccessibilitySelectedTextRangeAttribute,
1752 NSAccessibilityVisibleCharacterRangeAttribute,
1756 // Add the url attribute only if it has a valid url.
1757 if ([self url] != nil) {
1758 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1759 NSAccessibilityURLAttribute,
1763 // Position in set and Set size
1764 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_POS_IN_SET)) {
1765 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1769 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_SET_SIZE)) {
1770 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1776 if (browserAccessibility_->HasStringAttribute(
1777 ui::AX_ATTR_LIVE_STATUS)) {
1778 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1782 if (browserAccessibility_->HasStringAttribute(
1783 ui::AX_ATTR_LIVE_RELEVANT)) {
1784 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1788 if (browserAccessibility_->HasBoolAttribute(
1789 ui::AX_ATTR_LIVE_ATOMIC)) {
1790 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1794 if (browserAccessibility_->HasBoolAttribute(
1795 ui::AX_ATTR_LIVE_BUSY)) {
1796 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1801 std::string dropEffect;
1802 if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffect)) {
1803 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1808 std::string grabbed;
1809 if (browserAccessibility_->GetHtmlAttribute("aria-grabbed", &grabbed)) {
1810 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1815 // Add expanded attribute only if it has expanded or collapsed state.
1816 if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) ||
1817 GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) {
1818 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1819 NSAccessibilityExpandedAttribute,
1823 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)
1824 || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) {
1825 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1826 NSAccessibilityOrientationAttribute, nil]];
1829 if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) {
1830 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1831 NSAccessibilityPlaceholderValueAttribute, nil]];
1834 if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) {
1835 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1836 @"AXRequired", nil]];
1839 // Title UI Element.
1840 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1841 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1842 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1844 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1845 NSAccessibilityTitleUIElementAttribute,
1848 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1849 // for elements which are referred to by labelledby or are labels
1854 // Returns the index of the child in this objects array of children.
1855 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1856 if (!browserAccessibility_)
1859 NSUInteger index = 0;
1860 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1861 if ([child isEqual:childToCheck])
1868 // Returns whether or not the specified attribute can be set by the
1869 // accessibility API via |accessibilitySetValue:forAttribute:|.
1870 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1871 if (!browserAccessibility_)
1874 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1875 return GetState(browserAccessibility_,
1876 ui::AX_STATE_FOCUSABLE);
1877 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1878 return browserAccessibility_->GetBoolAttribute(
1879 ui::AX_ATTR_CAN_SET_VALUE);
1881 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1882 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1883 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1889 // Returns whether or not this object should be ignored in the accessibility
1891 - (BOOL)accessibilityIsIgnored {
1892 if (!browserAccessibility_)
1895 return [self isIgnored];
1898 // Performs the given accessibility action on the webkit accessibility object
1899 // that backs this object.
1900 - (void)accessibilityPerformAction:(NSString*)action {
1901 if (!browserAccessibility_)
1904 // TODO(dmazzoni): Support more actions.
1905 if ([action isEqualToString:NSAccessibilityPressAction]) {
1906 [self delegate]->AccessibilityDoDefaultAction(
1907 browserAccessibility_->GetId());
1908 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1909 [self delegate]->AccessibilityShowContextMenu(
1910 browserAccessibility_->GetId());
1914 // Returns the description of the given action.
1915 - (NSString*)accessibilityActionDescription:(NSString*)action {
1916 if (!browserAccessibility_)
1919 return NSAccessibilityActionDescription(action);
1922 // Sets an override value for a specific accessibility attribute.
1923 // This class does not support this.
1924 - (BOOL)accessibilitySetOverrideValue:(id)value
1925 forAttribute:(NSString*)attribute {
1929 // Sets the value for an accessibility attribute via the accessibility API.
1930 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1931 if (!browserAccessibility_)
1934 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1935 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1936 NSNumber* focusedNumber = value;
1937 BOOL focused = [focusedNumber intValue];
1939 manager->SetFocus(browserAccessibility_, true);
1941 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1942 NSRange range = [(NSValue*)value rangeValue];
1943 [self delegate]->AccessibilitySetTextSelection(
1944 browserAccessibility_->GetId(),
1945 range.location, range.location + range.length);
1949 // Returns the deepest accessibility child that should not be ignored.
1950 // It is assumed that the hit test has been narrowed down to this object
1951 // or one of its children, so this will never return nil unless this
1952 // object is invalid.
1953 - (id)accessibilityHitTest:(NSPoint)point {
1954 if (!browserAccessibility_)
1957 BrowserAccessibilityCocoa* hit = self;
1958 for (BrowserAccessibilityCocoa* child in [self children]) {
1959 if (!child->browserAccessibility_)
1961 NSPoint origin = [child origin];
1962 NSSize size = [[child size] sizeValue];
1964 rect.origin = origin;
1966 if (NSPointInRect(point, rect)) {
1968 id childResult = [child accessibilityHitTest:point];
1969 if (![childResult accessibilityIsIgnored]) {
1975 return NSAccessibilityUnignoredAncestor(hit);
1978 - (BOOL)isEqual:(id)object {
1979 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1981 return ([self hash] == [object hash]);
1984 - (NSUInteger)hash {
1985 // Potentially called during dealloc.
1986 if (!browserAccessibility_)
1987 return [super hash];
1988 return browserAccessibility_->GetId();
1991 - (BOOL)accessibilityShouldUseUniqueId {