Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2011 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 "chrome/browser/accessibility/browser_accessibility_win.h"
      6 
      7 #include "base/string_number_conversions.h"
      8 #include "base/string_util.h"
      9 #include "base/utf_string_conversions.h"
     10 #include "chrome/browser/accessibility/browser_accessibility_manager_win.h"
     11 #include "net/base/escape.h"
     12 
     13 using webkit_glue::WebAccessibility;
     14 
     15 // The GUID for the ISimpleDOM service is not defined in the IDL files.
     16 // This is taken directly from the Mozilla sources
     17 // (accessible/src/msaa/nsAccessNodeWrap.cpp) and it's also documented at:
     18 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
     19 
     20 const GUID GUID_ISimpleDOM = {
     21     0x0c539790, 0x12e4, 0x11cf,
     22     0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
     23 
     24 // static
     25 BrowserAccessibility* BrowserAccessibility::Create() {
     26   CComObject<BrowserAccessibilityWin>* instance;
     27   HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
     28   DCHECK(SUCCEEDED(hr));
     29   return instance->NewReference();
     30 }
     31 
     32 BrowserAccessibilityWin* BrowserAccessibility::toBrowserAccessibilityWin() {
     33   return static_cast<BrowserAccessibilityWin*>(this);
     34 }
     35 
     36 BrowserAccessibilityWin::BrowserAccessibilityWin()
     37     : ia_role_(0),
     38       ia_state_(0),
     39       ia2_role_(0),
     40       ia2_state_(0) {
     41 }
     42 
     43 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
     44 }
     45 
     46 //
     47 // IAccessible methods.
     48 //
     49 // Conventions:
     50 // * Always test for instance_active_ first and return E_FAIL if it's false.
     51 // * Always check for invalid arguments first, even if they're unused.
     52 // * Return S_FALSE if the only output is a string argument and it's empty.
     53 //
     54 
     55 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
     56   if (!instance_active_)
     57     return E_FAIL;
     58 
     59   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
     60   if (!target)
     61     return E_INVALIDARG;
     62 
     63   manager_->DoDefaultAction(*target);
     64   return S_OK;
     65 }
     66 
     67 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
     68                                                  LONG y_top,
     69                                                  VARIANT* child) {
     70   if (!instance_active_)
     71     return E_FAIL;
     72 
     73   if (!child)
     74     return E_INVALIDARG;
     75 
     76   gfx::Point point(x_left, y_top);
     77   if (!GetBoundsRect().Contains(point)) {
     78     // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
     79     child->vt = VT_EMPTY;
     80     return S_FALSE;
     81   }
     82 
     83   BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
     84   if (result == this) {
     85     // Point is within this object.
     86     child->vt = VT_I4;
     87     child->lVal = CHILDID_SELF;
     88   } else {
     89     child->vt = VT_DISPATCH;
     90     child->pdispVal = result->toBrowserAccessibilityWin()->NewReference();
     91   }
     92   return S_OK;
     93 }
     94 
     95 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, LONG* y_top,
     96                                                   LONG* width, LONG* height,
     97                                                   VARIANT var_id) {
     98   if (!instance_active_)
     99     return E_FAIL;
    100 
    101   if (!x_left || !y_top || !width || !height)
    102     return E_INVALIDARG;
    103 
    104   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    105   if (!target)
    106     return E_INVALIDARG;
    107 
    108   gfx::Rect bounds = target->GetBoundsRect();
    109   *x_left = bounds.x();
    110   *y_top  = bounds.y();
    111   *width  = bounds.width();
    112   *height = bounds.height();
    113 
    114   return S_OK;
    115 }
    116 
    117 STDMETHODIMP BrowserAccessibilityWin::accNavigate(
    118     LONG nav_dir, VARIANT start, VARIANT* end) {
    119   BrowserAccessibilityWin* target = GetTargetFromChildID(start);
    120   if (!target)
    121     return E_INVALIDARG;
    122 
    123   if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
    124       start.lVal != CHILDID_SELF) {
    125     // MSAA states that navigating to first/last child can only be from self.
    126     return E_INVALIDARG;
    127   }
    128 
    129   BrowserAccessibility* result = NULL;
    130   switch (nav_dir) {
    131     case NAVDIR_DOWN:
    132     case NAVDIR_UP:
    133     case NAVDIR_LEFT:
    134     case NAVDIR_RIGHT:
    135       // These directions are not implemented, matching Mozilla and IE.
    136       return E_NOTIMPL;
    137     case NAVDIR_FIRSTCHILD:
    138       if (!target->children_.empty())
    139         result = target->children_.front();
    140       break;
    141     case NAVDIR_LASTCHILD:
    142       if (!target->children_.empty())
    143         result = target->children_.back();
    144       break;
    145     case NAVDIR_NEXT:
    146       result = target->GetNextSibling();
    147       break;
    148     case NAVDIR_PREVIOUS:
    149       result = target->GetPreviousSibling();
    150       break;
    151   }
    152 
    153   if (!result) {
    154     end->vt = VT_EMPTY;
    155     return S_FALSE;
    156   }
    157 
    158   end->vt = VT_DISPATCH;
    159   end->pdispVal = result->toBrowserAccessibilityWin()->NewReference();
    160   return S_OK;
    161 }
    162 
    163 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
    164                                                    IDispatch** disp_child) {
    165   if (!instance_active_)
    166     return E_FAIL;
    167 
    168   if (!disp_child)
    169     return E_INVALIDARG;
    170 
    171   *disp_child = NULL;
    172 
    173   BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
    174   if (!target)
    175     return E_INVALIDARG;
    176 
    177   (*disp_child) = target->NewReference();
    178   return S_OK;
    179 }
    180 
    181 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
    182   if (!instance_active_)
    183     return E_FAIL;
    184 
    185   if (!child_count)
    186     return E_INVALIDARG;
    187 
    188   *child_count = children_.size();
    189   return S_OK;
    190 }
    191 
    192 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
    193                                                            BSTR* def_action) {
    194   if (!instance_active_)
    195     return E_FAIL;
    196 
    197   if (!def_action)
    198     return E_INVALIDARG;
    199 
    200   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    201   if (!target)
    202     return E_INVALIDARG;
    203 
    204   return target->GetAttributeAsBstr(
    205       WebAccessibility::ATTR_SHORTCUT, def_action);
    206 }
    207 
    208 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
    209                                                          BSTR* desc) {
    210   if (!instance_active_)
    211     return E_FAIL;
    212 
    213   if (!desc)
    214     return E_INVALIDARG;
    215 
    216   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    217   if (!target)
    218     return E_INVALIDARG;
    219 
    220   return target->GetAttributeAsBstr(WebAccessibility::ATTR_DESCRIPTION, desc);
    221 }
    222 
    223 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
    224   if (!instance_active_)
    225     return E_FAIL;
    226 
    227   if (!focus_child)
    228     return E_INVALIDARG;
    229 
    230   BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
    231       manager_->GetFocus(this));
    232   if (focus == this) {
    233     focus_child->vt = VT_I4;
    234     focus_child->lVal = CHILDID_SELF;
    235   } else if (focus == NULL) {
    236     focus_child->vt = VT_EMPTY;
    237   } else {
    238     focus_child->vt = VT_DISPATCH;
    239     focus_child->pdispVal = focus->NewReference();
    240   }
    241 
    242   return S_OK;
    243 }
    244 
    245 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
    246   if (!instance_active_)
    247     return E_FAIL;
    248 
    249   if (!help)
    250     return E_INVALIDARG;
    251 
    252   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    253   if (!target)
    254     return E_INVALIDARG;
    255 
    256   return target->GetAttributeAsBstr(WebAccessibility::ATTR_HELP, help);
    257 }
    258 
    259 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
    260                                                               BSTR* acc_key) {
    261   if (!instance_active_)
    262     return E_FAIL;
    263 
    264   if (!acc_key)
    265     return E_INVALIDARG;
    266 
    267   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    268   if (!target)
    269     return E_INVALIDARG;
    270 
    271   return target->GetAttributeAsBstr(WebAccessibility::ATTR_SHORTCUT, acc_key);
    272 }
    273 
    274 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
    275   if (!instance_active_)
    276     return E_FAIL;
    277 
    278   if (!name)
    279     return E_INVALIDARG;
    280 
    281   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    282   if (!target)
    283     return E_INVALIDARG;
    284 
    285   if (target->name_.empty())
    286     return S_FALSE;
    287 
    288   *name = SysAllocString(target->name_.c_str());
    289 
    290   DCHECK(*name);
    291   return S_OK;
    292 }
    293 
    294 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
    295   if (!instance_active_)
    296     return E_FAIL;
    297 
    298   if (!disp_parent)
    299     return E_INVALIDARG;
    300 
    301   IAccessible* parent = parent_->toBrowserAccessibilityWin();
    302   if (parent == NULL) {
    303     // This happens if we're the root of the tree;
    304     // return the IAccessible for the window.
    305     parent = manager_->toBrowserAccessibilityManagerWin()->
    306              GetParentWindowIAccessible();
    307   }
    308 
    309   parent->AddRef();
    310   *disp_parent = parent;
    311   return S_OK;
    312 }
    313 
    314 STDMETHODIMP BrowserAccessibilityWin::get_accRole(
    315     VARIANT var_id, VARIANT* role) {
    316   if (!instance_active_)
    317     return E_FAIL;
    318 
    319   if (!role)
    320     return E_INVALIDARG;
    321 
    322   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    323   if (!target)
    324     return E_INVALIDARG;
    325 
    326   if (!target->role_name_.empty()) {
    327     role->vt = VT_BSTR;
    328     role->bstrVal = SysAllocString(target->role_name_.c_str());
    329   } else {
    330     role->vt = VT_I4;
    331     role->lVal = target->ia_role_;
    332   }
    333   return S_OK;
    334 }
    335 
    336 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
    337                                                    VARIANT* state) {
    338   if (!instance_active_)
    339     return E_FAIL;
    340 
    341   if (!state)
    342     return E_INVALIDARG;
    343 
    344   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    345   if (!target)
    346     return E_INVALIDARG;
    347 
    348   state->vt = VT_I4;
    349   state->lVal = target->ia_state_;
    350   if (manager_->GetFocus(NULL) == this)
    351     state->lVal |= STATE_SYSTEM_FOCUSED;
    352 
    353   return S_OK;
    354 }
    355 
    356 STDMETHODIMP BrowserAccessibilityWin::get_accValue(
    357     VARIANT var_id, BSTR* value) {
    358   if (!instance_active_)
    359     return E_FAIL;
    360 
    361   if (!value)
    362     return E_INVALIDARG;
    363 
    364   BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
    365   if (!target)
    366     return E_INVALIDARG;
    367 
    368   *value = SysAllocString(target->value_.c_str());
    369 
    370   DCHECK(*value);
    371   return S_OK;
    372 }
    373 
    374 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(
    375     BSTR* help_file, VARIANT var_id, LONG* topic_id) {
    376   return E_NOTIMPL;
    377 }
    378 
    379 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
    380   if (!instance_active_)
    381     return E_FAIL;
    382 
    383   return E_NOTIMPL;
    384 }
    385 
    386 STDMETHODIMP BrowserAccessibilityWin::accSelect(
    387     LONG flags_sel, VARIANT var_id) {
    388   if (!instance_active_)
    389     return E_FAIL;
    390 
    391   if (flags_sel & SELFLAG_TAKEFOCUS) {
    392     manager_->SetFocus(this, true);
    393     return S_OK;
    394   }
    395 
    396   return S_FALSE;
    397 }
    398 
    399 //
    400 // IAccessible2 methods.
    401 //
    402 
    403 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
    404   if (!instance_active_)
    405     return E_FAIL;
    406 
    407   if (!role)
    408     return E_INVALIDARG;
    409 
    410   *role = ia2_role_;
    411 
    412   return S_OK;
    413 }
    414 
    415 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
    416   if (!instance_active_)
    417     return E_FAIL;
    418 
    419   if (!attributes)
    420     return E_INVALIDARG;
    421 
    422   // Follow Firefox's convention, which is to return a set of key-value pairs
    423   // separated by semicolons, with a colon between the key and the value.
    424   string16 str;
    425   for (unsigned int i = 0; i < html_attributes_.size(); i++) {
    426     if (i != 0)
    427       str += L';';
    428     str += Escape(html_attributes_[i].first);
    429     str += L':';
    430     str += Escape(html_attributes_[i].second);
    431   }
    432 
    433   if (str.empty())
    434     return S_FALSE;
    435 
    436   *attributes = SysAllocString(str.c_str());
    437   DCHECK(*attributes);
    438   return S_OK;
    439 }
    440 
    441 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
    442   if (!instance_active_)
    443     return E_FAIL;
    444 
    445   if (!states)
    446     return E_INVALIDARG;
    447 
    448   *states = ia2_state_;
    449 
    450   return S_OK;
    451 }
    452 
    453 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
    454   if (!instance_active_)
    455     return E_FAIL;
    456 
    457   if (!unique_id)
    458     return E_INVALIDARG;
    459 
    460   *unique_id = child_id_;
    461   return S_OK;
    462 }
    463 
    464 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
    465   if (!instance_active_)
    466     return E_FAIL;
    467 
    468   if (!window_handle)
    469     return E_INVALIDARG;
    470 
    471   *window_handle = manager_->GetParentView();
    472   return S_OK;
    473 }
    474 
    475 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
    476   if (!instance_active_)
    477     return E_FAIL;
    478 
    479   if (!index_in_parent)
    480     return E_INVALIDARG;
    481 
    482   *index_in_parent = index_in_parent_;
    483   return S_OK;
    484 }
    485 
    486 //
    487 // IAccessibleImage methods.
    488 //
    489 
    490 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
    491   if (!instance_active_)
    492     return E_FAIL;
    493 
    494   if (!desc)
    495     return E_INVALIDARG;
    496 
    497   return GetAttributeAsBstr(WebAccessibility::ATTR_DESCRIPTION, desc);
    498 }
    499 
    500 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
    501     enum IA2CoordinateType coordinate_type, LONG* x, LONG* y) {
    502   if (!instance_active_)
    503     return E_FAIL;
    504 
    505   if (!x || !y)
    506     return E_INVALIDARG;
    507 
    508   if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
    509     HWND parent_hwnd = manager_->GetParentView();
    510     POINT top_left = {0, 0};
    511     ::ClientToScreen(parent_hwnd, &top_left);
    512     *x = location_.x() + top_left.x;
    513     *y = location_.y() + top_left.y;
    514   } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
    515     *x = location_.x();
    516     *y = location_.y();
    517     if (parent_) {
    518       *x -= parent_->location().x();
    519       *y -= parent_->location().y();
    520     }
    521   } else {
    522     return E_INVALIDARG;
    523   }
    524 
    525   return S_OK;
    526 }
    527 
    528 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
    529   if (!instance_active_)
    530     return E_FAIL;
    531 
    532   if (!height || !width)
    533     return E_INVALIDARG;
    534 
    535   *height = location_.height();
    536   *width = location_.width();
    537   return S_OK;
    538 }
    539 
    540 //
    541 // IAccessibleText methods.
    542 //
    543 
    544 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
    545   if (!instance_active_)
    546     return E_FAIL;
    547 
    548   if (!n_characters)
    549     return E_INVALIDARG;
    550 
    551   if (role_ == WebAccessibility::ROLE_TEXT_FIELD) {
    552     *n_characters = value_.length();
    553   } else {
    554     *n_characters = name_.length();
    555   }
    556 
    557   return S_OK;
    558 }
    559 
    560 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
    561   if (!instance_active_)
    562     return E_FAIL;
    563 
    564   if (!offset)
    565     return E_INVALIDARG;
    566 
    567   if (role_ == WebAccessibility::ROLE_TEXT_FIELD) {
    568     int sel_start = 0;
    569     if (GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_START, &sel_start)) {
    570       *offset = sel_start;
    571     } else {
    572       *offset = 0;
    573     }
    574   } else {
    575     *offset = 0;
    576   }
    577 
    578   return S_OK;
    579 }
    580 
    581 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
    582   if (!instance_active_)
    583     return E_FAIL;
    584 
    585   if (!n_selections)
    586     return E_INVALIDARG;
    587 
    588   if (role_ == WebAccessibility::ROLE_TEXT_FIELD) {
    589     int sel_start = 0;
    590     int sel_end = 0;
    591     if (GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_START, &sel_start) &&
    592         GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_END, &sel_end) &&
    593         sel_start != sel_end) {
    594       *n_selections = 1;
    595     } else {
    596       *n_selections = 0;
    597     }
    598   } else {
    599     *n_selections = 0;
    600   }
    601 
    602   return S_OK;
    603 }
    604 
    605 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
    606                                                     LONG* start_offset,
    607                                                     LONG* end_offset) {
    608   if (!instance_active_)
    609     return E_FAIL;
    610 
    611   if (!start_offset || !end_offset || selection_index != 0)
    612     return E_INVALIDARG;
    613 
    614   if (role_ == WebAccessibility::ROLE_TEXT_FIELD) {
    615     int sel_start = 0;
    616     int sel_end = 0;
    617     if (GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_START, &sel_start) &&
    618         GetAttributeAsInt(WebAccessibility::ATTR_TEXT_SEL_END, &sel_end)) {
    619       *start_offset = sel_start;
    620       *end_offset = sel_end;
    621     } else {
    622       *start_offset = 0;
    623       *end_offset = 0;
    624     }
    625   } else {
    626     *start_offset = 0;
    627     *end_offset = 0;
    628   }
    629 
    630   return S_OK;
    631 }
    632 
    633 STDMETHODIMP BrowserAccessibilityWin::get_text(
    634     LONG start_offset, LONG end_offset, BSTR* text) {
    635   if (!instance_active_)
    636     return E_FAIL;
    637 
    638   if (!text)
    639     return E_INVALIDARG;
    640 
    641   const string16& text_str = TextForIAccessibleText();
    642 
    643   // The spec allows the arguments to be reversed.
    644   if (start_offset > end_offset) {
    645     LONG tmp = start_offset;
    646     start_offset = end_offset;
    647     end_offset = tmp;
    648   }
    649 
    650   // The spec does not allow the start or end offsets to be out or range;
    651   // we must return an error if so.
    652   LONG len = text_str.length();
    653   if (start_offset < 0)
    654     return E_INVALIDARG;
    655   if (end_offset > len)
    656     return E_INVALIDARG;
    657 
    658   string16 substr = text_str.substr(start_offset, end_offset - start_offset);
    659   if (substr.empty())
    660     return S_FALSE;
    661 
    662   *text = SysAllocString(substr.c_str());
    663   DCHECK(*text);
    664   return S_OK;
    665 }
    666 
    667 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
    668     LONG offset,
    669     enum IA2TextBoundaryType boundary_type,
    670     LONG* start_offset, LONG* end_offset,
    671     BSTR* text) {
    672   if (!instance_active_)
    673     return E_FAIL;
    674 
    675   if (!start_offset || !end_offset || !text)
    676     return E_INVALIDARG;
    677 
    678   // The IAccessible2 spec says we don't have to implement the "sentence"
    679   // boundary type, we can just let the screenreader handle it.
    680   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    681     *start_offset = 0;
    682     *end_offset = 0;
    683     *text = NULL;
    684     return S_FALSE;
    685   }
    686 
    687   const string16& text_str = TextForIAccessibleText();
    688 
    689   *start_offset = FindBoundary(text_str, boundary_type, offset, -1);
    690   *end_offset = FindBoundary(text_str, boundary_type, offset, 1);
    691   return get_text(*start_offset, *end_offset, text);
    692 }
    693 
    694 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
    695     LONG offset,
    696     enum IA2TextBoundaryType boundary_type,
    697     LONG* start_offset, LONG* end_offset,
    698     BSTR* text) {
    699   if (!instance_active_)
    700     return E_FAIL;
    701 
    702   if (!start_offset || !end_offset || !text)
    703     return E_INVALIDARG;
    704 
    705   // The IAccessible2 spec says we don't have to implement the "sentence"
    706   // boundary type, we can just let the screenreader handle it.
    707   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    708     *start_offset = 0;
    709     *end_offset = 0;
    710     *text = NULL;
    711     return S_FALSE;
    712   }
    713 
    714   const string16& text_str = TextForIAccessibleText();
    715 
    716   *start_offset = FindBoundary(text_str, boundary_type, offset, -1);
    717   *end_offset = offset;
    718   return get_text(*start_offset, *end_offset, text);
    719 }
    720 
    721 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
    722     LONG offset,
    723     enum IA2TextBoundaryType boundary_type,
    724     LONG* start_offset, LONG* end_offset,
    725     BSTR* text) {
    726   if (!instance_active_)
    727     return E_FAIL;
    728 
    729   if (!start_offset || !end_offset || !text)
    730     return E_INVALIDARG;
    731 
    732   // The IAccessible2 spec says we don't have to implement the "sentence"
    733   // boundary type, we can just let the screenreader handle it.
    734   if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
    735     *start_offset = 0;
    736     *end_offset = 0;
    737     *text = NULL;
    738     return S_FALSE;
    739   }
    740 
    741   const string16& text_str = TextForIAccessibleText();
    742 
    743   *start_offset = offset;
    744   *end_offset = FindBoundary(text_str, boundary_type, offset, 1);
    745   return get_text(*start_offset, *end_offset, text);
    746 }
    747 
    748 //
    749 // ISimpleDOMDocument methods.
    750 //
    751 
    752 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
    753   if (!instance_active_)
    754     return E_FAIL;
    755 
    756   if (!url)
    757     return E_INVALIDARG;
    758 
    759   return GetAttributeAsBstr(WebAccessibility::ATTR_DOC_URL, url);
    760 }
    761 
    762 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
    763   if (!instance_active_)
    764     return E_FAIL;
    765 
    766   if (!title)
    767     return E_INVALIDARG;
    768 
    769   return GetAttributeAsBstr(WebAccessibility::ATTR_DOC_TITLE, title);
    770 }
    771 
    772 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
    773   if (!instance_active_)
    774     return E_FAIL;
    775 
    776   if (!mime_type)
    777     return E_INVALIDARG;
    778 
    779   return GetAttributeAsBstr(WebAccessibility::ATTR_DOC_MIMETYPE, mime_type);
    780 }
    781 
    782 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
    783   if (!instance_active_)
    784     return E_FAIL;
    785 
    786   if (!doc_type)
    787     return E_INVALIDARG;
    788 
    789   return GetAttributeAsBstr(WebAccessibility::ATTR_DOC_DOCTYPE, doc_type);
    790 }
    791 
    792 //
    793 // ISimpleDOMNode methods.
    794 //
    795 
    796 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
    797     BSTR* node_name,
    798     short* name_space_id,
    799     BSTR* node_value,
    800     unsigned int* num_children,
    801     unsigned int* unique_id,
    802     unsigned short* node_type) {
    803   if (!instance_active_)
    804     return E_FAIL;
    805 
    806   if (!node_name || !name_space_id || !node_value || !num_children ||
    807       !unique_id || !node_type) {
    808     return E_INVALIDARG;
    809   }
    810 
    811   string16 tag;
    812   if (GetAttribute(WebAccessibility::ATTR_HTML_TAG, &tag))
    813     *node_name = SysAllocString(tag.c_str());
    814   else
    815     *node_name = NULL;
    816 
    817   *name_space_id = 0;
    818   *node_value = SysAllocString(value_.c_str());
    819   *num_children = children_.size();
    820   *unique_id = child_id_;
    821 
    822   if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
    823     *node_type = NODETYPE_DOCUMENT;
    824   } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
    825              ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
    826     *node_type = NODETYPE_TEXT;
    827   } else {
    828     *node_type = NODETYPE_ELEMENT;
    829   }
    830 
    831   return S_OK;
    832 }
    833 
    834 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
    835     unsigned short max_attribs,
    836     BSTR* attrib_names,
    837     short* name_space_id,
    838     BSTR* attrib_values,
    839     unsigned short* num_attribs) {
    840   if (!instance_active_)
    841     return E_FAIL;
    842 
    843   if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
    844     return E_INVALIDARG;
    845 
    846   *num_attribs = max_attribs;
    847   if (*num_attribs > html_attributes_.size())
    848     *num_attribs = html_attributes_.size();
    849 
    850   for (unsigned short i = 0; i < *num_attribs; ++i) {
    851     attrib_names[i] = SysAllocString(html_attributes_[i].first.c_str());
    852     name_space_id[i] = 0;
    853     attrib_values[i] = SysAllocString(html_attributes_[i].second.c_str());
    854   }
    855   return S_OK;
    856 }
    857 
    858 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
    859     unsigned short num_attribs,
    860     BSTR* attrib_names,
    861     short* name_space_id,
    862     BSTR* attrib_values) {
    863   if (!instance_active_)
    864     return E_FAIL;
    865 
    866   if (!attrib_names || !name_space_id || !attrib_values)
    867     return E_INVALIDARG;
    868 
    869   for (unsigned short i = 0; i < num_attribs; ++i) {
    870     name_space_id[i] = 0;
    871     bool found = false;
    872     string16 name = (LPCWSTR)attrib_names[i];
    873     for (unsigned int j = 0;  j < html_attributes_.size(); ++j) {
    874       if (html_attributes_[j].first == name) {
    875         attrib_values[i] = SysAllocString(html_attributes_[j].second.c_str());
    876         found = true;
    877         break;
    878       }
    879     }
    880     if (!found) {
    881       attrib_values[i] = NULL;
    882     }
    883   }
    884   return S_OK;
    885 }
    886 
    887 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
    888     unsigned short max_style_properties,
    889     boolean use_alternate_view,
    890     BSTR *style_properties,
    891     BSTR *style_values,
    892     unsigned short *num_style_properties)  {
    893   if (!instance_active_)
    894     return E_FAIL;
    895 
    896   if (!style_properties || !style_values)
    897     return E_INVALIDARG;
    898 
    899   // We only cache a single style property for now: DISPLAY
    900 
    901   if (max_style_properties == 0 ||
    902       !HasAttribute(WebAccessibility::ATTR_DISPLAY)) {
    903     *num_style_properties = 0;
    904     return S_OK;
    905   }
    906 
    907   string16 display;
    908   GetAttribute(WebAccessibility::ATTR_DISPLAY, &display);
    909   *num_style_properties = 1;
    910   style_properties[0] = SysAllocString(L"display");
    911   style_values[0] = SysAllocString(display.c_str());
    912 
    913   return S_OK;
    914 }
    915 
    916 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
    917     unsigned short num_style_properties,
    918     boolean use_alternate_view,
    919     BSTR* style_properties,
    920     BSTR* style_values) {
    921   if (!instance_active_)
    922     return E_FAIL;
    923 
    924   if (!style_properties || !style_values)
    925     return E_INVALIDARG;
    926 
    927   // We only cache a single style property for now: DISPLAY
    928 
    929   for (unsigned short i = 0; i < num_style_properties; i++) {
    930     string16 name = (LPCWSTR)style_properties[i];
    931     StringToLowerASCII(&name);
    932     if (name == L"display") {
    933       string16 display;
    934       GetAttribute(WebAccessibility::ATTR_DISPLAY, &display);
    935       style_values[i] = SysAllocString(display.c_str());
    936     } else {
    937       style_values[i] = NULL;
    938     }
    939   }
    940 
    941   return S_OK;
    942 }
    943 
    944 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
    945   return E_NOTIMPL;
    946 }
    947 
    948 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
    949   if (!instance_active_)
    950     return E_FAIL;
    951 
    952   if (!node)
    953     return E_INVALIDARG;
    954 
    955   *node = parent_->toBrowserAccessibilityWin()->NewReference();
    956   return S_OK;
    957 }
    958 
    959 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node)  {
    960   if (!instance_active_)
    961     return E_FAIL;
    962 
    963   if (!node)
    964     return E_INVALIDARG;
    965 
    966   if (children_.size()) {
    967     *node = children_[0]->toBrowserAccessibilityWin()->NewReference();
    968     return S_OK;
    969   } else {
    970     *node = NULL;
    971     return S_FALSE;
    972   }
    973 }
    974 
    975 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
    976   if (!instance_active_)
    977     return E_FAIL;
    978 
    979   if (!node)
    980     return E_INVALIDARG;
    981 
    982   if (children_.size()) {
    983     *node = children_[children_.size() - 1]->toBrowserAccessibilityWin()->
    984         NewReference();
    985     return S_OK;
    986   } else {
    987     *node = NULL;
    988     return S_FALSE;
    989   }
    990 }
    991 
    992 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
    993     ISimpleDOMNode** node) {
    994   if (!instance_active_)
    995     return E_FAIL;
    996 
    997   if (!node)
    998     return E_INVALIDARG;
    999 
   1000   if (parent_ && index_in_parent_ > 0) {
   1001     *node = parent_->children()[index_in_parent_ - 1]->
   1002         toBrowserAccessibilityWin()->NewReference();
   1003     return S_OK;
   1004   } else {
   1005     *node = NULL;
   1006     return S_FALSE;
   1007   }
   1008 }
   1009 
   1010 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
   1011   if (!instance_active_)
   1012     return E_FAIL;
   1013 
   1014   if (!node)
   1015     return E_INVALIDARG;
   1016 
   1017   if (parent_ &&
   1018       index_in_parent_ >= 0 &&
   1019       index_in_parent_ < static_cast<int>(parent_->children().size()) - 1) {
   1020     *node = parent_->children()[index_in_parent_ + 1]->
   1021         toBrowserAccessibilityWin()->NewReference();
   1022     return S_OK;
   1023   } else {
   1024     *node = NULL;
   1025     return S_FALSE;
   1026   }
   1027 }
   1028 
   1029 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
   1030     unsigned int child_index,
   1031     ISimpleDOMNode** node) {
   1032   if (!instance_active_)
   1033     return E_FAIL;
   1034 
   1035   if (!node)
   1036     return E_INVALIDARG;
   1037 
   1038   if (child_index < children_.size()) {
   1039     *node = children_[child_index]->toBrowserAccessibilityWin()->NewReference();
   1040     return S_OK;
   1041   } else {
   1042     *node = NULL;
   1043     return S_FALSE;
   1044   }
   1045 }
   1046 
   1047 //
   1048 // ISimpleDOMText methods.
   1049 //
   1050 
   1051 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
   1052   if (!instance_active_)
   1053     return E_FAIL;
   1054 
   1055   if (!dom_text)
   1056     return E_INVALIDARG;
   1057 
   1058   if (name_.empty())
   1059     return S_FALSE;
   1060 
   1061   *dom_text = SysAllocString(name_.c_str());
   1062   DCHECK(*dom_text);
   1063   return S_OK;
   1064 }
   1065 
   1066 //
   1067 // IServiceProvider methods.
   1068 //
   1069 
   1070 STDMETHODIMP BrowserAccessibilityWin::QueryService(
   1071     REFGUID guidService, REFIID riid, void** object) {
   1072   if (!instance_active_)
   1073     return E_FAIL;
   1074 
   1075   if (guidService == IID_IAccessible ||
   1076       guidService == IID_IAccessible2 ||
   1077       guidService == IID_IAccessibleImage ||
   1078       guidService == IID_IAccessibleText ||
   1079       guidService == IID_ISimpleDOMDocument ||
   1080       guidService == IID_ISimpleDOMNode ||
   1081       guidService == IID_ISimpleDOMText ||
   1082       guidService == GUID_ISimpleDOM) {
   1083     return QueryInterface(riid, object);
   1084   }
   1085 
   1086   *object = NULL;
   1087   return E_FAIL;
   1088 }
   1089 
   1090 //
   1091 // CComObjectRootEx methods.
   1092 //
   1093 
   1094 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
   1095     void* this_ptr,
   1096     const _ATL_INTMAP_ENTRY* entries,
   1097     REFIID iid,
   1098     void** object) {
   1099   if (iid == IID_IAccessibleText) {
   1100     if (ia_role_ != ROLE_SYSTEM_LINK && ia_role_ != ROLE_SYSTEM_TEXT) {
   1101       *object = NULL;
   1102       return E_NOINTERFACE;
   1103     }
   1104   } else if (iid == IID_IAccessibleImage) {
   1105     if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
   1106       *object = NULL;
   1107       return E_NOINTERFACE;
   1108     }
   1109   } else if (iid == IID_ISimpleDOMDocument) {
   1110     if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
   1111       *object = NULL;
   1112       return E_NOINTERFACE;
   1113     }
   1114   }
   1115 
   1116   return CComObjectRootBase::InternalQueryInterface(
   1117       this_ptr, entries, iid, object);
   1118 }
   1119 
   1120 //
   1121 // Private methods.
   1122 //
   1123 
   1124 // Initialize this object and mark it as active.
   1125 void BrowserAccessibilityWin::Initialize() {
   1126   BrowserAccessibility::Initialize();
   1127 
   1128   InitRoleAndState();
   1129 
   1130   // Expose headings levels to NVDA with the "level" object attribute.
   1131   if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 &&
   1132           IsAsciiDigit(role_name_[1])) {
   1133     html_attributes_.push_back(std::make_pair(L"level", role_name_.substr(1)));
   1134   }
   1135 
   1136   // Expose the "display" object attribute.
   1137   string16 display;
   1138   if (GetAttribute(WebAccessibility::ATTR_DISPLAY, &display))
   1139     html_attributes_.push_back(std::make_pair(L"display", display));
   1140 
   1141   // If this is static text, put the text in the name rather than the value.
   1142   if (role_ == WebAccessibility::ROLE_STATIC_TEXT && name_.empty())
   1143     name_.swap(value_);
   1144 
   1145   // If this object doesn't have a name but it does have a description,
   1146   // use the description as its name - because some screen readers only
   1147   // announce the name.
   1148   if (name_.empty() && HasAttribute(WebAccessibility::ATTR_DESCRIPTION))
   1149     GetAttribute(WebAccessibility::ATTR_DESCRIPTION, &name_);
   1150 }
   1151 
   1152 void BrowserAccessibilityWin::NativeAddReference() {
   1153   AddRef();
   1154 }
   1155 
   1156 void BrowserAccessibilityWin::NativeReleaseReference() {
   1157   Release();
   1158 }
   1159 
   1160 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
   1161   AddRef();
   1162   return this;
   1163 }
   1164 
   1165 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
   1166     const VARIANT& var_id) {
   1167   if (var_id.vt != VT_I4)
   1168     return NULL;
   1169 
   1170   LONG child_id = var_id.lVal;
   1171   if (child_id == CHILDID_SELF)
   1172     return this;
   1173 
   1174   if (child_id >= 1 && child_id <= static_cast<LONG>(children_.size()))
   1175     return children_[child_id - 1]->toBrowserAccessibilityWin();
   1176 
   1177   return manager_->GetFromChildID(child_id)->toBrowserAccessibilityWin();
   1178 }
   1179 
   1180 HRESULT BrowserAccessibilityWin::GetAttributeAsBstr(
   1181     WebAccessibility::Attribute attribute, BSTR* value_bstr) {
   1182   string16 str;
   1183 
   1184   if (!GetAttribute(attribute, &str))
   1185     return S_FALSE;
   1186 
   1187   if (str.empty())
   1188     return S_FALSE;
   1189 
   1190   *value_bstr = SysAllocString(str.c_str());
   1191   DCHECK(*value_bstr);
   1192 
   1193   return S_OK;
   1194 }
   1195 
   1196 string16 BrowserAccessibilityWin::Escape(const string16& str) {
   1197   return EscapeQueryParamValueUTF8(str, false);
   1198 }
   1199 
   1200 const string16& BrowserAccessibilityWin::TextForIAccessibleText() {
   1201   if (role_ == WebAccessibility::ROLE_TEXT_FIELD) {
   1202     return value_;
   1203   } else {
   1204     return name_;
   1205   }
   1206 }
   1207 
   1208 LONG BrowserAccessibilityWin::FindBoundary(
   1209     const string16& text,
   1210     IA2TextBoundaryType boundary,
   1211     LONG start_offset,
   1212     LONG direction) {
   1213   LONG text_size = static_cast<LONG>(text.size());
   1214   DCHECK((start_offset >= 0 && start_offset <= text_size) ||
   1215          start_offset == IA2_TEXT_OFFSET_LENGTH ||
   1216          start_offset == IA2_TEXT_OFFSET_CARET);
   1217   DCHECK(direction == 1 || direction == -1);
   1218 
   1219   if (start_offset == IA2_TEXT_OFFSET_LENGTH) {
   1220     start_offset = text_size;
   1221   } else if (start_offset == IA2_TEXT_OFFSET_CARET) {
   1222     get_caretOffset(&start_offset);
   1223   }
   1224 
   1225   if (boundary == IA2_TEXT_BOUNDARY_CHAR) {
   1226     if (direction == 1 && start_offset < text_size)
   1227       return start_offset + 1;
   1228     else
   1229       return start_offset;
   1230   }
   1231 
   1232   LONG result = start_offset;
   1233   for (;;) {
   1234     LONG pos;
   1235     if (direction == 1) {
   1236       if (result >= text_size)
   1237         return text_size;
   1238       pos = result;
   1239     } else {
   1240       if (result <= 0)
   1241         return 0;
   1242       pos = result - 1;
   1243     }
   1244 
   1245     switch (boundary) {
   1246       case IA2_TEXT_BOUNDARY_WORD:
   1247         if (IsWhitespace(text[pos]))
   1248           return result;
   1249         break;
   1250       case IA2_TEXT_BOUNDARY_LINE:
   1251       case IA2_TEXT_BOUNDARY_PARAGRAPH:
   1252         if (text[pos] == '\n')
   1253           return result;
   1254       case IA2_TEXT_BOUNDARY_SENTENCE:
   1255         // Note that we don't actually have to implement sentence support;
   1256         // currently IAccessibleText functions return S_FALSE so that
   1257         // screenreaders will handle it on their own.
   1258         if ((text[pos] == '.' || text[pos] == '!' || text[pos] == '?') &&
   1259             (pos == text_size - 1 || IsWhitespace(text[pos + 1]))) {
   1260           return result;
   1261         }
   1262       case IA2_TEXT_BOUNDARY_ALL:
   1263       default:
   1264         break;
   1265     }
   1266 
   1267     if (direction > 0) {
   1268       result++;
   1269     } else if (direction < 0) {
   1270       result--;
   1271     } else {
   1272       NOTREACHED();
   1273       return result;
   1274     }
   1275   }
   1276 }
   1277 
   1278 void BrowserAccessibilityWin::InitRoleAndState() {
   1279   ia_state_ = 0;
   1280   ia2_state_ = IA2_STATE_OPAQUE;
   1281 
   1282   if ((state_ >> WebAccessibility::STATE_CHECKED) & 1)
   1283     ia_state_ |= STATE_SYSTEM_CHECKED;
   1284   if ((state_ >> WebAccessibility::STATE_COLLAPSED) & 1)
   1285     ia_state_|= STATE_SYSTEM_COLLAPSED;
   1286   if ((state_ >> WebAccessibility::STATE_EXPANDED) & 1)
   1287     ia_state_|= STATE_SYSTEM_EXPANDED;
   1288   if ((state_ >> WebAccessibility::STATE_FOCUSABLE) & 1)
   1289     ia_state_|= STATE_SYSTEM_FOCUSABLE;
   1290   if ((state_ >> WebAccessibility::STATE_HASPOPUP) & 1)
   1291     ia_state_|= STATE_SYSTEM_HASPOPUP;
   1292   if ((state_ >> WebAccessibility::STATE_HOTTRACKED) & 1)
   1293     ia_state_|= STATE_SYSTEM_HOTTRACKED;
   1294   if ((state_ >> WebAccessibility::STATE_INDETERMINATE) & 1)
   1295     ia_state_|= STATE_SYSTEM_INDETERMINATE;
   1296   if ((state_ >> WebAccessibility::STATE_INVISIBLE) & 1)
   1297     ia_state_|= STATE_SYSTEM_INVISIBLE;
   1298   if ((state_ >> WebAccessibility::STATE_LINKED) & 1)
   1299     ia_state_|= STATE_SYSTEM_LINKED;
   1300   if ((state_ >> WebAccessibility::STATE_MULTISELECTABLE) & 1)
   1301     ia_state_|= STATE_SYSTEM_MULTISELECTABLE;
   1302   // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
   1303   if ((state_ >> WebAccessibility::STATE_OFFSCREEN) & 1)
   1304     ia_state_|= STATE_SYSTEM_OFFSCREEN;
   1305   if ((state_ >> WebAccessibility::STATE_PRESSED) & 1)
   1306     ia_state_|= STATE_SYSTEM_PRESSED;
   1307   if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1)
   1308     ia_state_|= STATE_SYSTEM_PROTECTED;
   1309   if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1)
   1310     ia_state_|= STATE_SYSTEM_SELECTABLE;
   1311   if ((state_ >> WebAccessibility::STATE_SELECTED) & 1)
   1312     ia_state_|= STATE_SYSTEM_SELECTED;
   1313   if ((state_ >> WebAccessibility::STATE_READONLY) & 1)
   1314     ia_state_|= STATE_SYSTEM_READONLY;
   1315   if ((state_ >> WebAccessibility::STATE_TRAVERSED) & 1)
   1316     ia_state_|= STATE_SYSTEM_TRAVERSED;
   1317   if ((state_ >> WebAccessibility::STATE_BUSY) & 1)
   1318     ia_state_|= STATE_SYSTEM_BUSY;
   1319   if ((state_ >> WebAccessibility::STATE_UNAVAILABLE) & 1)
   1320     ia_state_|= STATE_SYSTEM_UNAVAILABLE;
   1321 
   1322   string16 html_tag;
   1323   GetAttribute(WebAccessibility::ATTR_HTML_TAG, &html_tag);
   1324   ia_role_ = 0;
   1325   ia2_role_ = 0;
   1326   switch (role_) {
   1327     case WebAccessibility::ROLE_ALERT:
   1328     case WebAccessibility::ROLE_ALERT_DIALOG:
   1329       ia_role_ = ROLE_SYSTEM_ALERT;
   1330       break;
   1331     case WebAccessibility::ROLE_APPLICATION:
   1332       ia_role_ = ROLE_SYSTEM_APPLICATION;
   1333       break;
   1334     case WebAccessibility::ROLE_ARTICLE:
   1335       ia_role_ = ROLE_SYSTEM_GROUPING;
   1336       ia2_role_ = IA2_ROLE_SECTION;
   1337       break;
   1338     case WebAccessibility::ROLE_BUSY_INDICATOR:
   1339       ia_role_ = ROLE_SYSTEM_ANIMATION;
   1340       break;
   1341     case WebAccessibility::ROLE_BUTTON:
   1342       ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
   1343       break;
   1344     case WebAccessibility::ROLE_CELL:
   1345       ia_role_ = ROLE_SYSTEM_CELL;
   1346       break;
   1347     case WebAccessibility::ROLE_CHECKBOX:
   1348       ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
   1349       break;
   1350     case WebAccessibility::ROLE_COLOR_WELL:
   1351       ia_role_ = ROLE_SYSTEM_CLIENT;
   1352       ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
   1353       break;
   1354     case WebAccessibility::ROLE_COLUMN:
   1355       ia_role_ = ROLE_SYSTEM_COLUMN;
   1356       break;
   1357     case WebAccessibility::ROLE_COLUMN_HEADER:
   1358       ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
   1359       break;
   1360     case WebAccessibility::ROLE_COMBO_BOX:
   1361       ia_role_ = ROLE_SYSTEM_COMBOBOX;
   1362       break;
   1363     case WebAccessibility::ROLE_DEFINITION_LIST_DEFINITION:
   1364       role_name_ = html_tag;
   1365       ia2_role_ = IA2_ROLE_PARAGRAPH;
   1366       break;
   1367     case WebAccessibility::ROLE_DEFINITION_LIST_TERM:
   1368       ia_role_ = ROLE_SYSTEM_LISTITEM;
   1369       break;
   1370     case WebAccessibility::ROLE_DIALOG:
   1371       ia_role_ = ROLE_SYSTEM_DIALOG;
   1372       break;
   1373     case WebAccessibility::ROLE_DISCLOSURE_TRIANGLE:
   1374       ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
   1375       break;
   1376     case WebAccessibility::ROLE_DOCUMENT:
   1377     case WebAccessibility::ROLE_WEB_AREA:
   1378       ia_role_ = ROLE_SYSTEM_DOCUMENT;
   1379       ia_state_|= STATE_SYSTEM_READONLY;
   1380       ia_state_|= STATE_SYSTEM_FOCUSABLE;
   1381       break;
   1382     case WebAccessibility::ROLE_EDITABLE_TEXT:
   1383       ia_role_ = ROLE_SYSTEM_TEXT;
   1384       ia2_state_ |= IA2_STATE_SINGLE_LINE;
   1385       ia2_state_ |= IA2_STATE_EDITABLE;
   1386       break;
   1387     case WebAccessibility::ROLE_GRID:
   1388       ia_role_ = ROLE_SYSTEM_TABLE;
   1389       break;
   1390     case WebAccessibility::ROLE_GROUP:
   1391       if (html_tag == L"li") {
   1392         ia_role_ = ROLE_SYSTEM_LISTITEM;
   1393       } else {
   1394         if (html_tag.empty())
   1395           role_name_ = L"div";
   1396         else
   1397           role_name_ = html_tag;
   1398         ia2_role_ = IA2_ROLE_SECTION;
   1399       }
   1400       break;
   1401     case WebAccessibility::ROLE_GROW_AREA:
   1402       ia_role_ = ROLE_SYSTEM_GRIP;
   1403       break;
   1404     case WebAccessibility::ROLE_HEADING:
   1405       role_name_ = html_tag;
   1406       ia2_role_ = IA2_ROLE_HEADING;
   1407       break;
   1408     case WebAccessibility::ROLE_IMAGE:
   1409       ia_role_ = ROLE_SYSTEM_GRAPHIC;
   1410       break;
   1411     case WebAccessibility::ROLE_IMAGE_MAP:
   1412       role_name_ = html_tag;
   1413       ia2_role_ = IA2_ROLE_IMAGE_MAP;
   1414       break;
   1415     case WebAccessibility::ROLE_IMAGE_MAP_LINK:
   1416       ia_role_ = ROLE_SYSTEM_LINK;
   1417       ia_state_|= STATE_SYSTEM_LINKED;
   1418       break;
   1419     case WebAccessibility::ROLE_LANDMARK_APPLICATION:
   1420     case WebAccessibility::ROLE_LANDMARK_BANNER:
   1421     case WebAccessibility::ROLE_LANDMARK_COMPLEMENTARY:
   1422     case WebAccessibility::ROLE_LANDMARK_CONTENTINFO:
   1423     case WebAccessibility::ROLE_LANDMARK_MAIN:
   1424     case WebAccessibility::ROLE_LANDMARK_NAVIGATION:
   1425     case WebAccessibility::ROLE_LANDMARK_SEARCH:
   1426       ia_role_ = ROLE_SYSTEM_GROUPING;
   1427       ia2_role_ = IA2_ROLE_SECTION;
   1428       break;
   1429     case WebAccessibility::ROLE_LINK:
   1430     case WebAccessibility::ROLE_WEBCORE_LINK:
   1431       ia_role_ = ROLE_SYSTEM_LINK;
   1432       ia_state_|= STATE_SYSTEM_LINKED;
   1433       break;
   1434     case WebAccessibility::ROLE_LIST:
   1435       ia_role_ = ROLE_SYSTEM_LIST;
   1436       break;
   1437     case WebAccessibility::ROLE_LISTBOX:
   1438       ia_role_ = ROLE_SYSTEM_LIST;
   1439       break;
   1440     case WebAccessibility::ROLE_LISTBOX_OPTION:
   1441     case WebAccessibility::ROLE_LIST_ITEM:
   1442     case WebAccessibility::ROLE_LIST_MARKER:
   1443       ia_role_ = ROLE_SYSTEM_LISTITEM;
   1444       break;
   1445     case WebAccessibility::ROLE_MATH:
   1446       ia_role_ = ROLE_SYSTEM_EQUATION;
   1447       break;
   1448     case WebAccessibility::ROLE_MENU:
   1449     case WebAccessibility::ROLE_MENU_BUTTON:
   1450       ia_role_ = ROLE_SYSTEM_MENUPOPUP;
   1451       break;
   1452     case WebAccessibility::ROLE_MENU_BAR:
   1453       ia_role_ = ROLE_SYSTEM_MENUBAR;
   1454       break;
   1455     case WebAccessibility::ROLE_MENU_ITEM:
   1456     case WebAccessibility::ROLE_MENU_LIST_OPTION:
   1457       ia_role_ = ROLE_SYSTEM_MENUITEM;
   1458       break;
   1459     case WebAccessibility::ROLE_MENU_LIST_POPUP:
   1460       ia_role_ = ROLE_SYSTEM_MENUPOPUP;
   1461       break;
   1462     case WebAccessibility::ROLE_NOTE:
   1463       ia_role_ = ROLE_SYSTEM_GROUPING;
   1464       ia2_role_ = IA2_ROLE_NOTE;
   1465       break;
   1466     case WebAccessibility::ROLE_OUTLINE:
   1467       ia_role_ = ROLE_SYSTEM_OUTLINE;
   1468       break;
   1469     case WebAccessibility::ROLE_POPUP_BUTTON:
   1470       ia_role_ = ROLE_SYSTEM_COMBOBOX;
   1471       break;
   1472     case WebAccessibility::ROLE_PROGRESS_INDICATOR:
   1473       ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
   1474       break;
   1475     case WebAccessibility::ROLE_RADIO_BUTTON:
   1476       ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
   1477       break;
   1478     case WebAccessibility::ROLE_RADIO_GROUP:
   1479       ia_role_ = ROLE_SYSTEM_GROUPING;
   1480       ia2_role_ = IA2_ROLE_SECTION;
   1481       break;
   1482     case WebAccessibility::ROLE_REGION:
   1483       ia_role_ = ROLE_SYSTEM_GROUPING;
   1484       ia2_role_ = IA2_ROLE_SECTION;
   1485       break;
   1486     case WebAccessibility::ROLE_ROW:
   1487       ia_role_ = ROLE_SYSTEM_ROW;
   1488       break;
   1489     case WebAccessibility::ROLE_ROW_HEADER:
   1490       ia_role_ = ROLE_SYSTEM_ROWHEADER;
   1491       break;
   1492     case WebAccessibility::ROLE_RULER:
   1493       ia_role_ = ROLE_SYSTEM_CLIENT;
   1494       ia2_role_ = IA2_ROLE_RULER;
   1495       break;
   1496     case WebAccessibility::ROLE_SCROLLAREA:
   1497       ia_role_ = ROLE_SYSTEM_CLIENT;
   1498       ia2_role_ = IA2_ROLE_SCROLL_PANE;
   1499       break;
   1500     case WebAccessibility::ROLE_SCROLLBAR:
   1501       ia_role_ = ROLE_SYSTEM_SCROLLBAR;
   1502       break;
   1503     case WebAccessibility::ROLE_SLIDER:
   1504       ia_role_ = ROLE_SYSTEM_SLIDER;
   1505       break;
   1506     case WebAccessibility::ROLE_SPLIT_GROUP:
   1507       ia_role_ = ROLE_SYSTEM_CLIENT;
   1508       ia2_role_ = IA2_ROLE_SPLIT_PANE;
   1509       break;
   1510     case WebAccessibility::ROLE_ANNOTATION:
   1511     case WebAccessibility::ROLE_STATIC_TEXT:
   1512       ia_role_ = ROLE_SYSTEM_TEXT;
   1513       break;
   1514     case WebAccessibility::ROLE_STATUS:
   1515       ia_role_ = ROLE_SYSTEM_STATUSBAR;
   1516       break;
   1517     case WebAccessibility::ROLE_SPLITTER:
   1518       ia_role_ = ROLE_SYSTEM_SEPARATOR;
   1519       break;
   1520     case WebAccessibility::ROLE_TAB:
   1521       ia_role_ = ROLE_SYSTEM_PAGETAB;
   1522       break;
   1523     case WebAccessibility::ROLE_TABLE:
   1524       ia_role_ = ROLE_SYSTEM_TABLE;
   1525       break;
   1526     case WebAccessibility::ROLE_TABLE_HEADER_CONTAINER:
   1527       ia_role_ = ROLE_SYSTEM_GROUPING;
   1528       ia2_role_ = IA2_ROLE_SECTION;
   1529       break;
   1530     case WebAccessibility::ROLE_TAB_GROUP:
   1531     case WebAccessibility::ROLE_TAB_LIST:
   1532     case WebAccessibility::ROLE_TAB_PANEL:
   1533       ia_role_ = ROLE_SYSTEM_PAGETABLIST;
   1534       break;
   1535     case WebAccessibility::ROLE_TEXTAREA:
   1536       ia_role_ = ROLE_SYSTEM_TEXT;
   1537       ia2_state_ |= IA2_STATE_MULTI_LINE;
   1538       ia2_state_ |= IA2_STATE_EDITABLE;
   1539       break;
   1540     case WebAccessibility::ROLE_TEXT_FIELD:
   1541       ia_role_ = ROLE_SYSTEM_TEXT;
   1542       ia2_state_ |= IA2_STATE_SINGLE_LINE;
   1543       ia2_state_ |= IA2_STATE_EDITABLE;
   1544       break;
   1545     case WebAccessibility::ROLE_TIMER:
   1546       ia_role_ = ROLE_SYSTEM_CLOCK;
   1547       break;
   1548     case WebAccessibility::ROLE_TOOLBAR:
   1549       ia_role_ = ROLE_SYSTEM_TOOLBAR;
   1550       break;
   1551     case WebAccessibility::ROLE_TOOLTIP:
   1552       ia_role_ = ROLE_SYSTEM_TOOLTIP;
   1553       break;
   1554     case WebAccessibility::ROLE_TREE:
   1555       ia_role_ = ROLE_SYSTEM_OUTLINE;
   1556       break;
   1557     case WebAccessibility::ROLE_TREE_GRID:
   1558       ia_role_ = ROLE_SYSTEM_OUTLINE;
   1559       break;
   1560     case WebAccessibility::ROLE_TREE_ITEM:
   1561       ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
   1562       break;
   1563     case WebAccessibility::ROLE_WINDOW:
   1564       ia_role_ = ROLE_SYSTEM_WINDOW;
   1565       break;
   1566 
   1567     // TODO(dmazzoni): figure out the proper MSAA role for all of these.
   1568     case WebAccessibility::ROLE_BROWSER:
   1569     case WebAccessibility::ROLE_DIRECTORY:
   1570     case WebAccessibility::ROLE_DRAWER:
   1571     case WebAccessibility::ROLE_HELP_TAG:
   1572     case WebAccessibility::ROLE_IGNORED:
   1573     case WebAccessibility::ROLE_INCREMENTOR:
   1574     case WebAccessibility::ROLE_LOG:
   1575     case WebAccessibility::ROLE_MARQUEE:
   1576     case WebAccessibility::ROLE_MATTE:
   1577     case WebAccessibility::ROLE_RULER_MARKER:
   1578     case WebAccessibility::ROLE_SHEET:
   1579     case WebAccessibility::ROLE_SLIDER_THUMB:
   1580     case WebAccessibility::ROLE_SYSTEM_WIDE:
   1581     case WebAccessibility::ROLE_VALUE_INDICATOR:
   1582     default:
   1583       ia_role_ = ROLE_SYSTEM_CLIENT;
   1584       break;
   1585   }
   1586 
   1587   // The role should always be set.
   1588   DCHECK(!role_name_.empty() || ia_role_);
   1589 
   1590   // If we didn't explicitly set the IAccessible2 role, make it the same
   1591   // as the MSAA role.
   1592   if (!ia2_role_)
   1593     ia2_role_ = ia_role_;
   1594 }
   1595