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