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 { NSAccessibilityPositionAttribute, @"position" },
366 { NSAccessibilityRoleAttribute, @"role" },
367 { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
368 { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
369 { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
370 { NSAccessibilityRowsAttribute, @"rows" },
371 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
372 { NSAccessibilitySelectedChildrenAttribute, @"selectedChildren" },
373 { NSAccessibilitySizeAttribute, @"size" },
374 { NSAccessibilitySubroleAttribute, @"subrole" },
375 { NSAccessibilityTabsAttribute, @"tabs" },
376 { NSAccessibilityTitleAttribute, @"title" },
377 { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
378 { NSAccessibilityTopLevelUIElementAttribute, @"window" },
379 { NSAccessibilityURLAttribute, @"url" },
380 { NSAccessibilityValueAttribute, @"value" },
381 { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
382 { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
383 { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
384 { NSAccessibilityVisibleChildrenAttribute, @"visibleChildren" },
385 { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
386 { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
387 { NSAccessibilityWindowAttribute, @"window" },
388 { @"AXAccessKey", @"accessKey" },
389 { @"AXARIAAtomic", @"ariaAtomic" },
390 { @"AXARIABusy", @"ariaBusy" },
391 { @"AXARIALive", @"ariaLive" },
392 { @"AXARIASetSize", @"ariaSetSize" },
393 { @"AXARIAPosInSet", @"ariaPosInSet" },
394 { @"AXARIARelevant", @"ariaRelevant" },
395 { @"AXDropEffects", @"dropeffect" },
396 { @"AXGrabbed", @"grabbed" },
397 { @"AXInvalid", @"invalid" },
398 { @"AXLoaded", @"loaded" },
399 { @"AXLoadingProgress", @"loadingProgress" },
400 { @"AXPlaceholder", @"placeholder" },
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*)placeholder {
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_COMPLEMENTARY:
948 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
949 IDS_AX_ROLE_COMPLEMENTARY));
950 case ui::AX_ROLE_CONTENT_INFO:
951 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
952 IDS_AX_ROLE_ADDRESS));
953 case ui::AX_ROLE_DESCRIPTION_LIST:
954 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
955 IDS_AX_ROLE_DESCRIPTION_LIST));
956 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
957 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
958 IDS_AX_ROLE_DESCRIPTION_DETAIL));
959 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
960 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
961 IDS_AX_ROLE_DESCRIPTION_TERM));
962 case ui::AX_ROLE_FIGURE:
963 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
964 IDS_AX_ROLE_FIGURE));
965 case ui::AX_ROLE_FOOTER:
966 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
967 IDS_AX_ROLE_FOOTER));
968 case ui::AX_ROLE_FORM:
969 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
971 case ui::AX_ROLE_MAIN:
972 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
973 IDS_AX_ROLE_MAIN_CONTENT));
974 case ui::AX_ROLE_MARK:
975 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
977 case ui::AX_ROLE_MATH:
978 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
980 case ui::AX_ROLE_NAVIGATION:
981 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
982 IDS_AX_ROLE_NAVIGATIONAL_LINK));
983 case ui::AX_ROLE_REGION:
984 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
985 IDS_AX_ROLE_REGION));
986 case ui::AX_ROLE_SPIN_BUTTON:
987 // This control is similar to what VoiceOver calls a "stepper".
988 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
989 IDS_AX_ROLE_STEPPER));
990 case ui::AX_ROLE_STATUS:
991 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
992 IDS_AX_ROLE_STATUS));
993 case ui::AX_ROLE_SEARCH_BOX:
994 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
995 IDS_AX_ROLE_SEARCH_BOX));
996 case ui::AX_ROLE_SWITCH:
997 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
998 IDS_AX_ROLE_SWITCH));
999 case ui::AX_ROLE_TOGGLE_BUTTON:
1000 return base::SysUTF16ToNSString(content_client->GetLocalizedString(
1001 IDS_AX_ROLE_TOGGLE_BUTTON));
1006 return NSAccessibilityRoleDescription(role, nil);
1009 - (NSArray*)rowHeaders {
1010 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1011 [self internalRole] != ui::AX_ROLE_GRID) {
1015 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1016 const std::vector<int32>& uniqueCellIds =
1017 browserAccessibility_->GetIntListAttribute(
1018 ui::AX_ATTR_UNIQUE_CELL_IDS);
1019 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
1020 int id = uniqueCellIds[i];
1021 BrowserAccessibility* cell =
1022 browserAccessibility_->manager()->GetFromID(id);
1023 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1024 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1029 - (NSValue*)rowIndexRange {
1030 if (!browserAccessibility_->IsCellOrTableHeaderRole())
1035 browserAccessibility_->GetIntAttribute(
1036 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1037 browserAccessibility_->GetIntAttribute(
1038 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
1039 if (row >= 0 && rowspan >= 1)
1040 return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
1045 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1047 if ([self internalRole] == ui::AX_ROLE_TABLE||
1048 [self internalRole] == ui::AX_ROLE_GRID) {
1049 for (BrowserAccessibilityCocoa* child in [self children]) {
1050 if ([[child role] isEqualToString:NSAccessibilityRowRole])
1051 [ret addObject:child];
1053 } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
1054 const std::vector<int32>& indirectChildIds =
1055 browserAccessibility_->GetIntListAttribute(
1056 ui::AX_ATTR_INDIRECT_CHILD_IDS);
1057 for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
1058 int id = indirectChildIds[i];
1059 BrowserAccessibility* rowElement =
1060 browserAccessibility_->manager()->GetFromID(id);
1062 [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
1069 - (NSArray*)selectedChildren {
1070 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1071 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1072 BrowserAccessibility* focusedChild = manager->GetFocus(browserAccessibility_);
1074 // If it's not multiselectable, try to skip iterating over the
1076 if (!GetState(browserAccessibility_, ui::AX_STATE_MULTISELECTABLE)) {
1077 // First try the focused child.
1078 if (focusedChild && focusedChild != browserAccessibility_) {
1079 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
1083 // Next try the active descendant.
1084 int activeDescendantId;
1085 if (browserAccessibility_->GetIntAttribute(
1086 ui::AX_ATTR_ACTIVEDESCENDANT_ID, &activeDescendantId)) {
1087 BrowserAccessibility* activeDescendant =
1088 manager->GetFromID(activeDescendantId);
1089 if (activeDescendant) {
1090 [ret addObject:activeDescendant->ToBrowserAccessibilityCocoa()];
1096 // If it's multiselectable or if the previous attempts failed,
1097 // return any children with the "selected" state, which may
1098 // come from aria-selected.
1099 uint32 childCount = browserAccessibility_->PlatformChildCount();
1100 for (uint32 index = 0; index < childCount; ++index) {
1101 BrowserAccessibility* child =
1102 browserAccessibility_->PlatformGetChild(index);
1103 if (child->HasState(ui::AX_STATE_SELECTED))
1104 [ret addObject:child->ToBrowserAccessibilityCocoa()];
1107 // And if nothing's selected but one has focus, use the focused one.
1108 if ([ret count] == 0 &&
1110 focusedChild != browserAccessibility_) {
1111 [ret addObject:focusedChild->ToBrowserAccessibilityCocoa()];
1117 // Returns the size of this object.
1119 gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
1120 return [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
1123 - (NSString*)sortDirection {
1125 if (!browserAccessibility_->GetIntAttribute(
1126 ui::AX_ATTR_SORT_DIRECTION, &sortDirection))
1129 switch (sortDirection) {
1130 case ui::AX_SORT_DIRECTION_UNSORTED:
1132 case ui::AX_SORT_DIRECTION_ASCENDING:
1133 return @"AXSortDirectionAscending";
1134 case ui::AX_SORT_DIRECTION_DESCENDING:
1135 return @"AXSortDirectionDescending";
1136 case ui::AX_SORT_DIRECTION_OTHER:
1137 return @"AXSortDirectionUnknown";
1145 // Returns a subrole based upon the role.
1146 - (NSString*) subrole {
1147 ui::AXRole browserAccessibilityRole = [self internalRole];
1148 if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
1149 GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
1150 return @"AXSecureTextField";
1153 if (browserAccessibilityRole == ui::AX_ROLE_DESCRIPTION_LIST)
1154 return @"AXDefinitionList";
1156 if (browserAccessibilityRole == ui::AX_ROLE_LIST)
1157 return @"AXContentList";
1159 return [AXPlatformNodeCocoa nativeSubroleFromAXRole:browserAccessibilityRole];
1162 // Returns all tabs in this subtree.
1164 NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
1166 if ([self internalRole] == ui::AX_ROLE_TAB)
1167 [tabSubtree addObject:self];
1169 for (uint i=0; i < [[self children] count]; ++i) {
1170 NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
1171 if ([tabChildren count] > 0)
1172 [tabSubtree addObjectsFromArray:tabChildren];
1178 - (NSString*)title {
1179 return NSStringForStringAttribute(
1180 browserAccessibility_, ui::AX_ATTR_NAME);
1183 - (id)titleUIElement {
1185 if (browserAccessibility_->GetIntAttribute(
1186 ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
1187 BrowserAccessibility* titleElement =
1188 browserAccessibility_->manager()->GetFromID(titleElementId);
1190 return titleElement->ToBrowserAccessibilityCocoa();
1192 std::vector<int32> labelledby_ids =
1193 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
1194 if (labelledby_ids.size() == 1) {
1195 BrowserAccessibility* titleElement =
1196 browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
1198 return titleElement->ToBrowserAccessibilityCocoa();
1205 StringAttribute urlAttribute =
1206 [[self role] isEqualToString:@"AXWebArea"] ?
1207 ui::AX_ATTR_DOC_URL :
1210 std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
1214 return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
1218 // WebCore uses an attachmentView to get the below behavior.
1219 // We do not have any native views backing this object, so need
1220 // to approximate Cocoa ax behavior best as we can.
1221 NSString* role = [self role];
1222 if ([role isEqualToString:@"AXHeading"]) {
1224 if (browserAccessibility_->GetIntAttribute(
1225 ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
1226 return [NSNumber numberWithInt:level];
1228 } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
1229 // AXValue does not make sense for pure buttons.
1231 } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
1233 bool isAriaPressedDefined;
1235 value = browserAccessibility_->GetAriaTristate(
1236 "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
1241 return [NSNumber numberWithInt:value];
1243 } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
1244 [role isEqualToString:NSAccessibilityRadioButtonRole]) {
1247 browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
1249 browserAccessibility_, ui::AX_STATE_SELECTED) ?
1253 if (browserAccessibility_->GetBoolAttribute(
1254 ui::AX_ATTR_BUTTON_MIXED)) {
1257 return [NSNumber numberWithInt:value];
1258 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1259 [role isEqualToString:NSAccessibilitySliderRole] ||
1260 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1261 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1263 if (browserAccessibility_->GetFloatAttribute(
1264 ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
1265 return [NSNumber numberWithFloat:floatValue];
1267 } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
1268 int color = browserAccessibility_->GetIntAttribute(
1269 ui::AX_ATTR_COLOR_VALUE);
1270 int red = (color >> 16) & 0xFF;
1271 int green = (color >> 8) & 0xFF;
1272 int blue = color & 0xFF;
1273 // This string matches the one returned by a native Mac color well.
1274 return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
1275 red / 255., green / 255., blue / 255.];
1278 return NSStringForStringAttribute(
1279 browserAccessibility_, ui::AX_ATTR_VALUE);
1282 - (NSString*)valueDescription {
1283 return NSStringForStringAttribute(
1284 browserAccessibility_, ui::AX_ATTR_VALUE);
1287 - (NSValue*)visibleCharacterRange {
1288 std::string value = browserAccessibility_->GetStringAttribute(
1290 return [NSValue valueWithRange:NSMakeRange(0, value.size())];
1293 - (NSArray*)visibleCells {
1294 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1295 const std::vector<int32>& uniqueCellIds =
1296 browserAccessibility_->GetIntListAttribute(
1297 ui::AX_ATTR_UNIQUE_CELL_IDS);
1298 for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
1299 int id = uniqueCellIds[i];
1300 BrowserAccessibility* cell =
1301 browserAccessibility_->manager()->GetFromID(id);
1303 [ret addObject:cell->ToBrowserAccessibilityCocoa()];
1308 - (NSArray*)visibleChildren {
1309 NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
1310 uint32 childCount = browserAccessibility_->PlatformChildCount();
1311 for (uint32 index = 0; index < childCount; ++index) {
1312 BrowserAccessibilityCocoa* child =
1313 browserAccessibility_->PlatformGetChild(index)->
1314 ToBrowserAccessibilityCocoa();
1315 [ret addObject:child];
1320 - (NSArray*)visibleColumns {
1321 return [self columns];
1324 - (NSArray*)visibleRows {
1328 - (NSNumber*)visited {
1329 return [NSNumber numberWithBool:
1330 GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
1334 if (!browserAccessibility_)
1337 BrowserAccessibilityManagerMac* manager =
1338 static_cast<BrowserAccessibilityManagerMac*>(
1339 browserAccessibility_->manager());
1340 if (!manager || !manager->parent_view())
1343 return [manager->parent_view() window];
1346 - (NSString*)methodNameForAttribute:(NSString*)attribute {
1347 return [attributeToMethodNameMap objectForKey:attribute];
1350 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
1351 children_.swap(*other);
1354 // Returns the accessibility value for the given attribute. If the value isn't
1355 // supported this will return nil.
1356 - (id)accessibilityAttributeValue:(NSString*)attribute {
1357 if (!browserAccessibility_)
1361 NSSelectorFromString([self methodNameForAttribute:attribute]);
1363 return [self performSelector:selector];
1365 // TODO(dtseng): refactor remaining attributes.
1366 int selStart, selEnd;
1367 if (browserAccessibility_->GetIntAttribute(
1368 ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
1369 browserAccessibility_->
1370 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
1371 if (selStart > selEnd)
1372 std::swap(selStart, selEnd);
1373 int selLength = selEnd - selStart;
1374 if ([attribute isEqualToString:
1375 NSAccessibilityInsertionPointLineNumberAttribute]) {
1376 const std::vector<int32>& line_breaks =
1377 browserAccessibility_->GetIntListAttribute(
1378 ui::AX_ATTR_LINE_BREAKS);
1379 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1380 if (line_breaks[i] > selStart)
1381 return [NSNumber numberWithInt:i];
1383 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1385 if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
1386 base::string16 value = browserAccessibility_->GetString16Attribute(
1388 return base::SysUTF16ToNSString(value.substr(selStart, selLength));
1390 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1391 return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
1397 // Returns the accessibility value for the given attribute and parameter. If the
1398 // value isn't supported this will return nil.
1399 - (id)accessibilityAttributeValue:(NSString*)attribute
1400 forParameter:(id)parameter {
1401 if (!browserAccessibility_)
1404 const std::vector<int32>& line_breaks =
1405 browserAccessibility_->GetIntListAttribute(
1406 ui::AX_ATTR_LINE_BREAKS);
1407 std::string value = browserAccessibility_->GetStringAttribute(
1409 int len = static_cast<int>(value.size());
1411 if ([attribute isEqualToString:
1412 NSAccessibilityStringForRangeParameterizedAttribute]) {
1413 NSRange range = [(NSValue*)parameter rangeValue];
1414 base::string16 value = browserAccessibility_->GetString16Attribute(
1416 return base::SysUTF16ToNSString(value.substr(range.location, range.length));
1419 if ([attribute isEqualToString:
1420 NSAccessibilityLineForIndexParameterizedAttribute]) {
1421 int index = [(NSNumber*)parameter intValue];
1422 for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
1423 if (line_breaks[i] > index)
1424 return [NSNumber numberWithInt:i];
1426 return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
1429 if ([attribute isEqualToString:
1430 NSAccessibilityRangeForLineParameterizedAttribute]) {
1431 int line_index = [(NSNumber*)parameter intValue];
1432 int line_count = static_cast<int>(line_breaks.size()) + 1;
1433 if (line_index < 0 || line_index >= line_count)
1435 int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
1436 int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
1437 return [NSValue valueWithRange:
1438 NSMakeRange(start, end - start)];
1441 if ([attribute isEqualToString:
1442 NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1443 if ([self internalRole] != ui::AX_ROLE_TABLE &&
1444 [self internalRole] != ui::AX_ROLE_GRID) {
1447 if (![parameter isKindOfClass:[NSArray self]])
1449 NSArray* array = parameter;
1450 int column = [[array objectAtIndex:0] intValue];
1451 int row = [[array objectAtIndex:1] intValue];
1452 int num_columns = browserAccessibility_->GetIntAttribute(
1453 ui::AX_ATTR_TABLE_COLUMN_COUNT);
1454 int num_rows = browserAccessibility_->GetIntAttribute(
1455 ui::AX_ATTR_TABLE_ROW_COUNT);
1456 if (column < 0 || column >= num_columns ||
1457 row < 0 || row >= num_rows) {
1461 i < browserAccessibility_->PlatformChildCount();
1463 BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
1464 if (child->GetRole() != ui::AX_ROLE_ROW)
1467 if (!child->GetIntAttribute(
1468 ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
1476 j < child->PlatformChildCount();
1478 BrowserAccessibility* cell = child->PlatformGetChild(j);
1479 if (!cell->IsCellOrTableHeaderRole())
1482 if (!cell->GetIntAttribute(
1483 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
1487 if (colIndex == column)
1488 return cell->ToBrowserAccessibilityCocoa();
1489 if (colIndex > column)
1496 if ([attribute isEqualToString:
1497 NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1498 if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
1500 NSRange range = [(NSValue*)parameter rangeValue];
1501 gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
1502 range.location, range.length);
1503 NSPoint origin = NSMakePoint(rect.x(), rect.y());
1504 NSSize size = NSMakeSize(rect.width(), rect.height());
1505 NSPoint pointInScreen = [self pointInScreen:origin size:size];
1506 NSRect nsrect = NSMakeRect(
1507 pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
1508 return [NSValue valueWithRect:nsrect];
1510 if ([attribute isEqualToString:@"AXUIElementCountForSearchPredicate"]) {
1511 OneShotAccessibilityTreeSearch search(browserAccessibility_->manager());
1512 if (InitializeAccessibilityTreeSearch(&search, parameter))
1513 return [NSNumber numberWithInt:search.CountMatches()];
1517 if ([attribute isEqualToString:@"AXUIElementsForSearchPredicate"]) {
1518 OneShotAccessibilityTreeSearch search(browserAccessibility_->manager());
1519 if (InitializeAccessibilityTreeSearch(&search, parameter)) {
1520 size_t count = search.CountMatches();
1521 NSMutableArray* result = [NSMutableArray arrayWithCapacity:count];
1522 for (size_t i = 0; i < count; ++i) {
1523 BrowserAccessibility* match = search.GetMatchAtIndex(i);
1524 [result addObject:match->ToBrowserAccessibilityCocoa()];
1531 // TODO(dtseng): support the following attributes.
1532 if ([attribute isEqualTo:
1533 NSAccessibilityRangeForPositionParameterizedAttribute] ||
1534 [attribute isEqualTo:
1535 NSAccessibilityRangeForIndexParameterizedAttribute] ||
1536 [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
1537 [attribute isEqualTo:
1538 NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
1544 // Returns an array of parameterized attributes names that this object will
1546 - (NSArray*)accessibilityParameterizedAttributeNames {
1547 if (!browserAccessibility_)
1550 // General attributes.
1551 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1552 @"AXUIElementCountForSearchPredicate",
1553 @"AXUIElementsForSearchPredicate",
1556 if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
1557 [[self role] isEqualToString:NSAccessibilityGridRole]) {
1558 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1559 NSAccessibilityCellForColumnAndRowParameterizedAttribute,
1562 if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1563 [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
1564 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1565 NSAccessibilityLineForIndexParameterizedAttribute,
1566 NSAccessibilityRangeForLineParameterizedAttribute,
1567 NSAccessibilityStringForRangeParameterizedAttribute,
1568 NSAccessibilityRangeForPositionParameterizedAttribute,
1569 NSAccessibilityRangeForIndexParameterizedAttribute,
1570 NSAccessibilityBoundsForRangeParameterizedAttribute,
1571 NSAccessibilityRTFForRangeParameterizedAttribute,
1572 NSAccessibilityAttributedStringForRangeParameterizedAttribute,
1573 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
1576 if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
1577 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1578 NSAccessibilityBoundsForRangeParameterizedAttribute,
1584 // Returns an array of action names that this object will respond to.
1585 - (NSArray*)accessibilityActionNames {
1586 if (!browserAccessibility_)
1589 NSMutableArray* ret =
1590 [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
1591 NSString* role = [self role];
1592 // TODO(dtseng): this should only get set when there's a default action.
1593 if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
1594 ![role isEqualToString:NSAccessibilityTextFieldRole] &&
1595 ![role isEqualToString:NSAccessibilityTextAreaRole]) {
1596 [ret addObject:NSAccessibilityPressAction];
1602 // Returns a sub-array of values for the given attribute value, starting at
1603 // index, with up to maxCount items. If the given index is out of bounds,
1604 // or there are no values for the given attribute, it will return nil.
1605 // This method is used for querying subsets of values, without having to
1606 // return a large set of data, such as elements with a large number of
1608 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
1609 index:(NSUInteger)index
1610 maxCount:(NSUInteger)maxCount {
1611 if (!browserAccessibility_)
1614 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1617 NSUInteger arrayCount = [fullArray count];
1618 if (index >= arrayCount)
1621 if ((index + maxCount) > arrayCount) {
1622 subRange = NSMakeRange(index, arrayCount - index);
1624 subRange = NSMakeRange(index, maxCount);
1626 return [fullArray subarrayWithRange:subRange];
1629 // Returns the count of the specified accessibility array attribute.
1630 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
1631 if (!browserAccessibility_)
1634 NSArray* fullArray = [self accessibilityAttributeValue:attribute];
1635 return [fullArray count];
1638 // Returns the list of accessibility attributes that this object supports.
1639 - (NSArray*)accessibilityAttributeNames {
1640 if (!browserAccessibility_)
1643 // General attributes.
1644 NSMutableArray* ret = [NSMutableArray arrayWithObjects:
1645 NSAccessibilityChildrenAttribute,
1646 NSAccessibilityDescriptionAttribute,
1647 NSAccessibilityEnabledAttribute,
1648 NSAccessibilityFocusedAttribute,
1649 NSAccessibilityHelpAttribute,
1650 NSAccessibilityLinkedUIElementsAttribute,
1651 NSAccessibilityParentAttribute,
1652 NSAccessibilityPositionAttribute,
1653 NSAccessibilityRoleAttribute,
1654 NSAccessibilityRoleDescriptionAttribute,
1655 NSAccessibilitySizeAttribute,
1656 NSAccessibilitySubroleAttribute,
1657 NSAccessibilityTitleAttribute,
1658 NSAccessibilityTopLevelUIElementAttribute,
1659 NSAccessibilityValueAttribute,
1660 NSAccessibilityWindowAttribute,
1666 // Specific role attributes.
1667 NSString* role = [self role];
1668 NSString* subrole = [self subrole];
1669 if ([role isEqualToString:NSAccessibilityTableRole] ||
1670 [role isEqualToString:NSAccessibilityGridRole]) {
1671 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1672 NSAccessibilityColumnsAttribute,
1673 NSAccessibilityVisibleColumnsAttribute,
1674 NSAccessibilityRowsAttribute,
1675 NSAccessibilityVisibleRowsAttribute,
1676 NSAccessibilityVisibleCellsAttribute,
1677 NSAccessibilityHeaderAttribute,
1678 NSAccessibilityColumnHeaderUIElementsAttribute,
1679 NSAccessibilityRowHeaderUIElementsAttribute,
1681 } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
1682 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1683 NSAccessibilityIndexAttribute,
1684 NSAccessibilityHeaderAttribute,
1685 NSAccessibilityRowsAttribute,
1686 NSAccessibilityVisibleRowsAttribute,
1688 } else if ([role isEqualToString:NSAccessibilityCellRole]) {
1689 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1690 NSAccessibilityColumnIndexRangeAttribute,
1691 NSAccessibilityRowIndexRangeAttribute,
1694 } else if ([role isEqualToString:@"AXWebArea"]) {
1695 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1697 @"AXLoadingProgress",
1699 } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
1700 [role isEqualToString:NSAccessibilityTextAreaRole]) {
1701 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1702 NSAccessibilityInsertionPointLineNumberAttribute,
1703 NSAccessibilityNumberOfCharactersAttribute,
1704 NSAccessibilitySelectedTextAttribute,
1705 NSAccessibilitySelectedTextRangeAttribute,
1706 NSAccessibilityVisibleCharacterRangeAttribute,
1708 } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
1709 [ret addObject:NSAccessibilityTabsAttribute];
1710 } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
1711 [role isEqualToString:NSAccessibilitySliderRole] ||
1712 [role isEqualToString:NSAccessibilityIncrementorRole] ||
1713 [role isEqualToString:NSAccessibilityScrollBarRole]) {
1714 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1715 NSAccessibilityMaxValueAttribute,
1716 NSAccessibilityMinValueAttribute,
1717 NSAccessibilityValueDescriptionAttribute,
1719 } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
1720 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1721 NSAccessibilityDisclosingAttribute,
1722 NSAccessibilityDisclosedByRowAttribute,
1723 NSAccessibilityDisclosureLevelAttribute,
1724 NSAccessibilityDisclosedRowsAttribute,
1726 } else if ([role isEqualToString:NSAccessibilityRowRole]) {
1727 if (browserAccessibility_->GetParent()) {
1728 base::string16 parentRole;
1729 browserAccessibility_->GetParent()->GetHtmlAttribute(
1730 "role", &parentRole);
1731 const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
1732 if (parentRole == treegridRole) {
1733 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1734 NSAccessibilityDisclosingAttribute,
1735 NSAccessibilityDisclosedByRowAttribute,
1736 NSAccessibilityDisclosureLevelAttribute,
1737 NSAccessibilityDisclosedRowsAttribute,
1740 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1741 NSAccessibilityIndexAttribute,
1745 } else if ([role isEqualToString:NSAccessibilityListRole]) {
1746 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1747 NSAccessibilitySelectedChildrenAttribute,
1748 NSAccessibilityVisibleChildrenAttribute,
1752 // Add the url attribute only if it has a valid url.
1753 if ([self url] != nil) {
1754 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1755 NSAccessibilityURLAttribute,
1759 // Position in set and Set size
1760 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_POS_IN_SET)) {
1761 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1765 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_SET_SIZE)) {
1766 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1772 if (browserAccessibility_->HasStringAttribute(
1773 ui::AX_ATTR_LIVE_STATUS)) {
1774 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1778 if (browserAccessibility_->HasStringAttribute(
1779 ui::AX_ATTR_LIVE_RELEVANT)) {
1780 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1784 if (browserAccessibility_->HasBoolAttribute(
1785 ui::AX_ATTR_LIVE_ATOMIC)) {
1786 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1790 if (browserAccessibility_->HasBoolAttribute(
1791 ui::AX_ATTR_LIVE_BUSY)) {
1792 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1797 std::string dropEffect;
1798 if (browserAccessibility_->GetHtmlAttribute("aria-dropeffect", &dropEffect)) {
1799 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1804 std::string grabbed;
1805 if (browserAccessibility_->GetHtmlAttribute("aria-grabbed", &grabbed)) {
1806 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1811 // Add expanded attribute only if it has expanded or collapsed state.
1812 if (GetState(browserAccessibility_, ui::AX_STATE_EXPANDED) ||
1813 GetState(browserAccessibility_, ui::AX_STATE_COLLAPSED)) {
1814 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1815 NSAccessibilityExpandedAttribute,
1819 if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL)
1820 || GetState(browserAccessibility_, ui::AX_STATE_HORIZONTAL)) {
1821 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1822 NSAccessibilityOrientationAttribute, nil]];
1825 if (browserAccessibility_->HasStringAttribute(ui::AX_ATTR_PLACEHOLDER)) {
1826 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1827 @"AXPlaceholder", nil]];
1830 if (GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)) {
1831 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1832 @"AXRequired", nil]];
1835 // Title UI Element.
1836 if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
1837 (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
1838 browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
1840 [ret addObjectsFromArray:[NSArray arrayWithObjects:
1841 NSAccessibilityTitleUIElementAttribute,
1844 // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
1845 // for elements which are referred to by labelledby or are labels
1850 // Returns the index of the child in this objects array of children.
1851 - (NSUInteger)accessibilityGetIndexOf:(id)child {
1852 if (!browserAccessibility_)
1855 NSUInteger index = 0;
1856 for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
1857 if ([child isEqual:childToCheck])
1864 // Returns whether or not the specified attribute can be set by the
1865 // accessibility API via |accessibilitySetValue:forAttribute:|.
1866 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
1867 if (!browserAccessibility_)
1870 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
1871 return GetState(browserAccessibility_,
1872 ui::AX_STATE_FOCUSABLE);
1873 if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1874 return browserAccessibility_->GetBoolAttribute(
1875 ui::AX_ATTR_CAN_SET_VALUE);
1877 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
1878 ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
1879 [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
1885 // Returns whether or not this object should be ignored in the accessibility
1887 - (BOOL)accessibilityIsIgnored {
1888 if (!browserAccessibility_)
1891 return [self isIgnored];
1894 // Performs the given accessibility action on the webkit accessibility object
1895 // that backs this object.
1896 - (void)accessibilityPerformAction:(NSString*)action {
1897 if (!browserAccessibility_)
1900 // TODO(dmazzoni): Support more actions.
1901 if ([action isEqualToString:NSAccessibilityPressAction]) {
1902 [self delegate]->AccessibilityDoDefaultAction(
1903 browserAccessibility_->GetId());
1904 } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
1905 [self delegate]->AccessibilityShowContextMenu(
1906 browserAccessibility_->GetId());
1910 // Returns the description of the given action.
1911 - (NSString*)accessibilityActionDescription:(NSString*)action {
1912 if (!browserAccessibility_)
1915 return NSAccessibilityActionDescription(action);
1918 // Sets an override value for a specific accessibility attribute.
1919 // This class does not support this.
1920 - (BOOL)accessibilitySetOverrideValue:(id)value
1921 forAttribute:(NSString*)attribute {
1925 // Sets the value for an accessibility attribute via the accessibility API.
1926 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
1927 if (!browserAccessibility_)
1930 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1931 BrowserAccessibilityManager* manager = browserAccessibility_->manager();
1932 NSNumber* focusedNumber = value;
1933 BOOL focused = [focusedNumber intValue];
1935 manager->SetFocus(browserAccessibility_, true);
1937 if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1938 NSRange range = [(NSValue*)value rangeValue];
1939 [self delegate]->AccessibilitySetTextSelection(
1940 browserAccessibility_->GetId(),
1941 range.location, range.location + range.length);
1945 // Returns the deepest accessibility child that should not be ignored.
1946 // It is assumed that the hit test has been narrowed down to this object
1947 // or one of its children, so this will never return nil unless this
1948 // object is invalid.
1949 - (id)accessibilityHitTest:(NSPoint)point {
1950 if (!browserAccessibility_)
1953 BrowserAccessibilityCocoa* hit = self;
1954 for (BrowserAccessibilityCocoa* child in [self children]) {
1955 if (!child->browserAccessibility_)
1957 NSPoint origin = [child origin];
1958 NSSize size = [[child size] sizeValue];
1960 rect.origin = origin;
1962 if (NSPointInRect(point, rect)) {
1964 id childResult = [child accessibilityHitTest:point];
1965 if (![childResult accessibilityIsIgnored]) {
1971 return NSAccessibilityUnignoredAncestor(hit);
1974 - (BOOL)isEqual:(id)object {
1975 if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
1977 return ([self hash] == [object hash]);
1980 - (NSUInteger)hash {
1981 // Potentially called during dealloc.
1982 if (!browserAccessibility_)
1983 return [super hash];
1984 return browserAccessibility_->GetId();
1987 - (BOOL)accessibilityShouldUseUniqueId {