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