Home | History | Annotate | Download | only in accessibility
      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.
      4 
      5 #include <execinfo.h>
      6 
      7 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
      8 
      9 #include <map>
     10 
     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/browser/accessibility/browser_accessibility_manager.h"
     16 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
     17 #include "content/public/common/content_client.h"
     18 #include "grit/webkit_strings.h"
     19 
     20 // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5,
     21 // 10.6, and 10.7. It allows accessibility clients to observe events posted on
     22 // this object.
     23 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
     24 
     25 using ui::AXNodeData;
     26 using content::BrowserAccessibility;
     27 using content::BrowserAccessibilityManager;
     28 using content::BrowserAccessibilityManagerMac;
     29 using content::ContentClient;
     30 typedef ui::AXStringAttribute StringAttribute;
     31 
     32 namespace {
     33 
     34 // Returns an autoreleased copy of the AXNodeData's attribute.
     35 NSString* NSStringForStringAttribute(
     36     BrowserAccessibility* browserAccessibility,
     37     StringAttribute attribute) {
     38   return base::SysUTF8ToNSString(
     39       browserAccessibility->GetStringAttribute(attribute));
     40 }
     41 
     42 struct MapEntry {
     43   ui::AXRole webKitValue;
     44   NSString* nativeValue;
     45 };
     46 
     47 typedef std::map<ui::AXRole, NSString*> RoleMap;
     48 
     49 // GetState checks the bitmask used in AXNodeData to check
     50 // if the given state was set on the accessibility object.
     51 bool GetState(BrowserAccessibility* accessibility, ui::AXState state) {
     52   return ((accessibility->GetState() >> state) & 1);
     53 }
     54 
     55 RoleMap BuildRoleMap() {
     56   const MapEntry roles[] = {
     57     { ui::AX_ROLE_ALERT, NSAccessibilityGroupRole },
     58     { ui::AX_ROLE_ALERT_DIALOG, NSAccessibilityGroupRole },
     59     { ui::AX_ROLE_ANNOTATION, NSAccessibilityUnknownRole },
     60     { ui::AX_ROLE_APPLICATION, NSAccessibilityGroupRole },
     61     { ui::AX_ROLE_ARTICLE, NSAccessibilityGroupRole },
     62     { ui::AX_ROLE_BANNER, NSAccessibilityGroupRole },
     63     { ui::AX_ROLE_BROWSER, NSAccessibilityBrowserRole },
     64     { ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole },
     65     { ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole },
     66     { ui::AX_ROLE_CANVAS, NSAccessibilityImageRole },
     67     { ui::AX_ROLE_CELL, @"AXCell" },
     68     { ui::AX_ROLE_CHECK_BOX, NSAccessibilityCheckBoxRole },
     69     { ui::AX_ROLE_COLOR_WELL, NSAccessibilityColorWellRole },
     70     { ui::AX_ROLE_COLUMN, NSAccessibilityColumnRole },
     71     { ui::AX_ROLE_COLUMN_HEADER, @"AXCell" },
     72     { ui::AX_ROLE_COMBO_BOX, NSAccessibilityComboBoxRole },
     73     { ui::AX_ROLE_COMPLEMENTARY, NSAccessibilityGroupRole },
     74     { ui::AX_ROLE_CONTENT_INFO, NSAccessibilityGroupRole },
     75     { ui::AX_ROLE_DEFINITION, NSAccessibilityGroupRole },
     76     { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, NSAccessibilityGroupRole },
     77     { ui::AX_ROLE_DESCRIPTION_LIST_TERM, NSAccessibilityGroupRole },
     78     { ui::AX_ROLE_DIALOG, NSAccessibilityGroupRole },
     79     { ui::AX_ROLE_DIRECTORY, NSAccessibilityListRole },
     80     { ui::AX_ROLE_DISCLOSURE_TRIANGLE, NSAccessibilityDisclosureTriangleRole },
     81     { ui::AX_ROLE_DIV, NSAccessibilityGroupRole },
     82     { ui::AX_ROLE_DOCUMENT, NSAccessibilityGroupRole },
     83     { ui::AX_ROLE_DRAWER, NSAccessibilityDrawerRole },
     84     { ui::AX_ROLE_EDITABLE_TEXT, NSAccessibilityTextFieldRole },
     85     { ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole },
     86     { ui::AX_ROLE_FORM, NSAccessibilityGroupRole },
     87     { ui::AX_ROLE_GRID, NSAccessibilityGridRole },
     88     { ui::AX_ROLE_GROUP, NSAccessibilityGroupRole },
     89     { ui::AX_ROLE_GROW_AREA, NSAccessibilityGrowAreaRole },
     90     { ui::AX_ROLE_HEADING, @"AXHeading" },
     91     { ui::AX_ROLE_HELP_TAG, NSAccessibilityHelpTagRole },
     92     { ui::AX_ROLE_HORIZONTAL_RULE, NSAccessibilityGroupRole },
     93     { ui::AX_ROLE_IFRAME, NSAccessibilityGroupRole },
     94     { ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole },
     95     { ui::AX_ROLE_IMAGE, NSAccessibilityImageRole },
     96     { ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole },
     97     { ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole },
     98     { ui::AX_ROLE_INCREMENTOR, NSAccessibilityIncrementorRole },
     99     { ui::AX_ROLE_LABEL_TEXT, NSAccessibilityGroupRole },
    100     { ui::AX_ROLE_LINK, NSAccessibilityLinkRole },
    101     { ui::AX_ROLE_LIST, NSAccessibilityListRole },
    102     { ui::AX_ROLE_LIST_BOX, NSAccessibilityListRole },
    103     { ui::AX_ROLE_LIST_BOX_OPTION, NSAccessibilityStaticTextRole },
    104     { ui::AX_ROLE_LIST_ITEM, NSAccessibilityGroupRole },
    105     { ui::AX_ROLE_LIST_MARKER, @"AXListMarker" },
    106     { ui::AX_ROLE_LOG, NSAccessibilityGroupRole },
    107     { ui::AX_ROLE_MAIN, NSAccessibilityGroupRole },
    108     { ui::AX_ROLE_MARQUEE, NSAccessibilityGroupRole },
    109     { ui::AX_ROLE_MATH, NSAccessibilityGroupRole },
    110     { ui::AX_ROLE_MATTE, NSAccessibilityMatteRole },
    111     { ui::AX_ROLE_MENU, NSAccessibilityMenuRole },
    112     { ui::AX_ROLE_MENU_BAR, NSAccessibilityMenuBarRole },
    113     { ui::AX_ROLE_MENU_BUTTON, NSAccessibilityButtonRole },
    114     { ui::AX_ROLE_MENU_ITEM, NSAccessibilityMenuItemRole },
    115     { ui::AX_ROLE_MENU_LIST_OPTION, NSAccessibilityMenuItemRole },
    116     { ui::AX_ROLE_MENU_LIST_POPUP, NSAccessibilityUnknownRole },
    117     { ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole },
    118     { ui::AX_ROLE_NOTE, NSAccessibilityGroupRole },
    119     { ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole },
    120     { ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole },
    121     { ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole },
    122     { ui::AX_ROLE_PRESENTATIONAL, NSAccessibilityGroupRole },
    123     { ui::AX_ROLE_PROGRESS_INDICATOR, NSAccessibilityProgressIndicatorRole },
    124     { ui::AX_ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
    125     { ui::AX_ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole },
    126     { ui::AX_ROLE_REGION, NSAccessibilityGroupRole },
    127     { ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea" },
    128     { ui::AX_ROLE_ROW, NSAccessibilityRowRole },
    129     { ui::AX_ROLE_ROW_HEADER, @"AXCell" },
    130     { ui::AX_ROLE_RULER, NSAccessibilityRulerRole },
    131     { ui::AX_ROLE_RULER_MARKER, NSAccessibilityRulerMarkerRole },
    132     { ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole },
    133     { ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole },
    134     { ui::AX_ROLE_SHEET, NSAccessibilitySheetRole },
    135     { ui::AX_ROLE_SLIDER, NSAccessibilitySliderRole },
    136     { ui::AX_ROLE_SLIDER_THUMB, NSAccessibilityValueIndicatorRole },
    137     { ui::AX_ROLE_SPIN_BUTTON, NSAccessibilitySliderRole },
    138     { ui::AX_ROLE_SPLITTER, NSAccessibilitySplitterRole },
    139     { ui::AX_ROLE_SPLIT_GROUP, NSAccessibilitySplitGroupRole },
    140     { ui::AX_ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole },
    141     { ui::AX_ROLE_STATUS, NSAccessibilityGroupRole },
    142     { ui::AX_ROLE_SVG_ROOT, NSAccessibilityGroupRole },
    143     { ui::AX_ROLE_SYSTEM_WIDE, NSAccessibilityUnknownRole },
    144     { ui::AX_ROLE_TAB, NSAccessibilityRadioButtonRole },
    145     { ui::AX_ROLE_TABLE, NSAccessibilityTableRole },
    146     { ui::AX_ROLE_TABLE_HEADER_CONTAINER, NSAccessibilityGroupRole },
    147     { ui::AX_ROLE_TAB_LIST, NSAccessibilityTabGroupRole },
    148     { ui::AX_ROLE_TAB_PANEL, NSAccessibilityGroupRole },
    149     { ui::AX_ROLE_TEXT_AREA, NSAccessibilityTextAreaRole },
    150     { ui::AX_ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole },
    151     { ui::AX_ROLE_TIMER, NSAccessibilityGroupRole },
    152     { ui::AX_ROLE_TOGGLE_BUTTON, NSAccessibilityCheckBoxRole },
    153     { ui::AX_ROLE_TOOLBAR, NSAccessibilityToolbarRole },
    154     { ui::AX_ROLE_TOOLTIP, NSAccessibilityGroupRole },
    155     { ui::AX_ROLE_TREE, NSAccessibilityOutlineRole },
    156     { ui::AX_ROLE_TREE_GRID, NSAccessibilityTableRole },
    157     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityRowRole },
    158     { ui::AX_ROLE_VALUE_INDICATOR, NSAccessibilityValueIndicatorRole },
    159     { ui::AX_ROLE_WEB_AREA, @"AXWebArea" },
    160     { ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole },
    161 
    162     // TODO(dtseng): we don't correctly support the attributes for these roles.
    163     // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole },
    164   };
    165 
    166   RoleMap role_map;
    167   for (size_t i = 0; i < arraysize(roles); ++i)
    168     role_map[roles[i].webKitValue] = roles[i].nativeValue;
    169   return role_map;
    170 }
    171 
    172 // A mapping of webkit roles to native roles.
    173 NSString* NativeRoleFromAXRole(
    174     const ui::AXRole& role) {
    175   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_role,
    176                          (BuildRoleMap()));
    177   RoleMap::iterator it = web_accessibility_to_native_role.find(role);
    178   if (it != web_accessibility_to_native_role.end())
    179     return it->second;
    180   else
    181     return NSAccessibilityUnknownRole;
    182 }
    183 
    184 RoleMap BuildSubroleMap() {
    185   const MapEntry subroles[] = {
    186     { ui::AX_ROLE_ALERT, @"AXApplicationAlert" },
    187     { ui::AX_ROLE_ALERT_DIALOG, @"AXApplicationAlertDialog" },
    188     { ui::AX_ROLE_ARTICLE, @"AXDocumentArticle" },
    189     { ui::AX_ROLE_DEFINITION, @"AXDefinition" },
    190     { ui::AX_ROLE_DESCRIPTION_LIST_DETAIL, @"AXDescription" },
    191     { ui::AX_ROLE_DESCRIPTION_LIST_TERM, @"AXTerm" },
    192     { ui::AX_ROLE_DIALOG, @"AXApplicationDialog" },
    193     { ui::AX_ROLE_DOCUMENT, @"AXDocument" },
    194     { ui::AX_ROLE_FOOTER, @"AXLandmarkContentInfo" },
    195     { ui::AX_ROLE_APPLICATION, @"AXLandmarkApplication" },
    196     { ui::AX_ROLE_BANNER, @"AXLandmarkBanner" },
    197     { ui::AX_ROLE_COMPLEMENTARY, @"AXLandmarkComplementary" },
    198     { ui::AX_ROLE_CONTENT_INFO, @"AXLandmarkContentInfo" },
    199     { ui::AX_ROLE_MAIN, @"AXLandmarkMain" },
    200     { ui::AX_ROLE_NAVIGATION, @"AXLandmarkNavigation" },
    201     { ui::AX_ROLE_SEARCH, @"AXLandmarkSearch" },
    202     { ui::AX_ROLE_LOG, @"AXApplicationLog" },
    203     { ui::AX_ROLE_MARQUEE, @"AXApplicationMarquee" },
    204     { ui::AX_ROLE_MATH, @"AXDocumentMath" },
    205     { ui::AX_ROLE_NOTE, @"AXDocumentNote" },
    206     { ui::AX_ROLE_REGION, @"AXDocumentRegion" },
    207     { ui::AX_ROLE_STATUS, @"AXApplicationStatus" },
    208     { ui::AX_ROLE_TAB_PANEL, @"AXTabPanel" },
    209     { ui::AX_ROLE_TIMER, @"AXApplicationTimer" },
    210     { ui::AX_ROLE_TOGGLE_BUTTON, @"AXToggleButton" },
    211     { ui::AX_ROLE_TOOLTIP, @"AXUserInterfaceTooltip" },
    212     { ui::AX_ROLE_TREE_ITEM, NSAccessibilityOutlineRowSubrole },
    213   };
    214 
    215   RoleMap subrole_map;
    216   for (size_t i = 0; i < arraysize(subroles); ++i)
    217     subrole_map[subroles[i].webKitValue] = subroles[i].nativeValue;
    218   return subrole_map;
    219 }
    220 
    221 // A mapping of webkit roles to native subroles.
    222 NSString* NativeSubroleFromAXRole(
    223     const ui::AXRole& role) {
    224   CR_DEFINE_STATIC_LOCAL(RoleMap, web_accessibility_to_native_subrole,
    225                          (BuildSubroleMap()));
    226   RoleMap::iterator it = web_accessibility_to_native_subrole.find(role);
    227   if (it != web_accessibility_to_native_subrole.end())
    228     return it->second;
    229   else
    230     return nil;
    231 }
    232 
    233 // A mapping from an accessibility attribute to its method name.
    234 NSDictionary* attributeToMethodNameMap = nil;
    235 
    236 } // namespace
    237 
    238 @implementation BrowserAccessibilityCocoa
    239 
    240 + (void)initialize {
    241   const struct {
    242     NSString* attribute;
    243     NSString* methodName;
    244   } attributeToMethodNameContainer[] = {
    245     { NSAccessibilityChildrenAttribute, @"children" },
    246     { NSAccessibilityColumnsAttribute, @"columns" },
    247     { NSAccessibilityColumnHeaderUIElementsAttribute, @"columnHeaders" },
    248     { NSAccessibilityColumnIndexRangeAttribute, @"columnIndexRange" },
    249     { NSAccessibilityContentsAttribute, @"contents" },
    250     { NSAccessibilityDescriptionAttribute, @"description" },
    251     { NSAccessibilityDisclosingAttribute, @"disclosing" },
    252     { NSAccessibilityDisclosedByRowAttribute, @"disclosedByRow" },
    253     { NSAccessibilityDisclosureLevelAttribute, @"disclosureLevel" },
    254     { NSAccessibilityDisclosedRowsAttribute, @"disclosedRows" },
    255     { NSAccessibilityEnabledAttribute, @"enabled" },
    256     { NSAccessibilityFocusedAttribute, @"focused" },
    257     { NSAccessibilityHeaderAttribute, @"header" },
    258     { NSAccessibilityHelpAttribute, @"help" },
    259     { NSAccessibilityIndexAttribute, @"index" },
    260     { NSAccessibilityLinkedUIElementsAttribute, @"linkedUIElements" },
    261     { NSAccessibilityMaxValueAttribute, @"maxValue" },
    262     { NSAccessibilityMinValueAttribute, @"minValue" },
    263     { NSAccessibilityNumberOfCharactersAttribute, @"numberOfCharacters" },
    264     { NSAccessibilityOrientationAttribute, @"orientation" },
    265     { NSAccessibilityParentAttribute, @"parent" },
    266     { NSAccessibilityPositionAttribute, @"position" },
    267     { NSAccessibilityRoleAttribute, @"role" },
    268     { NSAccessibilityRoleDescriptionAttribute, @"roleDescription" },
    269     { NSAccessibilityRowHeaderUIElementsAttribute, @"rowHeaders" },
    270     { NSAccessibilityRowIndexRangeAttribute, @"rowIndexRange" },
    271     { NSAccessibilityRowsAttribute, @"rows" },
    272     // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
    273     { NSAccessibilitySizeAttribute, @"size" },
    274     { NSAccessibilitySubroleAttribute, @"subrole" },
    275     { NSAccessibilityTabsAttribute, @"tabs" },
    276     { NSAccessibilityTitleAttribute, @"title" },
    277     { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" },
    278     { NSAccessibilityTopLevelUIElementAttribute, @"window" },
    279     { NSAccessibilityURLAttribute, @"url" },
    280     { NSAccessibilityValueAttribute, @"value" },
    281     { NSAccessibilityValueDescriptionAttribute, @"valueDescription" },
    282     { NSAccessibilityVisibleCharacterRangeAttribute, @"visibleCharacterRange" },
    283     { NSAccessibilityVisibleCellsAttribute, @"visibleCells" },
    284     { NSAccessibilityVisibleColumnsAttribute, @"visibleColumns" },
    285     { NSAccessibilityVisibleRowsAttribute, @"visibleRows" },
    286     { NSAccessibilityWindowAttribute, @"window" },
    287     { @"AXAccessKey", @"accessKey" },
    288     { @"AXARIAAtomic", @"ariaAtomic" },
    289     { @"AXARIABusy", @"ariaBusy" },
    290     { @"AXARIALive", @"ariaLive" },
    291     { @"AXARIARelevant", @"ariaRelevant" },
    292     { @"AXInvalid", @"invalid" },
    293     { @"AXLoaded", @"loaded" },
    294     { @"AXLoadingProgress", @"loadingProgress" },
    295     { @"AXRequired", @"required" },
    296     { @"AXVisited", @"visited" },
    297   };
    298 
    299   NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
    300   const size_t numAttributes = sizeof(attributeToMethodNameContainer) /
    301                                sizeof(attributeToMethodNameContainer[0]);
    302   for (size_t i = 0; i < numAttributes; ++i) {
    303     [dict setObject:attributeToMethodNameContainer[i].methodName
    304              forKey:attributeToMethodNameContainer[i].attribute];
    305   }
    306   attributeToMethodNameMap = dict;
    307   dict = nil;
    308 }
    309 
    310 - (id)initWithObject:(BrowserAccessibility*)accessibility {
    311   if ((self = [super init]))
    312     browserAccessibility_ = accessibility;
    313   return self;
    314 }
    315 
    316 - (void)detach {
    317   if (browserAccessibility_) {
    318     NSAccessibilityUnregisterUniqueIdForUIElement(self);
    319     browserAccessibility_ = NULL;
    320   }
    321 }
    322 
    323 - (NSString*)accessKey {
    324   return NSStringForStringAttribute(
    325       browserAccessibility_, ui::AX_ATTR_ACCESS_KEY);
    326 }
    327 
    328 - (NSNumber*)ariaAtomic {
    329   bool boolValue = browserAccessibility_->GetBoolAttribute(
    330       ui::AX_ATTR_LIVE_ATOMIC);
    331   return [NSNumber numberWithBool:boolValue];
    332 }
    333 
    334 - (NSNumber*)ariaBusy {
    335   bool boolValue = browserAccessibility_->GetBoolAttribute(
    336       ui::AX_ATTR_LIVE_BUSY);
    337   return [NSNumber numberWithBool:boolValue];
    338 }
    339 
    340 - (NSString*)ariaLive {
    341   return NSStringForStringAttribute(
    342       browserAccessibility_, ui::AX_ATTR_LIVE_STATUS);
    343 }
    344 
    345 - (NSString*)ariaRelevant {
    346   return NSStringForStringAttribute(
    347       browserAccessibility_, ui::AX_ATTR_LIVE_RELEVANT);
    348 }
    349 
    350 // Returns an array of BrowserAccessibilityCocoa objects, representing the
    351 // accessibility children of this object.
    352 - (NSArray*)children {
    353   if (!children_) {
    354     uint32 childCount = browserAccessibility_->PlatformChildCount();
    355     children_.reset([[NSMutableArray alloc] initWithCapacity:childCount]);
    356     for (uint32 index = 0; index < childCount; ++index) {
    357       BrowserAccessibilityCocoa* child =
    358           browserAccessibility_->PlatformGetChild(index)->
    359               ToBrowserAccessibilityCocoa();
    360       if ([child isIgnored])
    361         [children_ addObjectsFromArray:[child children]];
    362       else
    363         [children_ addObject:child];
    364     }
    365 
    366     // Also, add indirect children (if any).
    367     const std::vector<int32>& indirectChildIds =
    368         browserAccessibility_->GetIntListAttribute(
    369             ui::AX_ATTR_INDIRECT_CHILD_IDS);
    370     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
    371       int32 child_id = indirectChildIds[i];
    372       BrowserAccessibility* child =
    373           browserAccessibility_->manager()->GetFromID(child_id);
    374 
    375       // This only became necessary as a result of crbug.com/93095. It should be
    376       // a DCHECK in the future.
    377       if (child) {
    378         BrowserAccessibilityCocoa* child_cocoa =
    379             child->ToBrowserAccessibilityCocoa();
    380         [children_ addObject:child_cocoa];
    381       }
    382     }
    383   }
    384   return children_;
    385 }
    386 
    387 - (void)childrenChanged {
    388   if (![self isIgnored]) {
    389     children_.reset();
    390   } else {
    391     [browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa()
    392        childrenChanged];
    393   }
    394 }
    395 
    396 - (NSArray*)columnHeaders {
    397   if ([self internalRole] != ui::AX_ROLE_TABLE &&
    398       [self internalRole] != ui::AX_ROLE_GRID) {
    399     return nil;
    400   }
    401 
    402   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    403   const std::vector<int32>& uniqueCellIds =
    404       browserAccessibility_->GetIntListAttribute(
    405           ui::AX_ATTR_UNIQUE_CELL_IDS);
    406   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
    407     int id = uniqueCellIds[i];
    408     BrowserAccessibility* cell =
    409         browserAccessibility_->manager()->GetFromID(id);
    410     if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
    411       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
    412   }
    413   return ret;
    414 }
    415 
    416 - (NSValue*)columnIndexRange {
    417   if ([self internalRole] != ui::AX_ROLE_CELL)
    418     return nil;
    419 
    420   int column = -1;
    421   int colspan = -1;
    422   browserAccessibility_->GetIntAttribute(
    423       ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
    424   browserAccessibility_->GetIntAttribute(
    425       ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan);
    426   if (column >= 0 && colspan >= 1)
    427     return [NSValue valueWithRange:NSMakeRange(column, colspan)];
    428   return nil;
    429 }
    430 
    431 - (NSArray*)columns {
    432   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    433   for (BrowserAccessibilityCocoa* child in [self children]) {
    434     if ([[child role] isEqualToString:NSAccessibilityColumnRole])
    435       [ret addObject:child];
    436   }
    437   return ret;
    438 }
    439 
    440 - (NSString*)description {
    441   std::string description;
    442   if (browserAccessibility_->GetStringAttribute(
    443           ui::AX_ATTR_DESCRIPTION, &description)) {
    444     return base::SysUTF8ToNSString(description);
    445   }
    446 
    447   // If the role is anything other than an image, or if there's
    448   // a title or title UI element, just return an empty string.
    449   if (![[self role] isEqualToString:NSAccessibilityImageRole])
    450     return @"";
    451   if (browserAccessibility_->HasStringAttribute(
    452           ui::AX_ATTR_NAME)) {
    453     return @"";
    454   }
    455   if ([self titleUIElement])
    456     return @"";
    457 
    458   // The remaining case is an image where there's no other title.
    459   // Return the base part of the filename as the description.
    460   std::string url;
    461   if (browserAccessibility_->GetStringAttribute(
    462           ui::AX_ATTR_URL, &url)) {
    463     // Given a url like http://foo.com/bar/baz.png, just return the
    464     // base name, e.g., "baz.png".
    465     size_t leftIndex = url.rfind('/');
    466     std::string basename =
    467         leftIndex != std::string::npos ? url.substr(leftIndex) : url;
    468     return base::SysUTF8ToNSString(basename);
    469   }
    470 
    471   return @"";
    472 }
    473 
    474 - (NSNumber*)disclosing {
    475   if ([self internalRole] == ui::AX_ROLE_TREE_ITEM) {
    476     return [NSNumber numberWithBool:
    477         GetState(browserAccessibility_, ui::AX_STATE_EXPANDED)];
    478   } else {
    479     return nil;
    480   }
    481 }
    482 
    483 - (id)disclosedByRow {
    484   // The row that contains this row.
    485   // It should be the same as the first parent that is a treeitem.
    486   return nil;
    487 }
    488 
    489 - (NSNumber*)disclosureLevel {
    490   ui::AXRole role = [self internalRole];
    491   if (role == ui::AX_ROLE_ROW ||
    492       role == ui::AX_ROLE_TREE_ITEM) {
    493     int level = browserAccessibility_->GetIntAttribute(
    494         ui::AX_ATTR_HIERARCHICAL_LEVEL);
    495     // Mac disclosureLevel is 0-based, but web levels are 1-based.
    496     if (level > 0)
    497       level--;
    498     return [NSNumber numberWithInt:level];
    499   } else {
    500     return nil;
    501   }
    502 }
    503 
    504 - (id)disclosedRows {
    505   // The rows that are considered inside this row.
    506   return nil;
    507 }
    508 
    509 - (NSNumber*)enabled {
    510   return [NSNumber numberWithBool:
    511       GetState(browserAccessibility_, ui::AX_STATE_ENABLED)];
    512 }
    513 
    514 - (NSNumber*)focused {
    515   BrowserAccessibilityManager* manager = browserAccessibility_->manager();
    516   NSNumber* ret = [NSNumber numberWithBool:
    517       manager->GetFocus(NULL) == browserAccessibility_];
    518   return ret;
    519 }
    520 
    521 - (id)header {
    522   int headerElementId = -1;
    523   if ([self internalRole] == ui::AX_ROLE_TABLE ||
    524       [self internalRole] == ui::AX_ROLE_GRID) {
    525     browserAccessibility_->GetIntAttribute(
    526         ui::AX_ATTR_TABLE_HEADER_ID, &headerElementId);
    527   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
    528     browserAccessibility_->GetIntAttribute(
    529         ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, &headerElementId);
    530   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
    531     browserAccessibility_->GetIntAttribute(
    532         ui::AX_ATTR_TABLE_ROW_HEADER_ID, &headerElementId);
    533   }
    534 
    535   if (headerElementId > 0) {
    536     BrowserAccessibility* headerObject =
    537         browserAccessibility_->manager()->GetFromID(headerElementId);
    538     if (headerObject)
    539       return headerObject->ToBrowserAccessibilityCocoa();
    540   }
    541   return nil;
    542 }
    543 
    544 - (NSString*)help {
    545   return NSStringForStringAttribute(
    546       browserAccessibility_, ui::AX_ATTR_HELP);
    547 }
    548 
    549 - (NSNumber*)index {
    550   if ([self internalRole] == ui::AX_ROLE_COLUMN) {
    551     int columnIndex = browserAccessibility_->GetIntAttribute(
    552           ui::AX_ATTR_TABLE_COLUMN_INDEX);
    553     return [NSNumber numberWithInt:columnIndex];
    554   } else if ([self internalRole] == ui::AX_ROLE_ROW) {
    555     int rowIndex = browserAccessibility_->GetIntAttribute(
    556         ui::AX_ATTR_TABLE_ROW_INDEX);
    557     return [NSNumber numberWithInt:rowIndex];
    558   }
    559 
    560   return nil;
    561 }
    562 
    563 // Returns whether or not this node should be ignored in the
    564 // accessibility tree.
    565 - (BOOL)isIgnored {
    566   return [[self role] isEqualToString:NSAccessibilityUnknownRole];
    567 }
    568 
    569 - (NSString*)invalid {
    570   base::string16 invalidUTF;
    571   if (!browserAccessibility_->GetHtmlAttribute("aria-invalid", &invalidUTF))
    572     return NULL;
    573   NSString* invalid = base::SysUTF16ToNSString(invalidUTF);
    574   if ([invalid isEqualToString:@"false"] ||
    575       [invalid isEqualToString:@""]) {
    576     return @"false";
    577   }
    578   return invalid;
    579 }
    580 
    581 - (void)addLinkedUIElementsFromAttribute:(ui::AXIntListAttribute)attribute
    582                                    addTo:(NSMutableArray*)outArray {
    583   const std::vector<int32>& attributeValues =
    584       browserAccessibility_->GetIntListAttribute(attribute);
    585   for (size_t i = 0; i < attributeValues.size(); ++i) {
    586     BrowserAccessibility* element =
    587         browserAccessibility_->manager()->GetFromID(attributeValues[i]);
    588     if (element)
    589       [outArray addObject:element->ToBrowserAccessibilityCocoa()];
    590   }
    591 }
    592 
    593 - (NSArray*)linkedUIElements {
    594   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    595   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_OWNS_IDS addTo:ret];
    596   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_CONTROLS_IDS addTo:ret];
    597   [self addLinkedUIElementsFromAttribute:ui::AX_ATTR_FLOWTO_IDS addTo:ret];
    598   if ([ret count] == 0)
    599     return nil;
    600   return ret;
    601 }
    602 
    603 - (NSNumber*)loaded {
    604   return [NSNumber numberWithBool:YES];
    605 }
    606 
    607 - (NSNumber*)loadingProgress {
    608   float floatValue = browserAccessibility_->GetFloatAttribute(
    609       ui::AX_ATTR_DOC_LOADING_PROGRESS);
    610   return [NSNumber numberWithFloat:floatValue];
    611 }
    612 
    613 - (NSNumber*)maxValue {
    614   float floatValue = browserAccessibility_->GetFloatAttribute(
    615       ui::AX_ATTR_MAX_VALUE_FOR_RANGE);
    616   return [NSNumber numberWithFloat:floatValue];
    617 }
    618 
    619 - (NSNumber*)minValue {
    620   float floatValue = browserAccessibility_->GetFloatAttribute(
    621       ui::AX_ATTR_MIN_VALUE_FOR_RANGE);
    622   return [NSNumber numberWithFloat:floatValue];
    623 }
    624 
    625 - (NSString*)orientation {
    626   // We present a spin button as a vertical slider, with a role description
    627   // of "spin button".
    628   if ([self internalRole] == ui::AX_ROLE_SPIN_BUTTON)
    629     return NSAccessibilityVerticalOrientationValue;
    630 
    631   if (GetState(browserAccessibility_, ui::AX_STATE_VERTICAL))
    632     return NSAccessibilityVerticalOrientationValue;
    633   else
    634     return NSAccessibilityHorizontalOrientationValue;
    635 }
    636 
    637 - (NSNumber*)numberOfCharacters {
    638   return [NSNumber numberWithInt:browserAccessibility_->value().length()];
    639 }
    640 
    641 // The origin of this accessibility object in the page's document.
    642 // This is relative to webkit's top-left origin, not Cocoa's
    643 // bottom-left origin.
    644 - (NSPoint)origin {
    645   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
    646   return NSMakePoint(bounds.x(), bounds.y());
    647 }
    648 
    649 - (id)parent {
    650   // A nil parent means we're the root.
    651   if (browserAccessibility_->GetParent()) {
    652     return NSAccessibilityUnignoredAncestor(
    653         browserAccessibility_->GetParent()->ToBrowserAccessibilityCocoa());
    654   } else {
    655     // Hook back up to RenderWidgetHostViewCocoa.
    656     BrowserAccessibilityManagerMac* manager =
    657         static_cast<BrowserAccessibilityManagerMac*>(
    658             browserAccessibility_->manager());
    659     return manager->parent_view();
    660   }
    661 }
    662 
    663 - (NSValue*)position {
    664   NSPoint origin = [self origin];
    665   NSSize size = [[self size] sizeValue];
    666   NSPoint pointInScreen = [self pointInScreen:origin size:size];
    667   return [NSValue valueWithPoint:pointInScreen];
    668 }
    669 
    670 - (NSNumber*)required {
    671   return [NSNumber numberWithBool:
    672       GetState(browserAccessibility_, ui::AX_STATE_REQUIRED)];
    673 }
    674 
    675 // Returns an enum indicating the role from browserAccessibility_.
    676 - (ui::AXRole)internalRole {
    677   return static_cast<ui::AXRole>(browserAccessibility_->GetRole());
    678 }
    679 
    680 - (content::BrowserAccessibilityDelegate*)delegate {
    681   return browserAccessibility_->manager() ?
    682       browserAccessibility_->manager()->delegate() :
    683       nil;
    684 }
    685 
    686 - (NSPoint)pointInScreen:(NSPoint)origin
    687                     size:(NSSize)size {
    688   if (!browserAccessibility_)
    689     return NSZeroPoint;
    690 
    691   gfx::Rect bounds(origin.x, origin.y, size.width, size.height);
    692   gfx::Point point = [self delegate]->AccessibilityOriginInScreen(bounds);
    693   return NSMakePoint(point.x(), point.y());
    694 }
    695 
    696 // Returns a string indicating the NSAccessibility role of this object.
    697 - (NSString*)role {
    698   ui::AXRole role = [self internalRole];
    699   if (role == ui::AX_ROLE_CANVAS &&
    700       browserAccessibility_->GetBoolAttribute(
    701           ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
    702     return NSAccessibilityGroupRole;
    703   }
    704   if (role == ui::AX_ROLE_BUTTON || role == ui::AX_ROLE_TOGGLE_BUTTON) {
    705     bool isAriaPressedDefined;
    706     bool isMixed;
    707     browserAccessibility_->GetAriaTristate("aria-pressed",
    708                                            &isAriaPressedDefined,
    709                                            &isMixed);
    710     if (isAriaPressedDefined)
    711       return NSAccessibilityCheckBoxRole;
    712     else
    713       return NSAccessibilityButtonRole;
    714   }
    715   return NativeRoleFromAXRole(role);
    716 }
    717 
    718 // Returns a string indicating the role description of this object.
    719 - (NSString*)roleDescription {
    720   NSString* role = [self role];
    721 
    722   ContentClient* content_client = content::GetContentClient();
    723 
    724   // The following descriptions are specific to webkit.
    725   if ([role isEqualToString:@"AXWebArea"]) {
    726     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    727         IDS_AX_ROLE_WEB_AREA));
    728   }
    729 
    730   if ([role isEqualToString:@"NSAccessibilityLinkRole"]) {
    731     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    732         IDS_AX_ROLE_LINK));
    733   }
    734 
    735   if ([role isEqualToString:@"AXHeading"]) {
    736     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    737         IDS_AX_ROLE_HEADING));
    738   }
    739 
    740   if ([role isEqualToString:NSAccessibilityGroupRole] ||
    741       [role isEqualToString:NSAccessibilityRadioButtonRole]) {
    742     std::string role;
    743     if (browserAccessibility_->GetHtmlAttribute("role", &role)) {
    744       ui::AXRole internalRole = [self internalRole];
    745       if ((internalRole != ui::AX_ROLE_GROUP &&
    746            internalRole != ui::AX_ROLE_LIST_ITEM) ||
    747           internalRole == ui::AX_ROLE_TAB) {
    748         // TODO(dtseng): This is not localized; see crbug/84814.
    749         return base::SysUTF8ToNSString(role);
    750       }
    751     }
    752   }
    753 
    754   switch([self internalRole]) {
    755   case ui::AX_ROLE_FOOTER:
    756     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    757         IDS_AX_ROLE_FOOTER));
    758   case ui::AX_ROLE_SPIN_BUTTON:
    759     // This control is similar to what VoiceOver calls a "stepper".
    760     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    761         IDS_AX_ROLE_STEPPER));
    762   case ui::AX_ROLE_TOGGLE_BUTTON:
    763     return base::SysUTF16ToNSString(content_client->GetLocalizedString(
    764         IDS_AX_ROLE_TOGGLE_BUTTON));
    765   default:
    766     break;
    767   }
    768 
    769   return NSAccessibilityRoleDescription(role, nil);
    770 }
    771 
    772 - (NSArray*)rowHeaders {
    773   if ([self internalRole] != ui::AX_ROLE_TABLE &&
    774       [self internalRole] != ui::AX_ROLE_GRID) {
    775     return nil;
    776   }
    777 
    778   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    779   const std::vector<int32>& uniqueCellIds =
    780       browserAccessibility_->GetIntListAttribute(
    781           ui::AX_ATTR_UNIQUE_CELL_IDS);
    782   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
    783     int id = uniqueCellIds[i];
    784     BrowserAccessibility* cell =
    785         browserAccessibility_->manager()->GetFromID(id);
    786     if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
    787       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
    788   }
    789   return ret;
    790 }
    791 
    792 - (NSValue*)rowIndexRange {
    793   if ([self internalRole] != ui::AX_ROLE_CELL)
    794     return nil;
    795 
    796   int row = -1;
    797   int rowspan = -1;
    798   browserAccessibility_->GetIntAttribute(
    799       ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
    800   browserAccessibility_->GetIntAttribute(
    801       ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan);
    802   if (row >= 0 && rowspan >= 1)
    803     return [NSValue valueWithRange:NSMakeRange(row, rowspan)];
    804   return nil;
    805 }
    806 
    807 - (NSArray*)rows {
    808   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    809 
    810   if ([self internalRole] == ui::AX_ROLE_TABLE||
    811       [self internalRole] == ui::AX_ROLE_GRID) {
    812     for (BrowserAccessibilityCocoa* child in [self children]) {
    813       if ([[child role] isEqualToString:NSAccessibilityRowRole])
    814         [ret addObject:child];
    815     }
    816   } else if ([self internalRole] == ui::AX_ROLE_COLUMN) {
    817     const std::vector<int32>& indirectChildIds =
    818         browserAccessibility_->GetIntListAttribute(
    819             ui::AX_ATTR_INDIRECT_CHILD_IDS);
    820     for (uint32 i = 0; i < indirectChildIds.size(); ++i) {
    821       int id = indirectChildIds[i];
    822       BrowserAccessibility* rowElement =
    823           browserAccessibility_->manager()->GetFromID(id);
    824       if (rowElement)
    825         [ret addObject:rowElement->ToBrowserAccessibilityCocoa()];
    826     }
    827   }
    828 
    829   return ret;
    830 }
    831 
    832 // Returns the size of this object.
    833 - (NSValue*)size {
    834   gfx::Rect bounds = browserAccessibility_->GetLocalBoundsRect();
    835   return  [NSValue valueWithSize:NSMakeSize(bounds.width(), bounds.height())];
    836 }
    837 
    838 // Returns a subrole based upon the role.
    839 - (NSString*) subrole {
    840   ui::AXRole browserAccessibilityRole = [self internalRole];
    841   if (browserAccessibilityRole == ui::AX_ROLE_TEXT_FIELD &&
    842       GetState(browserAccessibility_, ui::AX_STATE_PROTECTED)) {
    843     return @"AXSecureTextField";
    844   }
    845 
    846   NSString* htmlTag = NSStringForStringAttribute(
    847       browserAccessibility_, ui::AX_ATTR_HTML_TAG);
    848 
    849   if (browserAccessibilityRole == ui::AX_ROLE_LIST) {
    850     if ([htmlTag isEqualToString:@"dl"]) {
    851       return @"AXDescriptionList";
    852     } else {
    853       return @"AXContentList";
    854     }
    855   }
    856 
    857   return NativeSubroleFromAXRole(browserAccessibilityRole);
    858 }
    859 
    860 // Returns all tabs in this subtree.
    861 - (NSArray*)tabs {
    862   NSMutableArray* tabSubtree = [[[NSMutableArray alloc] init] autorelease];
    863 
    864   if ([self internalRole] == ui::AX_ROLE_TAB)
    865     [tabSubtree addObject:self];
    866 
    867   for (uint i=0; i < [[self children] count]; ++i) {
    868     NSArray* tabChildren = [[[self children] objectAtIndex:i] tabs];
    869     if ([tabChildren count] > 0)
    870       [tabSubtree addObjectsFromArray:tabChildren];
    871   }
    872 
    873   return tabSubtree;
    874 }
    875 
    876 - (NSString*)title {
    877   return NSStringForStringAttribute(
    878       browserAccessibility_, ui::AX_ATTR_NAME);
    879 }
    880 
    881 - (id)titleUIElement {
    882   int titleElementId;
    883   if (browserAccessibility_->GetIntAttribute(
    884           ui::AX_ATTR_TITLE_UI_ELEMENT, &titleElementId)) {
    885     BrowserAccessibility* titleElement =
    886         browserAccessibility_->manager()->GetFromID(titleElementId);
    887     if (titleElement)
    888       return titleElement->ToBrowserAccessibilityCocoa();
    889   }
    890   std::vector<int32> labelledby_ids =
    891       browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
    892   if (labelledby_ids.size() == 1) {
    893     BrowserAccessibility* titleElement =
    894         browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
    895     if (titleElement)
    896       return titleElement->ToBrowserAccessibilityCocoa();
    897   }
    898 
    899   return nil;
    900 }
    901 
    902 - (NSURL*)url {
    903   StringAttribute urlAttribute =
    904       [[self role] isEqualToString:@"AXWebArea"] ?
    905           ui::AX_ATTR_DOC_URL :
    906           ui::AX_ATTR_URL;
    907 
    908   std::string urlStr = browserAccessibility_->GetStringAttribute(urlAttribute);
    909   if (urlStr.empty())
    910     return nil;
    911 
    912   return [NSURL URLWithString:(base::SysUTF8ToNSString(urlStr))];
    913 }
    914 
    915 - (id)value {
    916   // WebCore uses an attachmentView to get the below behavior.
    917   // We do not have any native views backing this object, so need
    918   // to approximate Cocoa ax behavior best as we can.
    919   NSString* role = [self role];
    920   if ([role isEqualToString:@"AXHeading"]) {
    921     int level = 0;
    922     if (browserAccessibility_->GetIntAttribute(
    923             ui::AX_ATTR_HIERARCHICAL_LEVEL, &level)) {
    924       return [NSNumber numberWithInt:level];
    925     }
    926   } else if ([role isEqualToString:NSAccessibilityButtonRole]) {
    927     // AXValue does not make sense for pure buttons.
    928     return @"";
    929   } else if ([self internalRole] == ui::AX_ROLE_TOGGLE_BUTTON) {
    930     int value = 0;
    931     bool isAriaPressedDefined;
    932     bool isMixed;
    933     value = browserAccessibility_->GetAriaTristate(
    934         "aria-pressed", &isAriaPressedDefined, &isMixed) ? 1 : 0;
    935 
    936     if (isMixed)
    937       value = 2;
    938 
    939     return [NSNumber numberWithInt:value];
    940 
    941   } else if ([role isEqualToString:NSAccessibilityCheckBoxRole] ||
    942              [role isEqualToString:NSAccessibilityRadioButtonRole]) {
    943     int value = 0;
    944     value = GetState(
    945         browserAccessibility_, ui::AX_STATE_CHECKED) ? 1 : 0;
    946     value = GetState(
    947         browserAccessibility_, ui::AX_STATE_SELECTED) ?
    948             1 :
    949             value;
    950 
    951     if (browserAccessibility_->GetBoolAttribute(
    952         ui::AX_ATTR_BUTTON_MIXED)) {
    953       value = 2;
    954     }
    955     return [NSNumber numberWithInt:value];
    956   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
    957              [role isEqualToString:NSAccessibilitySliderRole] ||
    958              [role isEqualToString:NSAccessibilityScrollBarRole]) {
    959     float floatValue;
    960     if (browserAccessibility_->GetFloatAttribute(
    961             ui::AX_ATTR_VALUE_FOR_RANGE, &floatValue)) {
    962       return [NSNumber numberWithFloat:floatValue];
    963     }
    964   } else if ([role isEqualToString:NSAccessibilityColorWellRole]) {
    965     int r = browserAccessibility_->GetIntAttribute(
    966         ui::AX_ATTR_COLOR_VALUE_RED);
    967     int g = browserAccessibility_->GetIntAttribute(
    968         ui::AX_ATTR_COLOR_VALUE_GREEN);
    969     int b = browserAccessibility_->GetIntAttribute(
    970         ui::AX_ATTR_COLOR_VALUE_BLUE);
    971     // This string matches the one returned by a native Mac color well.
    972     return [NSString stringWithFormat:@"rgb %7.5f %7.5f %7.5f 1",
    973                 r / 255., g / 255., b / 255.];
    974   }
    975 
    976   return NSStringForStringAttribute(
    977       browserAccessibility_, ui::AX_ATTR_VALUE);
    978 }
    979 
    980 - (NSString*)valueDescription {
    981   return NSStringForStringAttribute(
    982       browserAccessibility_, ui::AX_ATTR_VALUE);
    983 }
    984 
    985 - (NSValue*)visibleCharacterRange {
    986   return [NSValue valueWithRange:
    987       NSMakeRange(0, browserAccessibility_->value().length())];
    988 }
    989 
    990 - (NSArray*)visibleCells {
    991   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
    992   const std::vector<int32>& uniqueCellIds =
    993       browserAccessibility_->GetIntListAttribute(
    994           ui::AX_ATTR_UNIQUE_CELL_IDS);
    995   for (size_t i = 0; i < uniqueCellIds.size(); ++i) {
    996     int id = uniqueCellIds[i];
    997     BrowserAccessibility* cell =
    998         browserAccessibility_->manager()->GetFromID(id);
    999     if (cell)
   1000       [ret addObject:cell->ToBrowserAccessibilityCocoa()];
   1001   }
   1002   return ret;
   1003 }
   1004 
   1005 - (NSArray*)visibleColumns {
   1006   return [self columns];
   1007 }
   1008 
   1009 - (NSArray*)visibleRows {
   1010   return [self rows];
   1011 }
   1012 
   1013 - (NSNumber*)visited {
   1014   return [NSNumber numberWithBool:
   1015       GetState(browserAccessibility_, ui::AX_STATE_VISITED)];
   1016 }
   1017 
   1018 - (id)window {
   1019   if (!browserAccessibility_)
   1020     return nil;
   1021 
   1022   BrowserAccessibilityManagerMac* manager =
   1023       static_cast<BrowserAccessibilityManagerMac*>(
   1024           browserAccessibility_->manager());
   1025   return [manager->parent_view() window];
   1026 }
   1027 
   1028 - (NSString*)methodNameForAttribute:(NSString*)attribute {
   1029   return [attributeToMethodNameMap objectForKey:attribute];
   1030 }
   1031 
   1032 - (void)swapChildren:(base::scoped_nsobject<NSMutableArray>*)other {
   1033   children_.swap(*other);
   1034 }
   1035 
   1036 // Returns the accessibility value for the given attribute.  If the value isn't
   1037 // supported this will return nil.
   1038 - (id)accessibilityAttributeValue:(NSString*)attribute {
   1039   if (!browserAccessibility_)
   1040     return nil;
   1041 
   1042   SEL selector =
   1043       NSSelectorFromString([self methodNameForAttribute:attribute]);
   1044   if (selector)
   1045     return [self performSelector:selector];
   1046 
   1047   // TODO(dtseng): refactor remaining attributes.
   1048   int selStart, selEnd;
   1049   if (browserAccessibility_->GetIntAttribute(
   1050           ui::AX_ATTR_TEXT_SEL_START, &selStart) &&
   1051       browserAccessibility_->
   1052           GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &selEnd)) {
   1053     if (selStart > selEnd)
   1054       std::swap(selStart, selEnd);
   1055     int selLength = selEnd - selStart;
   1056     if ([attribute isEqualToString:
   1057         NSAccessibilityInsertionPointLineNumberAttribute]) {
   1058       const std::vector<int32>& line_breaks =
   1059           browserAccessibility_->GetIntListAttribute(
   1060               ui::AX_ATTR_LINE_BREAKS);
   1061       for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
   1062         if (line_breaks[i] > selStart)
   1063           return [NSNumber numberWithInt:i];
   1064       }
   1065       return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
   1066     }
   1067     if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
   1068       std::string value = browserAccessibility_->GetStringAttribute(
   1069           ui::AX_ATTR_VALUE);
   1070       return base::SysUTF8ToNSString(value.substr(selStart, selLength));
   1071     }
   1072     if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
   1073       return [NSValue valueWithRange:NSMakeRange(selStart, selLength)];
   1074     }
   1075   }
   1076   return nil;
   1077 }
   1078 
   1079 // Returns the accessibility value for the given attribute and parameter. If the
   1080 // value isn't supported this will return nil.
   1081 - (id)accessibilityAttributeValue:(NSString*)attribute
   1082                      forParameter:(id)parameter {
   1083   if (!browserAccessibility_)
   1084     return nil;
   1085 
   1086   const std::vector<int32>& line_breaks =
   1087       browserAccessibility_->GetIntListAttribute(
   1088           ui::AX_ATTR_LINE_BREAKS);
   1089   int len = static_cast<int>(browserAccessibility_->value().size());
   1090 
   1091   if ([attribute isEqualToString:
   1092       NSAccessibilityStringForRangeParameterizedAttribute]) {
   1093     NSRange range = [(NSValue*)parameter rangeValue];
   1094     std::string value = browserAccessibility_->GetStringAttribute(
   1095         ui::AX_ATTR_VALUE);
   1096     return base::SysUTF8ToNSString(value.substr(range.location, range.length));
   1097   }
   1098 
   1099   if ([attribute isEqualToString:
   1100       NSAccessibilityLineForIndexParameterizedAttribute]) {
   1101     int index = [(NSNumber*)parameter intValue];
   1102     for (int i = 0; i < static_cast<int>(line_breaks.size()); ++i) {
   1103       if (line_breaks[i] > index)
   1104         return [NSNumber numberWithInt:i];
   1105     }
   1106     return [NSNumber numberWithInt:static_cast<int>(line_breaks.size())];
   1107   }
   1108 
   1109   if ([attribute isEqualToString:
   1110       NSAccessibilityRangeForLineParameterizedAttribute]) {
   1111     int line_index = [(NSNumber*)parameter intValue];
   1112     int line_count = static_cast<int>(line_breaks.size()) + 1;
   1113     if (line_index < 0 || line_index >= line_count)
   1114       return nil;
   1115     int start = line_index > 0 ? line_breaks[line_index - 1] : 0;
   1116     int end = line_index < line_count - 1 ? line_breaks[line_index] : len;
   1117     return [NSValue valueWithRange:
   1118         NSMakeRange(start, end - start)];
   1119   }
   1120 
   1121   if ([attribute isEqualToString:
   1122       NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
   1123     if ([self internalRole] != ui::AX_ROLE_TABLE &&
   1124         [self internalRole] != ui::AX_ROLE_GRID) {
   1125       return nil;
   1126     }
   1127     if (![parameter isKindOfClass:[NSArray self]])
   1128       return nil;
   1129     NSArray* array = parameter;
   1130     int column = [[array objectAtIndex:0] intValue];
   1131     int row = [[array objectAtIndex:1] intValue];
   1132     int num_columns = browserAccessibility_->GetIntAttribute(
   1133         ui::AX_ATTR_TABLE_COLUMN_COUNT);
   1134     int num_rows = browserAccessibility_->GetIntAttribute(
   1135         ui::AX_ATTR_TABLE_ROW_COUNT);
   1136     if (column < 0 || column >= num_columns ||
   1137         row < 0 || row >= num_rows) {
   1138       return nil;
   1139     }
   1140     for (size_t i = 0;
   1141          i < browserAccessibility_->PlatformChildCount();
   1142          ++i) {
   1143       BrowserAccessibility* child = browserAccessibility_->PlatformGetChild(i);
   1144       if (child->GetRole() != ui::AX_ROLE_ROW)
   1145         continue;
   1146       int rowIndex;
   1147       if (!child->GetIntAttribute(
   1148               ui::AX_ATTR_TABLE_ROW_INDEX, &rowIndex)) {
   1149         continue;
   1150       }
   1151       if (rowIndex < row)
   1152         continue;
   1153       if (rowIndex > row)
   1154         break;
   1155       for (size_t j = 0;
   1156            j < child->PlatformChildCount();
   1157            ++j) {
   1158         BrowserAccessibility* cell = child->PlatformGetChild(j);
   1159         if (cell->GetRole() != ui::AX_ROLE_CELL)
   1160           continue;
   1161         int colIndex;
   1162         if (!cell->GetIntAttribute(
   1163                 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
   1164                 &colIndex)) {
   1165           continue;
   1166         }
   1167         if (colIndex == column)
   1168           return cell->ToBrowserAccessibilityCocoa();
   1169         if (colIndex > column)
   1170           break;
   1171       }
   1172     }
   1173     return nil;
   1174   }
   1175 
   1176   if ([attribute isEqualToString:
   1177       NSAccessibilityBoundsForRangeParameterizedAttribute]) {
   1178     if ([self internalRole] != ui::AX_ROLE_STATIC_TEXT)
   1179       return nil;
   1180     NSRange range = [(NSValue*)parameter rangeValue];
   1181     gfx::Rect rect = browserAccessibility_->GetGlobalBoundsForRange(
   1182         range.location, range.length);
   1183     NSPoint origin = NSMakePoint(rect.x(), rect.y());
   1184     NSSize size = NSMakeSize(rect.width(), rect.height());
   1185     NSPoint pointInScreen = [self pointInScreen:origin size:size];
   1186     NSRect nsrect = NSMakeRect(
   1187         pointInScreen.x, pointInScreen.y, rect.width(), rect.height());
   1188     return [NSValue valueWithRect:nsrect];
   1189   }
   1190 
   1191   // TODO(dtseng): support the following attributes.
   1192   if ([attribute isEqualTo:
   1193           NSAccessibilityRangeForPositionParameterizedAttribute] ||
   1194       [attribute isEqualTo:
   1195           NSAccessibilityRangeForIndexParameterizedAttribute] ||
   1196       [attribute isEqualTo:NSAccessibilityRTFForRangeParameterizedAttribute] ||
   1197       [attribute isEqualTo:
   1198           NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
   1199     return nil;
   1200   }
   1201   return nil;
   1202 }
   1203 
   1204 // Returns an array of parameterized attributes names that this object will
   1205 // respond to.
   1206 - (NSArray*)accessibilityParameterizedAttributeNames {
   1207   if (!browserAccessibility_)
   1208     return nil;
   1209 
   1210   if ([[self role] isEqualToString:NSAccessibilityTableRole] ||
   1211       [[self role] isEqualToString:NSAccessibilityGridRole]) {
   1212     return [NSArray arrayWithObjects:
   1213         NSAccessibilityCellForColumnAndRowParameterizedAttribute,
   1214         nil];
   1215   }
   1216   if ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
   1217       [[self role] isEqualToString:NSAccessibilityTextAreaRole]) {
   1218     return [NSArray arrayWithObjects:
   1219         NSAccessibilityLineForIndexParameterizedAttribute,
   1220         NSAccessibilityRangeForLineParameterizedAttribute,
   1221         NSAccessibilityStringForRangeParameterizedAttribute,
   1222         NSAccessibilityRangeForPositionParameterizedAttribute,
   1223         NSAccessibilityRangeForIndexParameterizedAttribute,
   1224         NSAccessibilityBoundsForRangeParameterizedAttribute,
   1225         NSAccessibilityRTFForRangeParameterizedAttribute,
   1226         NSAccessibilityAttributedStringForRangeParameterizedAttribute,
   1227         NSAccessibilityStyleRangeForIndexParameterizedAttribute,
   1228         nil];
   1229   }
   1230   if ([self internalRole] == ui::AX_ROLE_STATIC_TEXT) {
   1231     return [NSArray arrayWithObjects:
   1232         NSAccessibilityBoundsForRangeParameterizedAttribute,
   1233         nil];
   1234   }
   1235   return nil;
   1236 }
   1237 
   1238 // Returns an array of action names that this object will respond to.
   1239 - (NSArray*)accessibilityActionNames {
   1240   if (!browserAccessibility_)
   1241     return nil;
   1242 
   1243   NSMutableArray* ret =
   1244       [NSMutableArray arrayWithObject:NSAccessibilityShowMenuAction];
   1245   NSString* role = [self role];
   1246   // TODO(dtseng): this should only get set when there's a default action.
   1247   if (![role isEqualToString:NSAccessibilityStaticTextRole] &&
   1248       ![role isEqualToString:NSAccessibilityTextAreaRole] &&
   1249       ![role isEqualToString:NSAccessibilityTextFieldRole]) {
   1250     [ret addObject:NSAccessibilityPressAction];
   1251   }
   1252 
   1253   return ret;
   1254 }
   1255 
   1256 // Returns a sub-array of values for the given attribute value, starting at
   1257 // index, with up to maxCount items.  If the given index is out of bounds,
   1258 // or there are no values for the given attribute, it will return nil.
   1259 // This method is used for querying subsets of values, without having to
   1260 // return a large set of data, such as elements with a large number of
   1261 // children.
   1262 - (NSArray*)accessibilityArrayAttributeValues:(NSString*)attribute
   1263                                         index:(NSUInteger)index
   1264                                      maxCount:(NSUInteger)maxCount {
   1265   if (!browserAccessibility_)
   1266     return nil;
   1267 
   1268   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
   1269   if (!fullArray)
   1270     return nil;
   1271   NSUInteger arrayCount = [fullArray count];
   1272   if (index >= arrayCount)
   1273     return nil;
   1274   NSRange subRange;
   1275   if ((index + maxCount) > arrayCount) {
   1276     subRange = NSMakeRange(index, arrayCount - index);
   1277   } else {
   1278     subRange = NSMakeRange(index, maxCount);
   1279   }
   1280   return [fullArray subarrayWithRange:subRange];
   1281 }
   1282 
   1283 // Returns the count of the specified accessibility array attribute.
   1284 - (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute {
   1285   if (!browserAccessibility_)
   1286     return nil;
   1287 
   1288   NSArray* fullArray = [self accessibilityAttributeValue:attribute];
   1289   return [fullArray count];
   1290 }
   1291 
   1292 // Returns the list of accessibility attributes that this object supports.
   1293 - (NSArray*)accessibilityAttributeNames {
   1294   if (!browserAccessibility_)
   1295     return nil;
   1296 
   1297   // General attributes.
   1298   NSMutableArray* ret = [NSMutableArray arrayWithObjects:
   1299       NSAccessibilityChildrenAttribute,
   1300       NSAccessibilityDescriptionAttribute,
   1301       NSAccessibilityEnabledAttribute,
   1302       NSAccessibilityFocusedAttribute,
   1303       NSAccessibilityHelpAttribute,
   1304       NSAccessibilityLinkedUIElementsAttribute,
   1305       NSAccessibilityParentAttribute,
   1306       NSAccessibilityPositionAttribute,
   1307       NSAccessibilityRoleAttribute,
   1308       NSAccessibilityRoleDescriptionAttribute,
   1309       NSAccessibilitySizeAttribute,
   1310       NSAccessibilitySubroleAttribute,
   1311       NSAccessibilityTitleAttribute,
   1312       NSAccessibilityTopLevelUIElementAttribute,
   1313       NSAccessibilityValueAttribute,
   1314       NSAccessibilityWindowAttribute,
   1315       @"AXAccessKey",
   1316       @"AXInvalid",
   1317       @"AXRequired",
   1318       @"AXVisited",
   1319       nil];
   1320 
   1321   // Specific role attributes.
   1322   NSString* role = [self role];
   1323   NSString* subrole = [self subrole];
   1324   if ([role isEqualToString:NSAccessibilityTableRole] ||
   1325       [role isEqualToString:NSAccessibilityGridRole]) {
   1326     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1327         NSAccessibilityColumnsAttribute,
   1328         NSAccessibilityVisibleColumnsAttribute,
   1329         NSAccessibilityRowsAttribute,
   1330         NSAccessibilityVisibleRowsAttribute,
   1331         NSAccessibilityVisibleCellsAttribute,
   1332         NSAccessibilityHeaderAttribute,
   1333         NSAccessibilityColumnHeaderUIElementsAttribute,
   1334         NSAccessibilityRowHeaderUIElementsAttribute,
   1335         nil]];
   1336   } else if ([role isEqualToString:NSAccessibilityColumnRole]) {
   1337     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1338         NSAccessibilityIndexAttribute,
   1339         NSAccessibilityHeaderAttribute,
   1340         NSAccessibilityRowsAttribute,
   1341         NSAccessibilityVisibleRowsAttribute,
   1342         nil]];
   1343   } else if ([role isEqualToString:NSAccessibilityCellRole]) {
   1344     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1345         NSAccessibilityColumnIndexRangeAttribute,
   1346         NSAccessibilityRowIndexRangeAttribute,
   1347         nil]];
   1348   } else if ([role isEqualToString:@"AXWebArea"]) {
   1349     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1350         @"AXLoaded",
   1351         @"AXLoadingProgress",
   1352         nil]];
   1353   } else if ([role isEqualToString:NSAccessibilityTextFieldRole] ||
   1354              [role isEqualToString:NSAccessibilityTextAreaRole]) {
   1355     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1356         NSAccessibilityInsertionPointLineNumberAttribute,
   1357         NSAccessibilityNumberOfCharactersAttribute,
   1358         NSAccessibilitySelectedTextAttribute,
   1359         NSAccessibilitySelectedTextRangeAttribute,
   1360         NSAccessibilityVisibleCharacterRangeAttribute,
   1361         nil]];
   1362   } else if ([role isEqualToString:NSAccessibilityTabGroupRole]) {
   1363     [ret addObject:NSAccessibilityTabsAttribute];
   1364   } else if ([role isEqualToString:NSAccessibilityProgressIndicatorRole] ||
   1365              [role isEqualToString:NSAccessibilitySliderRole] ||
   1366              [role isEqualToString:NSAccessibilityScrollBarRole]) {
   1367     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1368         NSAccessibilityMaxValueAttribute,
   1369         NSAccessibilityMinValueAttribute,
   1370         NSAccessibilityOrientationAttribute,
   1371         NSAccessibilityValueDescriptionAttribute,
   1372         nil]];
   1373   } else if ([subrole isEqualToString:NSAccessibilityOutlineRowSubrole]) {
   1374     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1375         NSAccessibilityDisclosingAttribute,
   1376         NSAccessibilityDisclosedByRowAttribute,
   1377         NSAccessibilityDisclosureLevelAttribute,
   1378         NSAccessibilityDisclosedRowsAttribute,
   1379         nil]];
   1380   } else if ([role isEqualToString:NSAccessibilityRowRole]) {
   1381     if (browserAccessibility_->GetParent()) {
   1382       base::string16 parentRole;
   1383       browserAccessibility_->GetParent()->GetHtmlAttribute(
   1384           "role", &parentRole);
   1385       const base::string16 treegridRole(base::ASCIIToUTF16("treegrid"));
   1386       if (parentRole == treegridRole) {
   1387         [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1388             NSAccessibilityDisclosingAttribute,
   1389             NSAccessibilityDisclosedByRowAttribute,
   1390             NSAccessibilityDisclosureLevelAttribute,
   1391             NSAccessibilityDisclosedRowsAttribute,
   1392             nil]];
   1393       } else {
   1394         [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1395             NSAccessibilityIndexAttribute,
   1396             nil]];
   1397       }
   1398     }
   1399   }
   1400 
   1401   // Add the url attribute only if it has a valid url.
   1402   if ([self url] != nil) {
   1403     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1404         NSAccessibilityURLAttribute,
   1405         nil]];
   1406   }
   1407 
   1408   // Live regions.
   1409   if (browserAccessibility_->HasStringAttribute(
   1410           ui::AX_ATTR_LIVE_STATUS)) {
   1411     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1412         @"AXARIALive",
   1413         @"AXARIARelevant",
   1414         nil]];
   1415   }
   1416   if (browserAccessibility_->HasStringAttribute(
   1417           ui::AX_ATTR_CONTAINER_LIVE_STATUS)) {
   1418     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1419         @"AXARIAAtomic",
   1420         @"AXARIABusy",
   1421         nil]];
   1422   }
   1423 
   1424   // Title UI Element.
   1425   if (browserAccessibility_->HasIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT) ||
   1426       (browserAccessibility_->HasIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS) &&
   1427        browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS)
   1428                             .size() == 1)) {
   1429     [ret addObjectsFromArray:[NSArray arrayWithObjects:
   1430          NSAccessibilityTitleUIElementAttribute,
   1431          nil]];
   1432   }
   1433   // TODO(aboxhall): expose NSAccessibilityServesAsTitleForUIElementsAttribute
   1434   // for elements which are referred to by labelledby or are labels
   1435 
   1436   return ret;
   1437 }
   1438 
   1439 // Returns the index of the child in this objects array of children.
   1440 - (NSUInteger)accessibilityGetIndexOf:(id)child {
   1441   if (!browserAccessibility_)
   1442     return nil;
   1443 
   1444   NSUInteger index = 0;
   1445   for (BrowserAccessibilityCocoa* childToCheck in [self children]) {
   1446     if ([child isEqual:childToCheck])
   1447       return index;
   1448     ++index;
   1449   }
   1450   return NSNotFound;
   1451 }
   1452 
   1453 // Returns whether or not the specified attribute can be set by the
   1454 // accessibility API via |accessibilitySetValue:forAttribute:|.
   1455 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute {
   1456   if (!browserAccessibility_)
   1457     return nil;
   1458 
   1459   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
   1460     return GetState(browserAccessibility_,
   1461         ui::AX_STATE_FOCUSABLE);
   1462   if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
   1463     return browserAccessibility_->GetBoolAttribute(
   1464         ui::AX_ATTR_CAN_SET_VALUE);
   1465   }
   1466   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] &&
   1467       ([[self role] isEqualToString:NSAccessibilityTextFieldRole] ||
   1468        [[self role] isEqualToString:NSAccessibilityTextAreaRole]))
   1469     return YES;
   1470 
   1471   return NO;
   1472 }
   1473 
   1474 // Returns whether or not this object should be ignored in the accessibilty
   1475 // tree.
   1476 - (BOOL)accessibilityIsIgnored {
   1477   if (!browserAccessibility_)
   1478     return true;
   1479 
   1480   return [self isIgnored];
   1481 }
   1482 
   1483 // Performs the given accessibilty action on the webkit accessibility object
   1484 // that backs this object.
   1485 - (void)accessibilityPerformAction:(NSString*)action {
   1486   if (!browserAccessibility_)
   1487     return;
   1488 
   1489   // TODO(dmazzoni): Support more actions.
   1490   if ([action isEqualToString:NSAccessibilityPressAction]) {
   1491     [self delegate]->AccessibilityDoDefaultAction(
   1492         browserAccessibility_->GetId());
   1493   } else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
   1494     [self delegate]->AccessibilityShowMenu(browserAccessibility_->GetId());
   1495   }
   1496 }
   1497 
   1498 // Returns the description of the given action.
   1499 - (NSString*)accessibilityActionDescription:(NSString*)action {
   1500   if (!browserAccessibility_)
   1501     return nil;
   1502 
   1503   return NSAccessibilityActionDescription(action);
   1504 }
   1505 
   1506 // Sets an override value for a specific accessibility attribute.
   1507 // This class does not support this.
   1508 - (BOOL)accessibilitySetOverrideValue:(id)value
   1509                          forAttribute:(NSString*)attribute {
   1510   return NO;
   1511 }
   1512 
   1513 // Sets the value for an accessibility attribute via the accessibility API.
   1514 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
   1515   if (!browserAccessibility_)
   1516     return;
   1517 
   1518   if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
   1519     NSNumber* focusedNumber = value;
   1520     BOOL focused = [focusedNumber intValue];
   1521     if (focused)
   1522       [self delegate]->AccessibilitySetFocus(browserAccessibility_->GetId());
   1523   }
   1524   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
   1525     NSRange range = [(NSValue*)value rangeValue];
   1526     [self delegate]->AccessibilitySetTextSelection(
   1527         browserAccessibility_->GetId(),
   1528         range.location, range.location + range.length);
   1529   }
   1530 }
   1531 
   1532 // Returns the deepest accessibility child that should not be ignored.
   1533 // It is assumed that the hit test has been narrowed down to this object
   1534 // or one of its children, so this will never return nil unless this
   1535 // object is invalid.
   1536 - (id)accessibilityHitTest:(NSPoint)point {
   1537   if (!browserAccessibility_)
   1538     return nil;
   1539 
   1540   BrowserAccessibilityCocoa* hit = self;
   1541   for (BrowserAccessibilityCocoa* child in [self children]) {
   1542     if (!child->browserAccessibility_)
   1543       continue;
   1544     NSPoint origin = [child origin];
   1545     NSSize size = [[child size] sizeValue];
   1546     NSRect rect;
   1547     rect.origin = origin;
   1548     rect.size = size;
   1549     if (NSPointInRect(point, rect)) {
   1550       hit = child;
   1551       id childResult = [child accessibilityHitTest:point];
   1552       if (![childResult accessibilityIsIgnored]) {
   1553         hit = childResult;
   1554         break;
   1555       }
   1556     }
   1557   }
   1558   return NSAccessibilityUnignoredAncestor(hit);
   1559 }
   1560 
   1561 - (BOOL)isEqual:(id)object {
   1562   if (![object isKindOfClass:[BrowserAccessibilityCocoa class]])
   1563     return NO;
   1564   return ([self hash] == [object hash]);
   1565 }
   1566 
   1567 - (NSUInteger)hash {
   1568   // Potentially called during dealloc.
   1569   if (!browserAccessibility_)
   1570     return [super hash];
   1571   return browserAccessibility_->GetId();
   1572 }
   1573 
   1574 - (BOOL)accessibilityShouldUseUniqueId {
   1575   return YES;
   1576 }
   1577 
   1578 @end
   1579 
   1580