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