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