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