1 // Copyright (c) 2012 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 "content/browser/accessibility/browser_accessibility_win.h"
6
7 #include <UIAutomationClient.h>
8 #include <UIAutomationCoreApi.h>
9
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/enum_variant.h"
15 #include "base/win/scoped_comptr.h"
16 #include "base/win/windows_version.h"
17 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
18 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
19 #include "content/common/accessibility_messages.h"
20 #include "content/public/common/content_client.h"
21 #include "ui/accessibility/ax_text_utils.h"
22 #include "ui/base/win/accessibility_ids_win.h"
23 #include "ui/base/win/accessibility_misc_utils.h"
24
25 namespace content {
26
27 // These nonstandard GUIDs are taken directly from the Mozilla sources
28 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
29 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
30 const GUID GUID_ISimpleDOM = {
31 0x0c539790, 0x12e4, 0x11cf,
32 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
33 const GUID GUID_IAccessibleContentDocument = {
34 0xa5d8e1f3, 0x3571, 0x4d8f,
35 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
36
37 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc";
38
39 // static
40 LONG BrowserAccessibilityWin::next_unique_id_win_ =
41 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
42
43 //
44 // BrowserAccessibilityRelation
45 //
46 // A simple implementation of IAccessibleRelation, used to represent
47 // a relationship between two accessible nodes in the tree.
48 //
49
50 class BrowserAccessibilityRelation
51 : public CComObjectRootEx<CComMultiThreadModel>,
52 public IAccessibleRelation {
53 BEGIN_COM_MAP(BrowserAccessibilityRelation)
COM_INTERFACE_ENTRY(IAccessibleRelation)54 COM_INTERFACE_ENTRY(IAccessibleRelation)
55 END_COM_MAP()
56
57 CONTENT_EXPORT BrowserAccessibilityRelation() {}
~BrowserAccessibilityRelation()58 CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {}
59
60 CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner,
61 const base::string16& type);
62 CONTENT_EXPORT void AddTarget(int target_id);
63
64 // IAccessibleRelation methods.
65 CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type);
66 CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets);
67 CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target);
68 CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets,
69 IUnknown** targets,
70 long* n_targets);
71
72 // IAccessibleRelation methods not implemented.
get_localizedRelationType(BSTR * relation_type)73 CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) {
74 return E_NOTIMPL;
75 }
76
77 private:
78 base::string16 type_;
79 base::win::ScopedComPtr<BrowserAccessibilityWin> owner_;
80 std::vector<int> target_ids_;
81 };
82
Initialize(BrowserAccessibilityWin * owner,const base::string16 & type)83 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner,
84 const base::string16& type) {
85 owner_ = owner;
86 type_ = type;
87 }
88
AddTarget(int target_id)89 void BrowserAccessibilityRelation::AddTarget(int target_id) {
90 target_ids_.push_back(target_id);
91 }
92
get_relationType(BSTR * relation_type)93 STDMETHODIMP BrowserAccessibilityRelation::get_relationType(
94 BSTR* relation_type) {
95 if (!relation_type)
96 return E_INVALIDARG;
97
98 if (!owner_->instance_active())
99 return E_FAIL;
100
101 *relation_type = SysAllocString(type_.c_str());
102 DCHECK(*relation_type);
103 return S_OK;
104 }
105
get_nTargets(long * n_targets)106 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
107 if (!n_targets)
108 return E_INVALIDARG;
109
110 if (!owner_->instance_active())
111 return E_FAIL;
112
113 *n_targets = static_cast<long>(target_ids_.size());
114
115 BrowserAccessibilityManager* manager = owner_->manager();
116 for (long i = *n_targets - 1; i >= 0; --i) {
117 BrowserAccessibility* result = manager->GetFromID(target_ids_[i]);
118 if (!result || !result->instance_active()) {
119 *n_targets = 0;
120 break;
121 }
122 }
123 return S_OK;
124 }
125
get_target(long target_index,IUnknown ** target)126 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
127 IUnknown** target) {
128 if (!target)
129 return E_INVALIDARG;
130
131 if (!owner_->instance_active())
132 return E_FAIL;
133
134 if (target_index < 0 ||
135 target_index >= static_cast<long>(target_ids_.size())) {
136 return E_INVALIDARG;
137 }
138
139 BrowserAccessibilityManager* manager = owner_->manager();
140 BrowserAccessibility* result =
141 manager->GetFromID(target_ids_[target_index]);
142 if (!result || !result->instance_active())
143 return E_FAIL;
144
145 *target = static_cast<IAccessible*>(
146 result->ToBrowserAccessibilityWin()->NewReference());
147 return S_OK;
148 }
149
get_targets(long max_targets,IUnknown ** targets,long * n_targets)150 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets,
151 IUnknown** targets,
152 long* n_targets) {
153 if (!targets || !n_targets)
154 return E_INVALIDARG;
155
156 if (!owner_->instance_active())
157 return E_FAIL;
158
159 long count = static_cast<long>(target_ids_.size());
160 if (count > max_targets)
161 count = max_targets;
162
163 *n_targets = count;
164 if (count == 0)
165 return S_FALSE;
166
167 for (long i = 0; i < count; ++i) {
168 HRESULT result = get_target(i, &targets[i]);
169 if (result != S_OK)
170 return result;
171 }
172
173 return S_OK;
174 }
175
176 //
177 // BrowserAccessibilityWin
178 //
179
180 // static
Create()181 BrowserAccessibility* BrowserAccessibility::Create() {
182 CComObject<BrowserAccessibilityWin>* instance;
183 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
184 DCHECK(SUCCEEDED(hr));
185 return instance->NewReference();
186 }
187
ToBrowserAccessibilityWin()188 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
189 return static_cast<BrowserAccessibilityWin*>(this);
190 }
191
BrowserAccessibilityWin()192 BrowserAccessibilityWin::BrowserAccessibilityWin()
193 : ia_role_(0),
194 ia_state_(0),
195 ia2_role_(0),
196 ia2_state_(0),
197 first_time_(true),
198 old_ia_state_(0),
199 previous_scroll_x_(0),
200 previous_scroll_y_(0) {
201 // Start unique IDs at -1 and decrement each time, because get_accChild
202 // uses positive IDs to enumerate children, so we use negative IDs to
203 // clearly distinguish between indices and unique IDs.
204 unique_id_win_ = next_unique_id_win_;
205 if (next_unique_id_win_ ==
206 base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
207 next_unique_id_win_ =
208 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
209 }
210 next_unique_id_win_--;
211 }
212
~BrowserAccessibilityWin()213 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
214 for (size_t i = 0; i < relations_.size(); ++i)
215 relations_[i]->Release();
216 }
217
218 //
219 // IAccessible methods.
220 //
221 // Conventions:
222 // * Always test for instance_active() first and return E_FAIL if it's false.
223 // * Always check for invalid arguments first, even if they're unused.
224 // * Return S_FALSE if the only output is a string argument and it's empty.
225 //
226
accDoDefaultAction(VARIANT var_id)227 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
228 if (!instance_active())
229 return E_FAIL;
230
231 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
232 if (!target)
233 return E_INVALIDARG;
234
235 manager()->DoDefaultAction(*target);
236 return S_OK;
237 }
238
accHitTest(LONG x_left,LONG y_top,VARIANT * child)239 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
240 LONG y_top,
241 VARIANT* child) {
242 if (!instance_active())
243 return E_FAIL;
244
245 if (!child)
246 return E_INVALIDARG;
247
248 gfx::Point point(x_left, y_top);
249 if (!GetGlobalBoundsRect().Contains(point)) {
250 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
251 child->vt = VT_EMPTY;
252 return S_FALSE;
253 }
254
255 BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
256 if (result == this) {
257 // Point is within this object.
258 child->vt = VT_I4;
259 child->lVal = CHILDID_SELF;
260 } else {
261 child->vt = VT_DISPATCH;
262 child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
263 }
264 return S_OK;
265 }
266
accLocation(LONG * x_left,LONG * y_top,LONG * width,LONG * height,VARIANT var_id)267 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
268 LONG* y_top,
269 LONG* width,
270 LONG* height,
271 VARIANT var_id) {
272 if (!instance_active())
273 return E_FAIL;
274
275 if (!x_left || !y_top || !width || !height)
276 return E_INVALIDARG;
277
278 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
279 if (!target)
280 return E_INVALIDARG;
281
282 gfx::Rect bounds = target->GetGlobalBoundsRect();
283 *x_left = bounds.x();
284 *y_top = bounds.y();
285 *width = bounds.width();
286 *height = bounds.height();
287
288 return S_OK;
289 }
290
accNavigate(LONG nav_dir,VARIANT start,VARIANT * end)291 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
292 VARIANT start,
293 VARIANT* end) {
294 BrowserAccessibilityWin* target = GetTargetFromChildID(start);
295 if (!target)
296 return E_INVALIDARG;
297
298 if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
299 start.lVal != CHILDID_SELF) {
300 // MSAA states that navigating to first/last child can only be from self.
301 return E_INVALIDARG;
302 }
303
304 uint32 child_count = target->PlatformChildCount();
305
306 BrowserAccessibility* result = NULL;
307 switch (nav_dir) {
308 case NAVDIR_DOWN:
309 case NAVDIR_UP:
310 case NAVDIR_LEFT:
311 case NAVDIR_RIGHT:
312 // These directions are not implemented, matching Mozilla and IE.
313 return E_NOTIMPL;
314 case NAVDIR_FIRSTCHILD:
315 if (child_count > 0)
316 result = target->PlatformGetChild(0);
317 break;
318 case NAVDIR_LASTCHILD:
319 if (child_count > 0)
320 result = target->PlatformGetChild(child_count - 1);
321 break;
322 case NAVDIR_NEXT:
323 result = target->GetNextSibling();
324 break;
325 case NAVDIR_PREVIOUS:
326 result = target->GetPreviousSibling();
327 break;
328 }
329
330 if (!result) {
331 end->vt = VT_EMPTY;
332 return S_FALSE;
333 }
334
335 end->vt = VT_DISPATCH;
336 end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
337 return S_OK;
338 }
339
get_accChild(VARIANT var_child,IDispatch ** disp_child)340 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
341 IDispatch** disp_child) {
342 if (!instance_active())
343 return E_FAIL;
344
345 if (!disp_child)
346 return E_INVALIDARG;
347
348 *disp_child = NULL;
349
350 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
351 if (!target)
352 return E_INVALIDARG;
353
354 (*disp_child) = target->NewReference();
355 return S_OK;
356 }
357
get_accChildCount(LONG * child_count)358 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
359 if (!instance_active())
360 return E_FAIL;
361
362 if (!child_count)
363 return E_INVALIDARG;
364
365 *child_count = PlatformChildCount();
366
367 return S_OK;
368 }
369
get_accDefaultAction(VARIANT var_id,BSTR * def_action)370 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
371 BSTR* def_action) {
372 if (!instance_active())
373 return E_FAIL;
374
375 if (!def_action)
376 return E_INVALIDARG;
377
378 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
379 if (!target)
380 return E_INVALIDARG;
381
382 return target->GetStringAttributeAsBstr(
383 ui::AX_ATTR_SHORTCUT, def_action);
384 }
385
get_accDescription(VARIANT var_id,BSTR * desc)386 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
387 BSTR* desc) {
388 if (!instance_active())
389 return E_FAIL;
390
391 if (!desc)
392 return E_INVALIDARG;
393
394 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
395 if (!target)
396 return E_INVALIDARG;
397
398 return target->GetStringAttributeAsBstr(
399 ui::AX_ATTR_DESCRIPTION, desc);
400 }
401
get_accFocus(VARIANT * focus_child)402 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
403 if (!instance_active())
404 return E_FAIL;
405
406 if (!focus_child)
407 return E_INVALIDARG;
408
409 BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
410 manager()->GetFocus(this));
411 if (focus == this) {
412 focus_child->vt = VT_I4;
413 focus_child->lVal = CHILDID_SELF;
414 } else if (focus == NULL) {
415 focus_child->vt = VT_EMPTY;
416 } else {
417 focus_child->vt = VT_DISPATCH;
418 focus_child->pdispVal = focus->NewReference();
419 }
420
421 return S_OK;
422 }
423
get_accHelp(VARIANT var_id,BSTR * help)424 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
425 if (!instance_active())
426 return E_FAIL;
427
428 if (!help)
429 return E_INVALIDARG;
430
431 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
432 if (!target)
433 return E_INVALIDARG;
434
435 return target->GetStringAttributeAsBstr(
436 ui::AX_ATTR_HELP, help);
437 }
438
get_accKeyboardShortcut(VARIANT var_id,BSTR * acc_key)439 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
440 BSTR* acc_key) {
441 if (!instance_active())
442 return E_FAIL;
443
444 if (!acc_key)
445 return E_INVALIDARG;
446
447 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
448 if (!target)
449 return E_INVALIDARG;
450
451 return target->GetStringAttributeAsBstr(
452 ui::AX_ATTR_SHORTCUT, acc_key);
453 }
454
get_accName(VARIANT var_id,BSTR * name)455 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
456 if (!instance_active())
457 return E_FAIL;
458
459 if (!name)
460 return E_INVALIDARG;
461
462 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
463 if (!target)
464 return E_INVALIDARG;
465
466 std::string name_str = target->name();
467
468 // If the name is empty, see if it's labeled by another element.
469 if (name_str.empty()) {
470 int title_elem_id;
471 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
472 &title_elem_id)) {
473 BrowserAccessibility* title_elem =
474 manager()->GetFromID(title_elem_id);
475 if (title_elem)
476 name_str = title_elem->GetTextRecursive();
477 }
478 }
479
480 if (name_str.empty())
481 return S_FALSE;
482
483 *name = SysAllocString(base::UTF8ToUTF16(name_str).c_str());
484
485 DCHECK(*name);
486 return S_OK;
487 }
488
get_accParent(IDispatch ** disp_parent)489 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
490 if (!instance_active())
491 return E_FAIL;
492
493 if (!disp_parent)
494 return E_INVALIDARG;
495
496 IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
497 if (parent_obj == NULL) {
498 // This happens if we're the root of the tree;
499 // return the IAccessible for the window.
500 parent_obj =
501 manager()->ToBrowserAccessibilityManagerWin()->parent_iaccessible();
502 // |parent| can only be NULL if the manager was created before the parent
503 // IAccessible was known and it wasn't subsequently set before a client
504 // requested it. This has been fixed. |parent| may also be NULL during
505 // destruction. Possible cases where this could occur include tabs being
506 // dragged to a new window, etc.
507 if (!parent_obj) {
508 DVLOG(1) << "In Function: "
509 << __FUNCTION__
510 << ". Parent IAccessible interface is NULL. Returning failure";
511 return E_FAIL;
512 }
513 }
514 parent_obj->AddRef();
515 *disp_parent = parent_obj;
516 return S_OK;
517 }
518
get_accRole(VARIANT var_id,VARIANT * role)519 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
520 VARIANT* role) {
521 if (!instance_active())
522 return E_FAIL;
523
524 if (!role)
525 return E_INVALIDARG;
526
527 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
528 if (!target)
529 return E_INVALIDARG;
530
531 if (!target->role_name_.empty()) {
532 role->vt = VT_BSTR;
533 role->bstrVal = SysAllocString(target->role_name_.c_str());
534 } else {
535 role->vt = VT_I4;
536 role->lVal = target->ia_role_;
537 }
538 return S_OK;
539 }
540
get_accState(VARIANT var_id,VARIANT * state)541 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
542 VARIANT* state) {
543 if (!instance_active())
544 return E_FAIL;
545
546 if (!state)
547 return E_INVALIDARG;
548
549 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
550 if (!target)
551 return E_INVALIDARG;
552
553 state->vt = VT_I4;
554 state->lVal = target->ia_state_;
555 if (manager()->GetFocus(NULL) == this)
556 state->lVal |= STATE_SYSTEM_FOCUSED;
557
558 return S_OK;
559 }
560
get_accValue(VARIANT var_id,BSTR * value)561 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
562 BSTR* value) {
563 if (!instance_active())
564 return E_FAIL;
565
566 if (!value)
567 return E_INVALIDARG;
568
569 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
570 if (!target)
571 return E_INVALIDARG;
572
573 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
574 target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
575 target->ia_role() == ROLE_SYSTEM_SLIDER) {
576 base::string16 value_text = target->GetValueText();
577 *value = SysAllocString(value_text.c_str());
578 DCHECK(*value);
579 return S_OK;
580 }
581
582 // Expose color well value.
583 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
584 int r = target->GetIntAttribute(
585 ui::AX_ATTR_COLOR_VALUE_RED);
586 int g = target->GetIntAttribute(
587 ui::AX_ATTR_COLOR_VALUE_GREEN);
588 int b = target->GetIntAttribute(
589 ui::AX_ATTR_COLOR_VALUE_BLUE);
590 base::string16 value_text;
591 value_text = base::IntToString16((r * 100) / 255) + L"% red " +
592 base::IntToString16((g * 100) / 255) + L"% green " +
593 base::IntToString16((b * 100) / 255) + L"% blue";
594 *value = SysAllocString(value_text.c_str());
595 DCHECK(*value);
596 return S_OK;
597 }
598
599 *value = SysAllocString(base::UTF8ToUTF16(target->value()).c_str());
600 DCHECK(*value);
601 return S_OK;
602 }
603
get_accHelpTopic(BSTR * help_file,VARIANT var_id,LONG * topic_id)604 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
605 VARIANT var_id,
606 LONG* topic_id) {
607 return E_NOTIMPL;
608 }
609
get_accSelection(VARIANT * selected)610 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
611 if (!instance_active())
612 return E_FAIL;
613
614 if (GetRole() != ui::AX_ROLE_LIST_BOX)
615 return E_NOTIMPL;
616
617 unsigned long selected_count = 0;
618 for (size_t i = 0; i < InternalChildCount(); ++i) {
619 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
620 ++selected_count;
621 }
622
623 if (selected_count == 0) {
624 selected->vt = VT_EMPTY;
625 return S_OK;
626 }
627
628 if (selected_count == 1) {
629 for (size_t i = 0; i < InternalChildCount(); ++i) {
630 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
631 selected->vt = VT_DISPATCH;
632 selected->pdispVal =
633 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
634 return S_OK;
635 }
636 }
637 }
638
639 // Multiple items are selected.
640 base::win::EnumVariant* enum_variant =
641 new base::win::EnumVariant(selected_count);
642 enum_variant->AddRef();
643 unsigned long index = 0;
644 for (size_t i = 0; i < InternalChildCount(); ++i) {
645 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
646 enum_variant->ItemAt(index)->vt = VT_DISPATCH;
647 enum_variant->ItemAt(index)->pdispVal =
648 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
649 ++index;
650 }
651 }
652 selected->vt = VT_UNKNOWN;
653 selected->punkVal = static_cast<IUnknown*>(
654 static_cast<base::win::IUnknownImpl*>(enum_variant));
655 return S_OK;
656 }
657
accSelect(LONG flags_sel,VARIANT var_id)658 STDMETHODIMP BrowserAccessibilityWin::accSelect(
659 LONG flags_sel, VARIANT var_id) {
660 if (!instance_active())
661 return E_FAIL;
662
663 if (flags_sel & SELFLAG_TAKEFOCUS) {
664 manager()->SetFocus(this, true);
665 return S_OK;
666 }
667
668 return S_FALSE;
669 }
670
671 //
672 // IAccessible2 methods.
673 //
674
role(LONG * role)675 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
676 if (!instance_active())
677 return E_FAIL;
678
679 if (!role)
680 return E_INVALIDARG;
681
682 *role = ia2_role_;
683
684 return S_OK;
685 }
686
get_attributes(BSTR * attributes)687 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
688 if (!instance_active())
689 return E_FAIL;
690
691 if (!attributes)
692 return E_INVALIDARG;
693
694 // The iaccessible2 attributes are a set of key-value pairs
695 // separated by semicolons, with a colon between the key and the value.
696 base::string16 str;
697 for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) {
698 if (i != 0)
699 str += L';';
700 str += ia2_attributes_[i];
701 }
702
703 if (str.empty())
704 return S_FALSE;
705
706 *attributes = SysAllocString(str.c_str());
707 DCHECK(*attributes);
708 return S_OK;
709 }
710
get_states(AccessibleStates * states)711 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
712 if (!instance_active())
713 return E_FAIL;
714
715 if (!states)
716 return E_INVALIDARG;
717
718 *states = ia2_state_;
719
720 return S_OK;
721 }
722
get_uniqueID(LONG * unique_id)723 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
724 if (!instance_active())
725 return E_FAIL;
726
727 if (!unique_id)
728 return E_INVALIDARG;
729
730 *unique_id = unique_id_win_;
731 return S_OK;
732 }
733
get_windowHandle(HWND * window_handle)734 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
735 if (!instance_active())
736 return E_FAIL;
737
738 if (!window_handle)
739 return E_INVALIDARG;
740
741 *window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
742 if (!*window_handle)
743 return E_FAIL;
744
745 return S_OK;
746 }
747
get_indexInParent(LONG * index_in_parent)748 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
749 if (!instance_active())
750 return E_FAIL;
751
752 if (!index_in_parent)
753 return E_INVALIDARG;
754
755 *index_in_parent = this->GetIndexInParent();
756 return S_OK;
757 }
758
get_nRelations(LONG * n_relations)759 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
760 if (!instance_active())
761 return E_FAIL;
762
763 if (!n_relations)
764 return E_INVALIDARG;
765
766 *n_relations = relations_.size();
767 return S_OK;
768 }
769
get_relation(LONG relation_index,IAccessibleRelation ** relation)770 STDMETHODIMP BrowserAccessibilityWin::get_relation(
771 LONG relation_index,
772 IAccessibleRelation** relation) {
773 if (!instance_active())
774 return E_FAIL;
775
776 if (relation_index < 0 ||
777 relation_index >= static_cast<long>(relations_.size())) {
778 return E_INVALIDARG;
779 }
780
781 if (!relation)
782 return E_INVALIDARG;
783
784 relations_[relation_index]->AddRef();
785 *relation = relations_[relation_index];
786 return S_OK;
787 }
788
get_relations(LONG max_relations,IAccessibleRelation ** relations,LONG * n_relations)789 STDMETHODIMP BrowserAccessibilityWin::get_relations(
790 LONG max_relations,
791 IAccessibleRelation** relations,
792 LONG* n_relations) {
793 if (!instance_active())
794 return E_FAIL;
795
796 if (!relations || !n_relations)
797 return E_INVALIDARG;
798
799 long count = static_cast<long>(relations_.size());
800 *n_relations = count;
801 if (count == 0)
802 return S_FALSE;
803
804 for (long i = 0; i < count; ++i) {
805 relations_[i]->AddRef();
806 relations[i] = relations_[i];
807 }
808
809 return S_OK;
810 }
811
scrollTo(enum IA2ScrollType scroll_type)812 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
813 if (!instance_active())
814 return E_FAIL;
815
816 gfx::Rect r = GetLocation();
817 switch(scroll_type) {
818 case IA2_SCROLL_TYPE_TOP_LEFT:
819 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
820 break;
821 case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
822 manager()->ScrollToMakeVisible(
823 *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
824 break;
825 case IA2_SCROLL_TYPE_TOP_EDGE:
826 manager()->ScrollToMakeVisible(
827 *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
828 break;
829 case IA2_SCROLL_TYPE_BOTTOM_EDGE:
830 manager()->ScrollToMakeVisible(
831 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
832 break;
833 case IA2_SCROLL_TYPE_LEFT_EDGE:
834 manager()->ScrollToMakeVisible(
835 *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
836 break;
837 case IA2_SCROLL_TYPE_RIGHT_EDGE:
838 manager()->ScrollToMakeVisible(
839 *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
840 break;
841 case IA2_SCROLL_TYPE_ANYWHERE:
842 default:
843 manager()->ScrollToMakeVisible(*this, r);
844 break;
845 }
846
847 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
848
849 return S_OK;
850 }
851
scrollToPoint(enum IA2CoordinateType coordinate_type,LONG x,LONG y)852 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
853 enum IA2CoordinateType coordinate_type,
854 LONG x,
855 LONG y) {
856 if (!instance_active())
857 return E_FAIL;
858
859 gfx::Point scroll_to(x, y);
860
861 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
862 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
863 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
864 if (GetParent())
865 scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
866 } else {
867 return E_INVALIDARG;
868 }
869
870 manager()->ScrollToPoint(*this, scroll_to);
871 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
872
873 return S_OK;
874 }
875
get_groupPosition(LONG * group_level,LONG * similar_items_in_group,LONG * position_in_group)876 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
877 LONG* group_level,
878 LONG* similar_items_in_group,
879 LONG* position_in_group) {
880 if (!instance_active())
881 return E_FAIL;
882
883 if (!group_level || !similar_items_in_group || !position_in_group)
884 return E_INVALIDARG;
885
886 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
887 GetParent() &&
888 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
889 *group_level = 0;
890 *similar_items_in_group = GetParent()->PlatformChildCount();
891 *position_in_group = GetIndexInParent() + 1;
892 return S_OK;
893 }
894
895 return E_NOTIMPL;
896 }
897
898 //
899 // IAccessibleApplication methods.
900 //
901
get_appName(BSTR * app_name)902 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
903 // No need to check |instance_active()| because this interface is
904 // global, and doesn't depend on any local state.
905
906 if (!app_name)
907 return E_INVALIDARG;
908
909 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
910 // the part before the "/".
911 std::vector<std::string> product_components;
912 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
913 DCHECK_EQ(2U, product_components.size());
914 if (product_components.size() != 2)
915 return E_FAIL;
916 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
917 DCHECK(*app_name);
918 return *app_name ? S_OK : E_FAIL;
919 }
920
get_appVersion(BSTR * app_version)921 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
922 // No need to check |instance_active()| because this interface is
923 // global, and doesn't depend on any local state.
924
925 if (!app_version)
926 return E_INVALIDARG;
927
928 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
929 // the part after the "/".
930 std::vector<std::string> product_components;
931 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
932 DCHECK_EQ(2U, product_components.size());
933 if (product_components.size() != 2)
934 return E_FAIL;
935 *app_version =
936 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
937 DCHECK(*app_version);
938 return *app_version ? S_OK : E_FAIL;
939 }
940
get_toolkitName(BSTR * toolkit_name)941 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
942 // No need to check |instance_active()| because this interface is
943 // global, and doesn't depend on any local state.
944
945 if (!toolkit_name)
946 return E_INVALIDARG;
947
948 // This is hard-coded; all products based on the Chromium engine
949 // will have the same toolkit name, so that assistive technology can
950 // detect any Chrome-based product.
951 *toolkit_name = SysAllocString(L"Chrome");
952 DCHECK(*toolkit_name);
953 return *toolkit_name ? S_OK : E_FAIL;
954 }
955
get_toolkitVersion(BSTR * toolkit_version)956 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
957 BSTR* toolkit_version) {
958 // No need to check |instance_active()| because this interface is
959 // global, and doesn't depend on any local state.
960
961 if (!toolkit_version)
962 return E_INVALIDARG;
963
964 std::string user_agent = GetContentClient()->GetUserAgent();
965 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
966 DCHECK(*toolkit_version);
967 return *toolkit_version ? S_OK : E_FAIL;
968 }
969
970 //
971 // IAccessibleImage methods.
972 //
973
get_description(BSTR * desc)974 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
975 if (!instance_active())
976 return E_FAIL;
977
978 if (!desc)
979 return E_INVALIDARG;
980
981 return GetStringAttributeAsBstr(
982 ui::AX_ATTR_DESCRIPTION, desc);
983 }
984
get_imagePosition(enum IA2CoordinateType coordinate_type,LONG * x,LONG * y)985 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
986 enum IA2CoordinateType coordinate_type,
987 LONG* x,
988 LONG* y) {
989 if (!instance_active())
990 return E_FAIL;
991
992 if (!x || !y)
993 return E_INVALIDARG;
994
995 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
996 HWND parent_hwnd =
997 manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
998 if (!parent_hwnd)
999 return E_FAIL;
1000 POINT top_left = {0, 0};
1001 ::ClientToScreen(parent_hwnd, &top_left);
1002 *x = GetLocation().x() + top_left.x;
1003 *y = GetLocation().y() + top_left.y;
1004 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1005 *x = GetLocation().x();
1006 *y = GetLocation().y();
1007 if (GetParent()) {
1008 *x -= GetParent()->GetLocation().x();
1009 *y -= GetParent()->GetLocation().y();
1010 }
1011 } else {
1012 return E_INVALIDARG;
1013 }
1014
1015 return S_OK;
1016 }
1017
get_imageSize(LONG * height,LONG * width)1018 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1019 if (!instance_active())
1020 return E_FAIL;
1021
1022 if (!height || !width)
1023 return E_INVALIDARG;
1024
1025 *height = GetLocation().height();
1026 *width = GetLocation().width();
1027 return S_OK;
1028 }
1029
1030 //
1031 // IAccessibleTable methods.
1032 //
1033
get_accessibleAt(long row,long column,IUnknown ** accessible)1034 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1035 long row,
1036 long column,
1037 IUnknown** accessible) {
1038 if (!instance_active())
1039 return E_FAIL;
1040
1041 if (!accessible)
1042 return E_INVALIDARG;
1043
1044 int columns;
1045 int rows;
1046 if (!GetIntAttribute(
1047 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1048 !GetIntAttribute(
1049 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1050 columns <= 0 ||
1051 rows <= 0) {
1052 return S_FALSE;
1053 }
1054
1055 if (row < 0 || row >= rows || column < 0 || column >= columns)
1056 return E_INVALIDARG;
1057
1058 const std::vector<int32>& cell_ids = GetIntListAttribute(
1059 ui::AX_ATTR_CELL_IDS);
1060 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1061
1062 int cell_id = cell_ids[row * columns + column];
1063 BrowserAccessibilityWin* cell = GetFromID(cell_id);
1064 if (cell) {
1065 *accessible = static_cast<IAccessible*>(cell->NewReference());
1066 return S_OK;
1067 }
1068
1069 *accessible = NULL;
1070 return E_INVALIDARG;
1071 }
1072
get_caption(IUnknown ** accessible)1073 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1074 if (!instance_active())
1075 return E_FAIL;
1076
1077 if (!accessible)
1078 return E_INVALIDARG;
1079
1080 // TODO(dmazzoni): implement
1081 return S_FALSE;
1082 }
1083
get_childIndex(long row,long column,long * cell_index)1084 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1085 long column,
1086 long* cell_index) {
1087 if (!instance_active())
1088 return E_FAIL;
1089
1090 if (!cell_index)
1091 return E_INVALIDARG;
1092
1093 int columns;
1094 int rows;
1095 if (!GetIntAttribute(
1096 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1097 !GetIntAttribute(
1098 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1099 columns <= 0 ||
1100 rows <= 0) {
1101 return S_FALSE;
1102 }
1103
1104 if (row < 0 || row >= rows || column < 0 || column >= columns)
1105 return E_INVALIDARG;
1106
1107 const std::vector<int32>& cell_ids = GetIntListAttribute(
1108 ui::AX_ATTR_CELL_IDS);
1109 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1110 ui::AX_ATTR_UNIQUE_CELL_IDS);
1111 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1112 int cell_id = cell_ids[row * columns + column];
1113 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1114 if (unique_cell_ids[i] == cell_id) {
1115 *cell_index = (long)i;
1116 return S_OK;
1117 }
1118 }
1119
1120 return S_FALSE;
1121 }
1122
get_columnDescription(long column,BSTR * description)1123 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1124 BSTR* description) {
1125 if (!instance_active())
1126 return E_FAIL;
1127
1128 if (!description)
1129 return E_INVALIDARG;
1130
1131 int columns;
1132 int rows;
1133 if (!GetIntAttribute(
1134 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1135 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1136 columns <= 0 ||
1137 rows <= 0) {
1138 return S_FALSE;
1139 }
1140
1141 if (column < 0 || column >= columns)
1142 return E_INVALIDARG;
1143
1144 const std::vector<int32>& cell_ids = GetIntListAttribute(
1145 ui::AX_ATTR_CELL_IDS);
1146 for (int i = 0; i < rows; ++i) {
1147 int cell_id = cell_ids[i * columns + column];
1148 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1149 manager()->GetFromID(cell_id));
1150 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1151 base::string16 cell_name = cell->GetString16Attribute(
1152 ui::AX_ATTR_NAME);
1153 if (cell_name.size() > 0) {
1154 *description = SysAllocString(cell_name.c_str());
1155 return S_OK;
1156 }
1157
1158 return cell->GetStringAttributeAsBstr(
1159 ui::AX_ATTR_DESCRIPTION, description);
1160 }
1161 }
1162
1163 return S_FALSE;
1164 }
1165
get_columnExtentAt(long row,long column,long * n_columns_spanned)1166 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1167 long row,
1168 long column,
1169 long* n_columns_spanned) {
1170 if (!instance_active())
1171 return E_FAIL;
1172
1173 if (!n_columns_spanned)
1174 return E_INVALIDARG;
1175
1176 int columns;
1177 int rows;
1178 if (!GetIntAttribute(
1179 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1180 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1181 columns <= 0 ||
1182 rows <= 0) {
1183 return S_FALSE;
1184 }
1185
1186 if (row < 0 || row >= rows || column < 0 || column >= columns)
1187 return E_INVALIDARG;
1188
1189 const std::vector<int32>& cell_ids = GetIntListAttribute(
1190 ui::AX_ATTR_CELL_IDS);
1191 int cell_id = cell_ids[row * columns + column];
1192 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1193 manager()->GetFromID(cell_id));
1194 int colspan;
1195 if (cell &&
1196 cell->GetIntAttribute(
1197 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1198 colspan >= 1) {
1199 *n_columns_spanned = colspan;
1200 return S_OK;
1201 }
1202
1203 return S_FALSE;
1204 }
1205
get_columnHeader(IAccessibleTable ** accessible_table,long * starting_row_index)1206 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1207 IAccessibleTable** accessible_table,
1208 long* starting_row_index) {
1209 // TODO(dmazzoni): implement
1210 return E_NOTIMPL;
1211 }
1212
get_columnIndex(long cell_index,long * column_index)1213 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1214 long* column_index) {
1215 if (!instance_active())
1216 return E_FAIL;
1217
1218 if (!column_index)
1219 return E_INVALIDARG;
1220
1221 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1222 ui::AX_ATTR_UNIQUE_CELL_IDS);
1223 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1224 if (cell_index < 0)
1225 return E_INVALIDARG;
1226 if (cell_index >= cell_id_count)
1227 return S_FALSE;
1228
1229 int cell_id = unique_cell_ids[cell_index];
1230 BrowserAccessibilityWin* cell =
1231 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1232 int col_index;
1233 if (cell &&
1234 cell->GetIntAttribute(
1235 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1236 *column_index = col_index;
1237 return S_OK;
1238 }
1239
1240 return S_FALSE;
1241 }
1242
get_nColumns(long * column_count)1243 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1244 if (!instance_active())
1245 return E_FAIL;
1246
1247 if (!column_count)
1248 return E_INVALIDARG;
1249
1250 int columns;
1251 if (GetIntAttribute(
1252 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
1253 *column_count = columns;
1254 return S_OK;
1255 }
1256
1257 return S_FALSE;
1258 }
1259
get_nRows(long * row_count)1260 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1261 if (!instance_active())
1262 return E_FAIL;
1263
1264 if (!row_count)
1265 return E_INVALIDARG;
1266
1267 int rows;
1268 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1269 *row_count = rows;
1270 return S_OK;
1271 }
1272
1273 return S_FALSE;
1274 }
1275
get_nSelectedChildren(long * cell_count)1276 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1277 if (!instance_active())
1278 return E_FAIL;
1279
1280 if (!cell_count)
1281 return E_INVALIDARG;
1282
1283 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1284 *cell_count = 0;
1285 return S_OK;
1286 }
1287
get_nSelectedColumns(long * column_count)1288 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1289 if (!instance_active())
1290 return E_FAIL;
1291
1292 if (!column_count)
1293 return E_INVALIDARG;
1294
1295 *column_count = 0;
1296 return S_OK;
1297 }
1298
get_nSelectedRows(long * row_count)1299 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1300 if (!instance_active())
1301 return E_FAIL;
1302
1303 if (!row_count)
1304 return E_INVALIDARG;
1305
1306 *row_count = 0;
1307 return S_OK;
1308 }
1309
get_rowDescription(long row,BSTR * description)1310 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1311 BSTR* description) {
1312 if (!instance_active())
1313 return E_FAIL;
1314
1315 if (!description)
1316 return E_INVALIDARG;
1317
1318 int columns;
1319 int rows;
1320 if (!GetIntAttribute(
1321 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1322 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1323 columns <= 0 ||
1324 rows <= 0) {
1325 return S_FALSE;
1326 }
1327
1328 if (row < 0 || row >= rows)
1329 return E_INVALIDARG;
1330
1331 const std::vector<int32>& cell_ids = GetIntListAttribute(
1332 ui::AX_ATTR_CELL_IDS);
1333 for (int i = 0; i < columns; ++i) {
1334 int cell_id = cell_ids[row * columns + i];
1335 BrowserAccessibilityWin* cell =
1336 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1337 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1338 base::string16 cell_name = cell->GetString16Attribute(
1339 ui::AX_ATTR_NAME);
1340 if (cell_name.size() > 0) {
1341 *description = SysAllocString(cell_name.c_str());
1342 return S_OK;
1343 }
1344
1345 return cell->GetStringAttributeAsBstr(
1346 ui::AX_ATTR_DESCRIPTION, description);
1347 }
1348 }
1349
1350 return S_FALSE;
1351 }
1352
get_rowExtentAt(long row,long column,long * n_rows_spanned)1353 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1354 long column,
1355 long* n_rows_spanned) {
1356 if (!instance_active())
1357 return E_FAIL;
1358
1359 if (!n_rows_spanned)
1360 return E_INVALIDARG;
1361
1362 int columns;
1363 int rows;
1364 if (!GetIntAttribute(
1365 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1366 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1367 columns <= 0 ||
1368 rows <= 0) {
1369 return S_FALSE;
1370 }
1371
1372 if (row < 0 || row >= rows || column < 0 || column >= columns)
1373 return E_INVALIDARG;
1374
1375 const std::vector<int32>& cell_ids = GetIntListAttribute(
1376 ui::AX_ATTR_CELL_IDS);
1377 int cell_id = cell_ids[row * columns + column];
1378 BrowserAccessibilityWin* cell =
1379 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1380 int rowspan;
1381 if (cell &&
1382 cell->GetIntAttribute(
1383 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1384 rowspan >= 1) {
1385 *n_rows_spanned = rowspan;
1386 return S_OK;
1387 }
1388
1389 return S_FALSE;
1390 }
1391
get_rowHeader(IAccessibleTable ** accessible_table,long * starting_column_index)1392 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1393 IAccessibleTable** accessible_table,
1394 long* starting_column_index) {
1395 // TODO(dmazzoni): implement
1396 return E_NOTIMPL;
1397 }
1398
get_rowIndex(long cell_index,long * row_index)1399 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1400 long* row_index) {
1401 if (!instance_active())
1402 return E_FAIL;
1403
1404 if (!row_index)
1405 return E_INVALIDARG;
1406
1407 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1408 ui::AX_ATTR_UNIQUE_CELL_IDS);
1409 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1410 if (cell_index < 0)
1411 return E_INVALIDARG;
1412 if (cell_index >= cell_id_count)
1413 return S_FALSE;
1414
1415 int cell_id = unique_cell_ids[cell_index];
1416 BrowserAccessibilityWin* cell =
1417 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1418 int cell_row_index;
1419 if (cell &&
1420 cell->GetIntAttribute(
1421 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1422 *row_index = cell_row_index;
1423 return S_OK;
1424 }
1425
1426 return S_FALSE;
1427 }
1428
get_selectedChildren(long max_children,long ** children,long * n_children)1429 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1430 long** children,
1431 long* n_children) {
1432 if (!instance_active())
1433 return E_FAIL;
1434
1435 if (!children || !n_children)
1436 return E_INVALIDARG;
1437
1438 // TODO(dmazzoni): Implement this.
1439 *n_children = 0;
1440 return S_OK;
1441 }
1442
get_selectedColumns(long max_columns,long ** columns,long * n_columns)1443 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1444 long** columns,
1445 long* n_columns) {
1446 if (!instance_active())
1447 return E_FAIL;
1448
1449 if (!columns || !n_columns)
1450 return E_INVALIDARG;
1451
1452 // TODO(dmazzoni): Implement this.
1453 *n_columns = 0;
1454 return S_OK;
1455 }
1456
get_selectedRows(long max_rows,long ** rows,long * n_rows)1457 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1458 long** rows,
1459 long* n_rows) {
1460 if (!instance_active())
1461 return E_FAIL;
1462
1463 if (!rows || !n_rows)
1464 return E_INVALIDARG;
1465
1466 // TODO(dmazzoni): Implement this.
1467 *n_rows = 0;
1468 return S_OK;
1469 }
1470
get_summary(IUnknown ** accessible)1471 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1472 if (!instance_active())
1473 return E_FAIL;
1474
1475 if (!accessible)
1476 return E_INVALIDARG;
1477
1478 // TODO(dmazzoni): implement
1479 return S_FALSE;
1480 }
1481
get_isColumnSelected(long column,boolean * is_selected)1482 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1483 long column,
1484 boolean* is_selected) {
1485 if (!instance_active())
1486 return E_FAIL;
1487
1488 if (!is_selected)
1489 return E_INVALIDARG;
1490
1491 // TODO(dmazzoni): Implement this.
1492 *is_selected = false;
1493 return S_OK;
1494 }
1495
get_isRowSelected(long row,boolean * is_selected)1496 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1497 boolean* is_selected) {
1498 if (!instance_active())
1499 return E_FAIL;
1500
1501 if (!is_selected)
1502 return E_INVALIDARG;
1503
1504 // TODO(dmazzoni): Implement this.
1505 *is_selected = false;
1506 return S_OK;
1507 }
1508
get_isSelected(long row,long column,boolean * is_selected)1509 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1510 long column,
1511 boolean* is_selected) {
1512 if (!instance_active())
1513 return E_FAIL;
1514
1515 if (!is_selected)
1516 return E_INVALIDARG;
1517
1518 // TODO(dmazzoni): Implement this.
1519 *is_selected = false;
1520 return S_OK;
1521 }
1522
get_rowColumnExtentsAtIndex(long index,long * row,long * column,long * row_extents,long * column_extents,boolean * is_selected)1523 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1524 long index,
1525 long* row,
1526 long* column,
1527 long* row_extents,
1528 long* column_extents,
1529 boolean* is_selected) {
1530 if (!instance_active())
1531 return E_FAIL;
1532
1533 if (!row || !column || !row_extents || !column_extents || !is_selected)
1534 return E_INVALIDARG;
1535
1536 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1537 ui::AX_ATTR_UNIQUE_CELL_IDS);
1538 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1539 if (index < 0)
1540 return E_INVALIDARG;
1541 if (index >= cell_id_count)
1542 return S_FALSE;
1543
1544 int cell_id = unique_cell_ids[index];
1545 BrowserAccessibilityWin* cell =
1546 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1547 int rowspan;
1548 int colspan;
1549 if (cell &&
1550 cell->GetIntAttribute(
1551 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1552 cell->GetIntAttribute(
1553 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1554 rowspan >= 1 &&
1555 colspan >= 1) {
1556 *row_extents = rowspan;
1557 *column_extents = colspan;
1558 return S_OK;
1559 }
1560
1561 return S_FALSE;
1562 }
1563
1564 //
1565 // IAccessibleTable2 methods.
1566 //
1567
get_cellAt(long row,long column,IUnknown ** cell)1568 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1569 long column,
1570 IUnknown** cell) {
1571 return get_accessibleAt(row, column, cell);
1572 }
1573
get_nSelectedCells(long * cell_count)1574 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1575 return get_nSelectedChildren(cell_count);
1576 }
1577
get_selectedCells(IUnknown *** cells,long * n_selected_cells)1578 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1579 IUnknown*** cells,
1580 long* n_selected_cells) {
1581 if (!instance_active())
1582 return E_FAIL;
1583
1584 if (!cells || !n_selected_cells)
1585 return E_INVALIDARG;
1586
1587 // TODO(dmazzoni): Implement this.
1588 *n_selected_cells = 0;
1589 return S_OK;
1590 }
1591
get_selectedColumns(long ** columns,long * n_columns)1592 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1593 long* n_columns) {
1594 if (!instance_active())
1595 return E_FAIL;
1596
1597 if (!columns || !n_columns)
1598 return E_INVALIDARG;
1599
1600 // TODO(dmazzoni): Implement this.
1601 *n_columns = 0;
1602 return S_OK;
1603 }
1604
get_selectedRows(long ** rows,long * n_rows)1605 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1606 long* n_rows) {
1607 if (!instance_active())
1608 return E_FAIL;
1609
1610 if (!rows || !n_rows)
1611 return E_INVALIDARG;
1612
1613 // TODO(dmazzoni): Implement this.
1614 *n_rows = 0;
1615 return S_OK;
1616 }
1617
1618
1619 //
1620 // IAccessibleTableCell methods.
1621 //
1622
get_columnExtent(long * n_columns_spanned)1623 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1624 long* n_columns_spanned) {
1625 if (!instance_active())
1626 return E_FAIL;
1627
1628 if (!n_columns_spanned)
1629 return E_INVALIDARG;
1630
1631 int colspan;
1632 if (GetIntAttribute(
1633 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1634 colspan >= 1) {
1635 *n_columns_spanned = colspan;
1636 return S_OK;
1637 }
1638
1639 return S_FALSE;
1640 }
1641
get_columnHeaderCells(IUnknown *** cell_accessibles,long * n_column_header_cells)1642 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1643 IUnknown*** cell_accessibles,
1644 long* n_column_header_cells) {
1645 if (!instance_active())
1646 return E_FAIL;
1647
1648 if (!cell_accessibles || !n_column_header_cells)
1649 return E_INVALIDARG;
1650
1651 *n_column_header_cells = 0;
1652
1653 int column;
1654 if (!GetIntAttribute(
1655 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1656 return S_FALSE;
1657 }
1658
1659 BrowserAccessibility* table = GetParent();
1660 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1661 table = table->GetParent();
1662 if (!table) {
1663 NOTREACHED();
1664 return S_FALSE;
1665 }
1666
1667 int columns;
1668 int rows;
1669 if (!table->GetIntAttribute(
1670 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1671 !table->GetIntAttribute(
1672 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1673 return S_FALSE;
1674 }
1675 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1676 return S_FALSE;
1677
1678 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1679 ui::AX_ATTR_CELL_IDS);
1680
1681 for (int i = 0; i < rows; ++i) {
1682 int cell_id = cell_ids[i * columns + column];
1683 BrowserAccessibilityWin* cell =
1684 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1685 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
1686 (*n_column_header_cells)++;
1687 }
1688
1689 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1690 (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1691 int index = 0;
1692 for (int i = 0; i < rows; ++i) {
1693 int cell_id = cell_ids[i * columns + column];
1694 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1695 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1696 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1697 cell->ToBrowserAccessibilityWin()->NewReference());
1698 ++index;
1699 }
1700 }
1701
1702 return S_OK;
1703 }
1704
get_columnIndex(long * column_index)1705 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1706 if (!instance_active())
1707 return E_FAIL;
1708
1709 if (!column_index)
1710 return E_INVALIDARG;
1711
1712 int column;
1713 if (GetIntAttribute(
1714 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1715 *column_index = column;
1716 return S_OK;
1717 }
1718
1719 return S_FALSE;
1720 }
1721
get_rowExtent(long * n_rows_spanned)1722 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1723 if (!instance_active())
1724 return E_FAIL;
1725
1726 if (!n_rows_spanned)
1727 return E_INVALIDARG;
1728
1729 int rowspan;
1730 if (GetIntAttribute(
1731 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1732 rowspan >= 1) {
1733 *n_rows_spanned = rowspan;
1734 return S_OK;
1735 }
1736
1737 return S_FALSE;
1738 }
1739
get_rowHeaderCells(IUnknown *** cell_accessibles,long * n_row_header_cells)1740 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1741 IUnknown*** cell_accessibles,
1742 long* n_row_header_cells) {
1743 if (!instance_active())
1744 return E_FAIL;
1745
1746 if (!cell_accessibles || !n_row_header_cells)
1747 return E_INVALIDARG;
1748
1749 *n_row_header_cells = 0;
1750
1751 int row;
1752 if (!GetIntAttribute(
1753 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1754 return S_FALSE;
1755 }
1756
1757 BrowserAccessibility* table = GetParent();
1758 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1759 table = table->GetParent();
1760 if (!table) {
1761 NOTREACHED();
1762 return S_FALSE;
1763 }
1764
1765 int columns;
1766 int rows;
1767 if (!table->GetIntAttribute(
1768 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1769 !table->GetIntAttribute(
1770 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1771 return S_FALSE;
1772 }
1773 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1774 return S_FALSE;
1775
1776 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1777 ui::AX_ATTR_CELL_IDS);
1778
1779 for (int i = 0; i < columns; ++i) {
1780 int cell_id = cell_ids[row * columns + i];
1781 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1782 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1783 (*n_row_header_cells)++;
1784 }
1785
1786 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1787 (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1788 int index = 0;
1789 for (int i = 0; i < columns; ++i) {
1790 int cell_id = cell_ids[row * columns + i];
1791 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1792 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1793 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1794 cell->ToBrowserAccessibilityWin()->NewReference());
1795 ++index;
1796 }
1797 }
1798
1799 return S_OK;
1800 }
1801
get_rowIndex(long * row_index)1802 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1803 if (!instance_active())
1804 return E_FAIL;
1805
1806 if (!row_index)
1807 return E_INVALIDARG;
1808
1809 int row;
1810 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1811 *row_index = row;
1812 return S_OK;
1813 }
1814 return S_FALSE;
1815 }
1816
get_isSelected(boolean * is_selected)1817 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1818 if (!instance_active())
1819 return E_FAIL;
1820
1821 if (!is_selected)
1822 return E_INVALIDARG;
1823
1824 *is_selected = false;
1825 return S_OK;
1826 }
1827
get_rowColumnExtents(long * row_index,long * column_index,long * row_extents,long * column_extents,boolean * is_selected)1828 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1829 long* row_index,
1830 long* column_index,
1831 long* row_extents,
1832 long* column_extents,
1833 boolean* is_selected) {
1834 if (!instance_active())
1835 return E_FAIL;
1836
1837 if (!row_index ||
1838 !column_index ||
1839 !row_extents ||
1840 !column_extents ||
1841 !is_selected) {
1842 return E_INVALIDARG;
1843 }
1844
1845 int row;
1846 int column;
1847 int rowspan;
1848 int colspan;
1849 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1850 GetIntAttribute(
1851 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1852 GetIntAttribute(
1853 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1854 GetIntAttribute(
1855 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1856 *row_index = row;
1857 *column_index = column;
1858 *row_extents = rowspan;
1859 *column_extents = colspan;
1860 *is_selected = false;
1861 return S_OK;
1862 }
1863
1864 return S_FALSE;
1865 }
1866
get_table(IUnknown ** table)1867 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1868 if (!instance_active())
1869 return E_FAIL;
1870
1871 if (!table)
1872 return E_INVALIDARG;
1873
1874
1875 int row;
1876 int column;
1877 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1878 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1879
1880 BrowserAccessibility* find_table = GetParent();
1881 while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
1882 find_table = find_table->GetParent();
1883 if (!find_table) {
1884 NOTREACHED();
1885 return S_FALSE;
1886 }
1887
1888 *table = static_cast<IAccessibleTable*>(
1889 find_table->ToBrowserAccessibilityWin()->NewReference());
1890
1891 return S_OK;
1892 }
1893
1894 //
1895 // IAccessibleText methods.
1896 //
1897
get_nCharacters(LONG * n_characters)1898 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1899 if (!instance_active())
1900 return E_FAIL;
1901
1902 if (!n_characters)
1903 return E_INVALIDARG;
1904
1905 *n_characters = TextForIAccessibleText().length();
1906 return S_OK;
1907 }
1908
get_caretOffset(LONG * offset)1909 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1910 if (!instance_active())
1911 return E_FAIL;
1912
1913 if (!offset)
1914 return E_INVALIDARG;
1915
1916 *offset = 0;
1917 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1918 GetRole() == ui::AX_ROLE_TEXT_AREA) {
1919 int sel_start = 0;
1920 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1921 &sel_start))
1922 *offset = sel_start;
1923 }
1924
1925 return S_OK;
1926 }
1927
get_characterExtents(LONG offset,enum IA2CoordinateType coordinate_type,LONG * out_x,LONG * out_y,LONG * out_width,LONG * out_height)1928 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1929 LONG offset,
1930 enum IA2CoordinateType coordinate_type,
1931 LONG* out_x,
1932 LONG* out_y,
1933 LONG* out_width,
1934 LONG* out_height) {
1935 if (!instance_active())
1936 return E_FAIL;
1937
1938 if (!out_x || !out_y || !out_width || !out_height)
1939 return E_INVALIDARG;
1940
1941 const base::string16& text_str = TextForIAccessibleText();
1942 HandleSpecialTextOffset(text_str, &offset);
1943
1944 if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1945 return E_INVALIDARG;
1946
1947 gfx::Rect character_bounds;
1948 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1949 character_bounds = GetGlobalBoundsForRange(offset, 1);
1950 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1951 character_bounds = GetLocalBoundsForRange(offset, 1);
1952 character_bounds -= GetLocation().OffsetFromOrigin();
1953 } else {
1954 return E_INVALIDARG;
1955 }
1956
1957 *out_x = character_bounds.x();
1958 *out_y = character_bounds.y();
1959 *out_width = character_bounds.width();
1960 *out_height = character_bounds.height();
1961
1962 return S_OK;
1963 }
1964
get_nSelections(LONG * n_selections)1965 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
1966 if (!instance_active())
1967 return E_FAIL;
1968
1969 if (!n_selections)
1970 return E_INVALIDARG;
1971
1972 *n_selections = 0;
1973 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1974 GetRole() == ui::AX_ROLE_TEXT_AREA) {
1975 int sel_start = 0;
1976 int sel_end = 0;
1977 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1978 &sel_start) &&
1979 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
1980 sel_start != sel_end)
1981 *n_selections = 1;
1982 }
1983
1984 return S_OK;
1985 }
1986
get_selection(LONG selection_index,LONG * start_offset,LONG * end_offset)1987 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
1988 LONG* start_offset,
1989 LONG* end_offset) {
1990 if (!instance_active())
1991 return E_FAIL;
1992
1993 if (!start_offset || !end_offset || selection_index != 0)
1994 return E_INVALIDARG;
1995
1996 *start_offset = 0;
1997 *end_offset = 0;
1998 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1999 GetRole() == ui::AX_ROLE_TEXT_AREA) {
2000 int sel_start = 0;
2001 int sel_end = 0;
2002 if (GetIntAttribute(
2003 ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
2004 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
2005 *start_offset = sel_start;
2006 *end_offset = sel_end;
2007 }
2008 }
2009
2010 return S_OK;
2011 }
2012
get_text(LONG start_offset,LONG end_offset,BSTR * text)2013 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2014 LONG end_offset,
2015 BSTR* text) {
2016 if (!instance_active())
2017 return E_FAIL;
2018
2019 if (!text)
2020 return E_INVALIDARG;
2021
2022 const base::string16& text_str = TextForIAccessibleText();
2023
2024 // Handle special text offsets.
2025 HandleSpecialTextOffset(text_str, &start_offset);
2026 HandleSpecialTextOffset(text_str, &end_offset);
2027
2028 // The spec allows the arguments to be reversed.
2029 if (start_offset > end_offset) {
2030 LONG tmp = start_offset;
2031 start_offset = end_offset;
2032 end_offset = tmp;
2033 }
2034
2035 // The spec does not allow the start or end offsets to be out or range;
2036 // we must return an error if so.
2037 LONG len = text_str.length();
2038 if (start_offset < 0)
2039 return E_INVALIDARG;
2040 if (end_offset > len)
2041 return E_INVALIDARG;
2042
2043 base::string16 substr = text_str.substr(start_offset,
2044 end_offset - start_offset);
2045 if (substr.empty())
2046 return S_FALSE;
2047
2048 *text = SysAllocString(substr.c_str());
2049 DCHECK(*text);
2050 return S_OK;
2051 }
2052
get_textAtOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2053 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2054 LONG offset,
2055 enum IA2TextBoundaryType boundary_type,
2056 LONG* start_offset,
2057 LONG* end_offset,
2058 BSTR* text) {
2059 if (!instance_active())
2060 return E_FAIL;
2061
2062 if (!start_offset || !end_offset || !text)
2063 return E_INVALIDARG;
2064
2065 // The IAccessible2 spec says we don't have to implement the "sentence"
2066 // boundary type, we can just let the screenreader handle it.
2067 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2068 *start_offset = 0;
2069 *end_offset = 0;
2070 *text = NULL;
2071 return S_FALSE;
2072 }
2073
2074 const base::string16& text_str = TextForIAccessibleText();
2075
2076 *start_offset = FindBoundary(
2077 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2078 *end_offset = FindBoundary(
2079 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2080 return get_text(*start_offset, *end_offset, text);
2081 }
2082
get_textBeforeOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2083 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2084 LONG offset,
2085 enum IA2TextBoundaryType boundary_type,
2086 LONG* start_offset,
2087 LONG* end_offset,
2088 BSTR* text) {
2089 if (!instance_active())
2090 return E_FAIL;
2091
2092 if (!start_offset || !end_offset || !text)
2093 return E_INVALIDARG;
2094
2095 // The IAccessible2 spec says we don't have to implement the "sentence"
2096 // boundary type, we can just let the screenreader handle it.
2097 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2098 *start_offset = 0;
2099 *end_offset = 0;
2100 *text = NULL;
2101 return S_FALSE;
2102 }
2103
2104 const base::string16& text_str = TextForIAccessibleText();
2105
2106 *start_offset = FindBoundary(
2107 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2108 *end_offset = offset;
2109 return get_text(*start_offset, *end_offset, text);
2110 }
2111
get_textAfterOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2112 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2113 LONG offset,
2114 enum IA2TextBoundaryType boundary_type,
2115 LONG* start_offset,
2116 LONG* end_offset,
2117 BSTR* text) {
2118 if (!instance_active())
2119 return E_FAIL;
2120
2121 if (!start_offset || !end_offset || !text)
2122 return E_INVALIDARG;
2123
2124 // The IAccessible2 spec says we don't have to implement the "sentence"
2125 // boundary type, we can just let the screenreader handle it.
2126 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2127 *start_offset = 0;
2128 *end_offset = 0;
2129 *text = NULL;
2130 return S_FALSE;
2131 }
2132
2133 const base::string16& text_str = TextForIAccessibleText();
2134
2135 *start_offset = offset;
2136 *end_offset = FindBoundary(
2137 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2138 return get_text(*start_offset, *end_offset, text);
2139 }
2140
get_newText(IA2TextSegment * new_text)2141 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2142 if (!instance_active())
2143 return E_FAIL;
2144
2145 if (!new_text)
2146 return E_INVALIDARG;
2147
2148 base::string16 text = TextForIAccessibleText();
2149
2150 new_text->text = SysAllocString(text.c_str());
2151 new_text->start = 0;
2152 new_text->end = static_cast<long>(text.size());
2153 return S_OK;
2154 }
2155
get_oldText(IA2TextSegment * old_text)2156 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2157 if (!instance_active())
2158 return E_FAIL;
2159
2160 if (!old_text)
2161 return E_INVALIDARG;
2162
2163 old_text->text = SysAllocString(old_text_.c_str());
2164 old_text->start = 0;
2165 old_text->end = static_cast<long>(old_text_.size());
2166 return S_OK;
2167 }
2168
get_offsetAtPoint(LONG x,LONG y,enum IA2CoordinateType coord_type,LONG * offset)2169 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2170 LONG x,
2171 LONG y,
2172 enum IA2CoordinateType coord_type,
2173 LONG* offset) {
2174 if (!instance_active())
2175 return E_FAIL;
2176
2177 if (!offset)
2178 return E_INVALIDARG;
2179
2180 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2181 // screen readers still return partially accurate results rather than
2182 // completely failing.
2183 *offset = 0;
2184 return S_OK;
2185 }
2186
scrollSubstringTo(LONG start_index,LONG end_index,enum IA2ScrollType scroll_type)2187 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2188 LONG start_index,
2189 LONG end_index,
2190 enum IA2ScrollType scroll_type) {
2191 // TODO(dmazzoni): adjust this for the start and end index, too.
2192 return scrollTo(scroll_type);
2193 }
2194
scrollSubstringToPoint(LONG start_index,LONG end_index,enum IA2CoordinateType coordinate_type,LONG x,LONG y)2195 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2196 LONG start_index,
2197 LONG end_index,
2198 enum IA2CoordinateType coordinate_type,
2199 LONG x, LONG y) {
2200 // TODO(dmazzoni): adjust this for the start and end index, too.
2201 return scrollToPoint(coordinate_type, x, y);
2202 }
2203
addSelection(LONG start_offset,LONG end_offset)2204 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2205 LONG end_offset) {
2206 if (!instance_active())
2207 return E_FAIL;
2208
2209 const base::string16& text_str = TextForIAccessibleText();
2210 HandleSpecialTextOffset(text_str, &start_offset);
2211 HandleSpecialTextOffset(text_str, &end_offset);
2212
2213 manager()->SetTextSelection(*this, start_offset, end_offset);
2214 return S_OK;
2215 }
2216
removeSelection(LONG selection_index)2217 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2218 if (!instance_active())
2219 return E_FAIL;
2220
2221 if (selection_index != 0)
2222 return E_INVALIDARG;
2223
2224 manager()->SetTextSelection(*this, 0, 0);
2225 return S_OK;
2226 }
2227
setCaretOffset(LONG offset)2228 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2229 if (!instance_active())
2230 return E_FAIL;
2231
2232 const base::string16& text_str = TextForIAccessibleText();
2233 HandleSpecialTextOffset(text_str, &offset);
2234 manager()->SetTextSelection(*this, offset, offset);
2235 return S_OK;
2236 }
2237
setSelection(LONG selection_index,LONG start_offset,LONG end_offset)2238 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2239 LONG start_offset,
2240 LONG end_offset) {
2241 if (!instance_active())
2242 return E_FAIL;
2243
2244 if (selection_index != 0)
2245 return E_INVALIDARG;
2246
2247 const base::string16& text_str = TextForIAccessibleText();
2248 HandleSpecialTextOffset(text_str, &start_offset);
2249 HandleSpecialTextOffset(text_str, &end_offset);
2250
2251 manager()->SetTextSelection(*this, start_offset, end_offset);
2252 return S_OK;
2253 }
2254
2255 //
2256 // IAccessibleHypertext methods.
2257 //
2258
get_nHyperlinks(long * hyperlink_count)2259 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2260 if (!instance_active())
2261 return E_FAIL;
2262
2263 if (!hyperlink_count)
2264 return E_INVALIDARG;
2265
2266 *hyperlink_count = hyperlink_offset_to_index_.size();
2267 return S_OK;
2268 }
2269
get_hyperlink(long index,IAccessibleHyperlink ** hyperlink)2270 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2271 long index,
2272 IAccessibleHyperlink** hyperlink) {
2273 if (!instance_active())
2274 return E_FAIL;
2275
2276 if (!hyperlink ||
2277 index < 0 ||
2278 index >= static_cast<long>(hyperlinks_.size())) {
2279 return E_INVALIDARG;
2280 }
2281
2282 BrowserAccessibilityWin* child =
2283 InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin();
2284 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2285 return S_OK;
2286 }
2287
get_hyperlinkIndex(long char_index,long * hyperlink_index)2288 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2289 long char_index,
2290 long* hyperlink_index) {
2291 if (!instance_active())
2292 return E_FAIL;
2293
2294 if (!hyperlink_index)
2295 return E_INVALIDARG;
2296
2297 *hyperlink_index = -1;
2298
2299 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
2300 return E_INVALIDARG;
2301
2302 std::map<int32, int32>::iterator it =
2303 hyperlink_offset_to_index_.find(char_index);
2304 if (it == hyperlink_offset_to_index_.end())
2305 return E_FAIL;
2306
2307 *hyperlink_index = it->second;
2308 return S_OK;
2309 }
2310
2311 //
2312 // IAccessibleValue methods.
2313 //
2314
get_currentValue(VARIANT * value)2315 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2316 if (!instance_active())
2317 return E_FAIL;
2318
2319 if (!value)
2320 return E_INVALIDARG;
2321
2322 float float_val;
2323 if (GetFloatAttribute(
2324 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
2325 value->vt = VT_R8;
2326 value->dblVal = float_val;
2327 return S_OK;
2328 }
2329
2330 value->vt = VT_EMPTY;
2331 return S_FALSE;
2332 }
2333
get_minimumValue(VARIANT * value)2334 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2335 if (!instance_active())
2336 return E_FAIL;
2337
2338 if (!value)
2339 return E_INVALIDARG;
2340
2341 float float_val;
2342 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
2343 &float_val)) {
2344 value->vt = VT_R8;
2345 value->dblVal = float_val;
2346 return S_OK;
2347 }
2348
2349 value->vt = VT_EMPTY;
2350 return S_FALSE;
2351 }
2352
get_maximumValue(VARIANT * value)2353 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2354 if (!instance_active())
2355 return E_FAIL;
2356
2357 if (!value)
2358 return E_INVALIDARG;
2359
2360 float float_val;
2361 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
2362 &float_val)) {
2363 value->vt = VT_R8;
2364 value->dblVal = float_val;
2365 return S_OK;
2366 }
2367
2368 value->vt = VT_EMPTY;
2369 return S_FALSE;
2370 }
2371
setCurrentValue(VARIANT new_value)2372 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2373 // TODO(dmazzoni): Implement this.
2374 return E_NOTIMPL;
2375 }
2376
2377 //
2378 // ISimpleDOMDocument methods.
2379 //
2380
get_URL(BSTR * url)2381 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2382 if (!instance_active())
2383 return E_FAIL;
2384
2385 if (!url)
2386 return E_INVALIDARG;
2387
2388 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
2389 }
2390
get_title(BSTR * title)2391 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2392 if (!instance_active())
2393 return E_FAIL;
2394
2395 if (!title)
2396 return E_INVALIDARG;
2397
2398 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
2399 }
2400
get_mimeType(BSTR * mime_type)2401 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2402 if (!instance_active())
2403 return E_FAIL;
2404
2405 if (!mime_type)
2406 return E_INVALIDARG;
2407
2408 return GetStringAttributeAsBstr(
2409 ui::AX_ATTR_DOC_MIMETYPE, mime_type);
2410 }
2411
get_docType(BSTR * doc_type)2412 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2413 if (!instance_active())
2414 return E_FAIL;
2415
2416 if (!doc_type)
2417 return E_INVALIDARG;
2418
2419 return GetStringAttributeAsBstr(
2420 ui::AX_ATTR_DOC_DOCTYPE, doc_type);
2421 }
2422
2423 //
2424 // ISimpleDOMNode methods.
2425 //
2426
get_nodeInfo(BSTR * node_name,short * name_space_id,BSTR * node_value,unsigned int * num_children,unsigned int * unique_id,unsigned short * node_type)2427 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2428 BSTR* node_name,
2429 short* name_space_id,
2430 BSTR* node_value,
2431 unsigned int* num_children,
2432 unsigned int* unique_id,
2433 unsigned short* node_type) {
2434 if (!instance_active())
2435 return E_FAIL;
2436
2437 if (!node_name || !name_space_id || !node_value || !num_children ||
2438 !unique_id || !node_type) {
2439 return E_INVALIDARG;
2440 }
2441
2442 base::string16 tag;
2443 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
2444 *node_name = SysAllocString(tag.c_str());
2445 else
2446 *node_name = NULL;
2447
2448 *name_space_id = 0;
2449 *node_value = SysAllocString(base::UTF8ToUTF16(value()).c_str());
2450 *num_children = PlatformChildCount();
2451 *unique_id = unique_id_win_;
2452
2453 if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
2454 *node_type = NODETYPE_DOCUMENT;
2455 } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
2456 ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
2457 *node_type = NODETYPE_TEXT;
2458 } else {
2459 *node_type = NODETYPE_ELEMENT;
2460 }
2461
2462 return S_OK;
2463 }
2464
get_attributes(unsigned short max_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values,unsigned short * num_attribs)2465 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2466 unsigned short max_attribs,
2467 BSTR* attrib_names,
2468 short* name_space_id,
2469 BSTR* attrib_values,
2470 unsigned short* num_attribs) {
2471 if (!instance_active())
2472 return E_FAIL;
2473
2474 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2475 return E_INVALIDARG;
2476
2477 *num_attribs = max_attribs;
2478 if (*num_attribs > GetHtmlAttributes().size())
2479 *num_attribs = GetHtmlAttributes().size();
2480
2481 for (unsigned short i = 0; i < *num_attribs; ++i) {
2482 attrib_names[i] = SysAllocString(
2483 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
2484 name_space_id[i] = 0;
2485 attrib_values[i] = SysAllocString(
2486 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
2487 }
2488 return S_OK;
2489 }
2490
get_attributesForNames(unsigned short num_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values)2491 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2492 unsigned short num_attribs,
2493 BSTR* attrib_names,
2494 short* name_space_id,
2495 BSTR* attrib_values) {
2496 if (!instance_active())
2497 return E_FAIL;
2498
2499 if (!attrib_names || !name_space_id || !attrib_values)
2500 return E_INVALIDARG;
2501
2502 for (unsigned short i = 0; i < num_attribs; ++i) {
2503 name_space_id[i] = 0;
2504 bool found = false;
2505 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2506 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) {
2507 if (GetHtmlAttributes()[j].first == name) {
2508 attrib_values[i] = SysAllocString(
2509 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
2510 found = true;
2511 break;
2512 }
2513 }
2514 if (!found) {
2515 attrib_values[i] = NULL;
2516 }
2517 }
2518 return S_OK;
2519 }
2520
get_computedStyle(unsigned short max_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values,unsigned short * num_style_properties)2521 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2522 unsigned short max_style_properties,
2523 boolean use_alternate_view,
2524 BSTR* style_properties,
2525 BSTR* style_values,
2526 unsigned short *num_style_properties) {
2527 if (!instance_active())
2528 return E_FAIL;
2529
2530 if (!style_properties || !style_values)
2531 return E_INVALIDARG;
2532
2533 // We only cache a single style property for now: DISPLAY
2534
2535 base::string16 display;
2536 if (max_style_properties == 0 ||
2537 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
2538 *num_style_properties = 0;
2539 return S_OK;
2540 }
2541
2542 *num_style_properties = 1;
2543 style_properties[0] = SysAllocString(L"display");
2544 style_values[0] = SysAllocString(display.c_str());
2545
2546 return S_OK;
2547 }
2548
get_computedStyleForProperties(unsigned short num_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values)2549 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2550 unsigned short num_style_properties,
2551 boolean use_alternate_view,
2552 BSTR* style_properties,
2553 BSTR* style_values) {
2554 if (!instance_active())
2555 return E_FAIL;
2556
2557 if (!style_properties || !style_values)
2558 return E_INVALIDARG;
2559
2560 // We only cache a single style property for now: DISPLAY
2561
2562 for (unsigned short i = 0; i < num_style_properties; ++i) {
2563 base::string16 name = (LPCWSTR)style_properties[i];
2564 StringToLowerASCII(&name);
2565 if (name == L"display") {
2566 base::string16 display = GetString16Attribute(
2567 ui::AX_ATTR_DISPLAY);
2568 style_values[i] = SysAllocString(display.c_str());
2569 } else {
2570 style_values[i] = NULL;
2571 }
2572 }
2573
2574 return S_OK;
2575 }
2576
scrollTo(boolean placeTopLeft)2577 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2578 return scrollTo(placeTopLeft ?
2579 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2580 }
2581
get_parentNode(ISimpleDOMNode ** node)2582 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2583 if (!instance_active())
2584 return E_FAIL;
2585
2586 if (!node)
2587 return E_INVALIDARG;
2588
2589 *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
2590 return S_OK;
2591 }
2592
get_firstChild(ISimpleDOMNode ** node)2593 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
2594 if (!instance_active())
2595 return E_FAIL;
2596
2597 if (!node)
2598 return E_INVALIDARG;
2599
2600 if (PlatformChildCount() == 0) {
2601 *node = NULL;
2602 return S_FALSE;
2603 }
2604
2605 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2606 return S_OK;
2607 }
2608
get_lastChild(ISimpleDOMNode ** node)2609 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2610 if (!instance_active())
2611 return E_FAIL;
2612
2613 if (!node)
2614 return E_INVALIDARG;
2615
2616 if (PlatformChildCount() == 0) {
2617 *node = NULL;
2618 return S_FALSE;
2619 }
2620
2621 *node = PlatformGetChild(PlatformChildCount() - 1)
2622 ->ToBrowserAccessibilityWin()->NewReference();
2623 return S_OK;
2624 }
2625
get_previousSibling(ISimpleDOMNode ** node)2626 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2627 ISimpleDOMNode** node) {
2628 if (!instance_active())
2629 return E_FAIL;
2630
2631 if (!node)
2632 return E_INVALIDARG;
2633
2634 if (!GetParent() || GetIndexInParent() <= 0) {
2635 *node = NULL;
2636 return S_FALSE;
2637 }
2638
2639 *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2640 ToBrowserAccessibilityWin()->NewReference();
2641 return S_OK;
2642 }
2643
get_nextSibling(ISimpleDOMNode ** node)2644 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2645 if (!instance_active())
2646 return E_FAIL;
2647
2648 if (!node)
2649 return E_INVALIDARG;
2650
2651 if (!GetParent() ||
2652 GetIndexInParent() < 0 ||
2653 GetIndexInParent() >= static_cast<int>(
2654 GetParent()->InternalChildCount()) - 1) {
2655 *node = NULL;
2656 return S_FALSE;
2657 }
2658
2659 *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2660 ToBrowserAccessibilityWin()->NewReference();
2661 return S_OK;
2662 }
2663
get_childAt(unsigned int child_index,ISimpleDOMNode ** node)2664 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2665 unsigned int child_index,
2666 ISimpleDOMNode** node) {
2667 if (!instance_active())
2668 return E_FAIL;
2669
2670 if (!node)
2671 return E_INVALIDARG;
2672
2673 if (child_index >= PlatformChildCount())
2674 return E_INVALIDARG;
2675
2676 BrowserAccessibility* child = PlatformGetChild(child_index);
2677 if (!child) {
2678 *node = NULL;
2679 return S_FALSE;
2680 }
2681
2682 *node = child->ToBrowserAccessibilityWin()->NewReference();
2683 return S_OK;
2684 }
2685
2686 //
2687 // ISimpleDOMText methods.
2688 //
2689
get_domText(BSTR * dom_text)2690 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2691 if (!instance_active())
2692 return E_FAIL;
2693
2694 if (!dom_text)
2695 return E_INVALIDARG;
2696
2697 return GetStringAttributeAsBstr(
2698 ui::AX_ATTR_NAME, dom_text);
2699 }
2700
get_clippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2701 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2702 unsigned int start_index,
2703 unsigned int end_index,
2704 int* out_x,
2705 int* out_y,
2706 int* out_width,
2707 int* out_height) {
2708 // TODO(dmazzoni): fully support this API by intersecting the
2709 // rect with the container's rect.
2710 return get_unclippedSubstringBounds(
2711 start_index, end_index, out_x, out_y, out_width, out_height);
2712 }
2713
get_unclippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2714 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2715 unsigned int start_index,
2716 unsigned int end_index,
2717 int* out_x,
2718 int* out_y,
2719 int* out_width,
2720 int* out_height) {
2721 if (!instance_active())
2722 return E_FAIL;
2723
2724 if (!out_x || !out_y || !out_width || !out_height)
2725 return E_INVALIDARG;
2726
2727 const base::string16& text_str = TextForIAccessibleText();
2728 if (start_index > text_str.size() ||
2729 end_index > text_str.size() ||
2730 start_index > end_index) {
2731 return E_INVALIDARG;
2732 }
2733
2734 gfx::Rect bounds = GetGlobalBoundsForRange(
2735 start_index, end_index - start_index);
2736 *out_x = bounds.x();
2737 *out_y = bounds.y();
2738 *out_width = bounds.width();
2739 *out_height = bounds.height();
2740 return S_OK;
2741 }
2742
scrollToSubstring(unsigned int start_index,unsigned int end_index)2743 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2744 unsigned int start_index,
2745 unsigned int end_index) {
2746 if (!instance_active())
2747 return E_FAIL;
2748
2749 const base::string16& text_str = TextForIAccessibleText();
2750 if (start_index > text_str.size() ||
2751 end_index > text_str.size() ||
2752 start_index > end_index) {
2753 return E_INVALIDARG;
2754 }
2755
2756 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2757 start_index, end_index - start_index));
2758 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2759
2760 return S_OK;
2761 }
2762
2763 //
2764 // IServiceProvider methods.
2765 //
2766
QueryService(REFGUID guidService,REFIID riid,void ** object)2767 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2768 REFIID riid,
2769 void** object) {
2770 if (!instance_active())
2771 return E_FAIL;
2772
2773 // The system uses IAccessible APIs for many purposes, but only
2774 // assistive technology like screen readers uses IAccessible2.
2775 // Enable full accessibility support when IAccessible2 APIs are queried.
2776 if (riid == IID_IAccessible2)
2777 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2778
2779 if (guidService == GUID_IAccessibleContentDocument) {
2780 // Special Mozilla extension: return the accessible for the root document.
2781 // Screen readers use this to distinguish between a document loaded event
2782 // on the root document vs on an iframe.
2783 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2784 IID_IAccessible2, object);
2785 }
2786
2787 if (guidService == IID_IAccessible ||
2788 guidService == IID_IAccessible2 ||
2789 guidService == IID_IAccessibleAction ||
2790 guidService == IID_IAccessibleApplication ||
2791 guidService == IID_IAccessibleHyperlink ||
2792 guidService == IID_IAccessibleHypertext ||
2793 guidService == IID_IAccessibleImage ||
2794 guidService == IID_IAccessibleTable ||
2795 guidService == IID_IAccessibleTable2 ||
2796 guidService == IID_IAccessibleTableCell ||
2797 guidService == IID_IAccessibleText ||
2798 guidService == IID_IAccessibleValue ||
2799 guidService == IID_ISimpleDOMDocument ||
2800 guidService == IID_ISimpleDOMNode ||
2801 guidService == IID_ISimpleDOMText ||
2802 guidService == GUID_ISimpleDOM) {
2803 return QueryInterface(riid, object);
2804 }
2805
2806 // We only support the IAccessibleEx interface on Windows 8 and above. This
2807 // is needed for the on-screen Keyboard to show up in metro mode, when the
2808 // user taps an editable portion on the page.
2809 // All methods in the IAccessibleEx interface are unimplemented.
2810 if (riid == IID_IAccessibleEx &&
2811 base::win::GetVersion() >= base::win::VERSION_WIN8) {
2812 return QueryInterface(riid, object);
2813 }
2814
2815 *object = NULL;
2816 return E_FAIL;
2817 }
2818
GetPatternProvider(PATTERNID id,IUnknown ** provider)2819 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2820 IUnknown** provider) {
2821 DVLOG(1) << "In Function: "
2822 << __FUNCTION__
2823 << " for pattern id: "
2824 << id;
2825 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2826 if (IsEditableText()) {
2827 DVLOG(1) << "Returning UIA text provider";
2828 base::win::UIATextProvider::CreateTextProvider(
2829 GetValueText(), true, provider);
2830 return S_OK;
2831 }
2832 }
2833 return E_NOTIMPL;
2834 }
2835
GetPropertyValue(PROPERTYID id,VARIANT * ret)2836 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2837 VARIANT* ret) {
2838 DVLOG(1) << "In Function: "
2839 << __FUNCTION__
2840 << " for property id: "
2841 << id;
2842 V_VT(ret) = VT_EMPTY;
2843 if (id == UIA_ControlTypePropertyId) {
2844 if (IsEditableText()) {
2845 V_VT(ret) = VT_I4;
2846 ret->lVal = UIA_EditControlTypeId;
2847 DVLOG(1) << "Returning Edit control type";
2848 } else {
2849 DVLOG(1) << "Returning empty control type";
2850 }
2851 }
2852 return S_OK;
2853 }
2854
2855 //
2856 // CComObjectRootEx methods.
2857 //
2858
InternalQueryInterface(void * this_ptr,const _ATL_INTMAP_ENTRY * entries,REFIID iid,void ** object)2859 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2860 void* this_ptr,
2861 const _ATL_INTMAP_ENTRY* entries,
2862 REFIID iid,
2863 void** object) {
2864 if (iid == IID_IAccessibleImage) {
2865 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
2866 *object = NULL;
2867 return E_NOINTERFACE;
2868 }
2869 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2870 if (ia_role_ != ROLE_SYSTEM_TABLE) {
2871 *object = NULL;
2872 return E_NOINTERFACE;
2873 }
2874 } else if (iid == IID_IAccessibleTableCell) {
2875 if (ia_role_ != ROLE_SYSTEM_CELL) {
2876 *object = NULL;
2877 return E_NOINTERFACE;
2878 }
2879 } else if (iid == IID_IAccessibleValue) {
2880 if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR &&
2881 ia_role_ != ROLE_SYSTEM_SCROLLBAR &&
2882 ia_role_ != ROLE_SYSTEM_SLIDER) {
2883 *object = NULL;
2884 return E_NOINTERFACE;
2885 }
2886 } else if (iid == IID_ISimpleDOMDocument) {
2887 if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
2888 *object = NULL;
2889 return E_NOINTERFACE;
2890 }
2891 }
2892
2893 return CComObjectRootBase::InternalQueryInterface(
2894 this_ptr, entries, iid, object);
2895 }
2896
2897 //
2898 // Private methods.
2899 //
2900
2901 // Called every time this node's data changes.
OnDataChanged()2902 void BrowserAccessibilityWin::OnDataChanged() {
2903 BrowserAccessibility::OnDataChanged();
2904
2905 InitRoleAndState();
2906
2907 // Expose the "display" and "tag" attributes.
2908 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
2909 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
2910 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
2911
2912 // Expose "level" attribute for headings, trees, etc.
2913 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
2914
2915 // Expose the set size and position in set for listbox options.
2916 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
2917 GetParent() &&
2918 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
2919 ia2_attributes_.push_back(
2920 L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
2921 ia2_attributes_.push_back(
2922 L"setsize:" + base::IntToString16(GetIndexInParent() + 1));
2923 }
2924
2925 if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
2926 ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
2927 ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
2928 ia2_attributes_.push_back(L"checkable:true");
2929 }
2930
2931 // Expose live region attributes.
2932 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
2933 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
2934 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
2935 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
2936
2937 // Expose container live region attributes.
2938 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
2939 "container-live");
2940 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
2941 "container-relevant");
2942 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
2943 "container-atomic");
2944 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
2945 "container-busy");
2946
2947 // Expose slider value.
2948 if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
2949 ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
2950 ia_role_ == ROLE_SYSTEM_SLIDER) {
2951 ia2_attributes_.push_back(L"valuetext:" + GetValueText());
2952 }
2953
2954 // Expose table cell index.
2955 if (ia_role_ == ROLE_SYSTEM_CELL) {
2956 BrowserAccessibility* table = GetParent();
2957 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
2958 table = table->GetParent();
2959 if (table) {
2960 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
2961 ui::AX_ATTR_UNIQUE_CELL_IDS);
2962 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
2963 if (unique_cell_ids[i] == GetId()) {
2964 ia2_attributes_.push_back(
2965 base::string16(L"table-cell-index:") + base::IntToString16(i));
2966 }
2967 }
2968 }
2969 }
2970
2971 // The calculation of the accessible name of an element has been
2972 // standardized in the HTML to Platform Accessibility APIs Implementation
2973 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2974 // appropriate accessible name on Windows, we need to apply some logic
2975 // to the fields we get from WebKit.
2976 //
2977 // TODO(dmazzoni): move most of this logic into WebKit.
2978 //
2979 // WebKit gives us:
2980 //
2981 // name: the default name, e.g. inner text
2982 // title ui element: a reference to a <label> element on the same
2983 // page that labels this node.
2984 // description: accessible labels that override the default name:
2985 // aria-label or aria-labelledby or aria-describedby
2986 // help: the value of the "title" attribute
2987 //
2988 // On Windows, the logic we apply lets some fields take precedence and
2989 // always returns the primary name in "name" and the secondary name,
2990 // if any, in "description".
2991
2992 int title_elem_id = GetIntAttribute(
2993 ui::AX_ATTR_TITLE_UI_ELEMENT);
2994 std::string help = GetStringAttribute(ui::AX_ATTR_HELP);
2995 std::string description = GetStringAttribute(
2996 ui::AX_ATTR_DESCRIPTION);
2997
2998 // WebKit annoyingly puts the title in the description if there's no other
2999 // description, which just confuses the rest of the logic. Put it back.
3000 // Now "help" is always the value of the "title" attribute, if present.
3001 std::string title_attr;
3002 if (GetHtmlAttribute("title", &title_attr) &&
3003 description == title_attr &&
3004 help.empty()) {
3005 help = description;
3006 description.clear();
3007 }
3008
3009 // Now implement the main logic: the descripion should become the name if
3010 // it's nonempty, and the help should become the description if
3011 // there's no description - or the name if there's no name or description.
3012 if (!description.empty()) {
3013 set_name(description);
3014 description.clear();
3015 }
3016 if (!help.empty() && description.empty()) {
3017 description = help;
3018 help.clear();
3019 }
3020 if (!description.empty() && name().empty() && !title_elem_id) {
3021 set_name(description);
3022 description.clear();
3023 }
3024
3025 // If it's a text field, also consider the placeholder.
3026 std::string placeholder;
3027 if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
3028 HasState(ui::AX_STATE_FOCUSABLE) &&
3029 GetHtmlAttribute("placeholder", &placeholder)) {
3030 if (name().empty() && !title_elem_id) {
3031 set_name(placeholder);
3032 } else if (description.empty()) {
3033 description = placeholder;
3034 }
3035 }
3036
3037 SetStringAttribute(ui::AX_ATTR_DESCRIPTION, description);
3038 SetStringAttribute(ui::AX_ATTR_HELP, help);
3039
3040 // On Windows, the value of a document should be its url.
3041 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
3042 GetRole() == ui::AX_ROLE_WEB_AREA) {
3043 set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL));
3044 }
3045
3046 // For certain roles (listbox option, static text, and list marker)
3047 // WebKit stores the main accessible text in the "value" - swap it so
3048 // that it's the "name".
3049 if (name().empty() &&
3050 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
3051 GetRole() == ui::AX_ROLE_STATIC_TEXT ||
3052 GetRole() == ui::AX_ROLE_LIST_MARKER)) {
3053 std::string tmp = value();
3054 set_value(name());
3055 set_name(tmp);
3056 }
3057
3058 // If this doesn't have a value and is linked then set its value to the url
3059 // attribute. This allows screen readers to read an empty link's destination.
3060 if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
3061 set_value(GetStringAttribute(ui::AX_ATTR_URL));
3062
3063 // Clear any old relationships between this node and other nodes.
3064 for (size_t i = 0; i < relations_.size(); ++i)
3065 relations_[i]->Release();
3066 relations_.clear();
3067
3068 // Handle title UI element.
3069 if (title_elem_id) {
3070 // Add a labelled by relationship.
3071 CComObject<BrowserAccessibilityRelation>* relation;
3072 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3073 &relation);
3074 DCHECK(SUCCEEDED(hr));
3075 relation->AddRef();
3076 relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3077 relation->AddTarget(title_elem_id);
3078 relations_.push_back(relation);
3079 }
3080 }
3081
OnUpdateFinished()3082 void BrowserAccessibilityWin::OnUpdateFinished() {
3083 // Construct the hypertext for this node.
3084 hyperlink_offset_to_index_.clear();
3085 hyperlinks_.clear();
3086 hypertext_.clear();
3087 for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
3088 BrowserAccessibility* child = PlatformGetChild(i);
3089 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
3090 hypertext_ += base::UTF8ToUTF16(child->name());
3091 } else {
3092 hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size();
3093 hypertext_ += kEmbeddedCharacter;
3094 hyperlinks_.push_back(i);
3095 }
3096 }
3097 DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size());
3098
3099 // Fire an event when an alert first appears.
3100 if (GetRole() == ui::AX_ROLE_ALERT && first_time_)
3101 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
3102
3103 // Fire events if text has changed.
3104 base::string16 text = TextForIAccessibleText();
3105 if (previous_text_ != text) {
3106 if (!previous_text_.empty() && !text.empty()) {
3107 manager()->NotifyAccessibilityEvent(
3108 ui::AX_EVENT_SHOW, this);
3109 }
3110
3111 // TODO(dmazzoni): Look into HIDE events, too.
3112
3113 old_text_ = previous_text_;
3114 previous_text_ = text;
3115 }
3116
3117 BrowserAccessibilityManagerWin* manager =
3118 this->manager()->ToBrowserAccessibilityManagerWin();
3119
3120 // Fire events if the state has changed.
3121 if (!first_time_ && ia_state_ != old_ia_state_) {
3122 // Normally focus events are handled elsewhere, however
3123 // focus for managed descendants is platform-specific.
3124 // Fire a focus event if the focused descendant in a multi-select
3125 // list box changes.
3126 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3127 (ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
3128 (ia_state_ & STATE_SYSTEM_SELECTABLE) &&
3129 (ia_state_ & STATE_SYSTEM_FOCUSED) &&
3130 !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) {
3131 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, unique_id_win());
3132 }
3133
3134 if ((ia_state_ & STATE_SYSTEM_SELECTED) &&
3135 !(old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3136 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD,
3137 unique_id_win());
3138 } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) &&
3139 (old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3140 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE,
3141 unique_id_win());
3142 }
3143
3144 old_ia_state_ = ia_state_;
3145 }
3146
3147 // Fire an event if this container object has scrolled.
3148 int sx = 0;
3149 int sy = 0;
3150 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
3151 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
3152 if (!first_time_ &&
3153 (sx != previous_scroll_x_ || sy != previous_scroll_y_)) {
3154 manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND,
3155 unique_id_win());
3156 }
3157 previous_scroll_x_ = sx;
3158 previous_scroll_y_ = sy;
3159 }
3160
3161 first_time_ = false;
3162 }
3163
NativeAddReference()3164 void BrowserAccessibilityWin::NativeAddReference() {
3165 AddRef();
3166 }
3167
NativeReleaseReference()3168 void BrowserAccessibilityWin::NativeReleaseReference() {
3169 Release();
3170 }
3171
IsNative() const3172 bool BrowserAccessibilityWin::IsNative() const {
3173 return true;
3174 }
3175
OnLocationChanged() const3176 void BrowserAccessibilityWin::OnLocationChanged() const {
3177 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3178 EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
3179 }
3180
NewReference()3181 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3182 AddRef();
3183 return this;
3184 }
3185
GetTargetFromChildID(const VARIANT & var_id)3186 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3187 const VARIANT& var_id) {
3188 if (var_id.vt != VT_I4)
3189 return NULL;
3190
3191 LONG child_id = var_id.lVal;
3192 if (child_id == CHILDID_SELF)
3193 return this;
3194
3195 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3196 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3197
3198 return manager()->ToBrowserAccessibilityManagerWin()->
3199 GetFromUniqueIdWin(child_id);
3200 }
3201
GetStringAttributeAsBstr(ui::AXStringAttribute attribute,BSTR * value_bstr)3202 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3203 ui::AXStringAttribute attribute,
3204 BSTR* value_bstr) {
3205 base::string16 str;
3206
3207 if (!GetString16Attribute(attribute, &str))
3208 return S_FALSE;
3209
3210 if (str.empty())
3211 return S_FALSE;
3212
3213 *value_bstr = SysAllocString(str.c_str());
3214 DCHECK(*value_bstr);
3215
3216 return S_OK;
3217 }
3218
StringAttributeToIA2(ui::AXStringAttribute attribute,const char * ia2_attr)3219 void BrowserAccessibilityWin::StringAttributeToIA2(
3220 ui::AXStringAttribute attribute,
3221 const char* ia2_attr) {
3222 base::string16 value;
3223 if (GetString16Attribute(attribute, &value))
3224 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + value);
3225 }
3226
BoolAttributeToIA2(ui::AXBoolAttribute attribute,const char * ia2_attr)3227 void BrowserAccessibilityWin::BoolAttributeToIA2(
3228 ui::AXBoolAttribute attribute,
3229 const char* ia2_attr) {
3230 bool value;
3231 if (GetBoolAttribute(attribute, &value)) {
3232 ia2_attributes_.push_back((base::ASCIIToUTF16(ia2_attr) + L":") +
3233 (value ? L"true" : L"false"));
3234 }
3235 }
3236
IntAttributeToIA2(ui::AXIntAttribute attribute,const char * ia2_attr)3237 void BrowserAccessibilityWin::IntAttributeToIA2(
3238 ui::AXIntAttribute attribute,
3239 const char* ia2_attr) {
3240 int value;
3241 if (GetIntAttribute(attribute, &value)) {
3242 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" +
3243 base::IntToString16(value));
3244 }
3245 }
3246
GetValueText()3247 base::string16 BrowserAccessibilityWin::GetValueText() {
3248 float fval;
3249 base::string16 value = base::UTF8ToUTF16(this->value());
3250
3251 if (value.empty() &&
3252 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
3253 value = base::UTF8ToUTF16(base::DoubleToString(fval));
3254 }
3255 return value;
3256 }
3257
TextForIAccessibleText()3258 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3259 if (IsEditableText())
3260 return base::UTF8ToUTF16(value());
3261 return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ?
3262 base::UTF8ToUTF16(name()) : hypertext_;
3263 }
3264
HandleSpecialTextOffset(const base::string16 & text,LONG * offset)3265 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3266 const base::string16& text,
3267 LONG* offset) {
3268 if (*offset == IA2_TEXT_OFFSET_LENGTH)
3269 *offset = static_cast<LONG>(text.size());
3270 else if (*offset == IA2_TEXT_OFFSET_CARET)
3271 get_caretOffset(offset);
3272 }
3273
IA2TextBoundaryToTextBoundary(IA2TextBoundaryType ia2_boundary)3274 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3275 IA2TextBoundaryType ia2_boundary) {
3276 switch(ia2_boundary) {
3277 case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
3278 case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
3279 case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
3280 case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
3281 case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
3282 case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
3283 default:
3284 NOTREACHED();
3285 return ui::CHAR_BOUNDARY;
3286 }
3287 }
3288
FindBoundary(const base::string16 & text,IA2TextBoundaryType ia2_boundary,LONG start_offset,ui::TextBoundaryDirection direction)3289 LONG BrowserAccessibilityWin::FindBoundary(
3290 const base::string16& text,
3291 IA2TextBoundaryType ia2_boundary,
3292 LONG start_offset,
3293 ui::TextBoundaryDirection direction) {
3294 HandleSpecialTextOffset(text, &start_offset);
3295 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3296 const std::vector<int32>& line_breaks = GetIntListAttribute(
3297 ui::AX_ATTR_LINE_BREAKS);
3298 return ui::FindAccessibleTextBoundary(
3299 text, line_breaks, boundary, start_offset, direction);
3300 }
3301
GetFromID(int32 id)3302 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
3303 return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
3304 }
3305
InitRoleAndState()3306 void BrowserAccessibilityWin::InitRoleAndState() {
3307 ia_state_ = 0;
3308 ia2_state_ = IA2_STATE_OPAQUE;
3309 ia2_attributes_.clear();
3310
3311 if (HasState(ui::AX_STATE_BUSY))
3312 ia_state_ |= STATE_SYSTEM_BUSY;
3313 if (HasState(ui::AX_STATE_CHECKED))
3314 ia_state_ |= STATE_SYSTEM_CHECKED;
3315 if (HasState(ui::AX_STATE_COLLAPSED))
3316 ia_state_ |= STATE_SYSTEM_COLLAPSED;
3317 if (HasState(ui::AX_STATE_EXPANDED))
3318 ia_state_ |= STATE_SYSTEM_EXPANDED;
3319 if (HasState(ui::AX_STATE_FOCUSABLE))
3320 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3321 if (HasState(ui::AX_STATE_HASPOPUP))
3322 ia_state_ |= STATE_SYSTEM_HASPOPUP;
3323 if (HasState(ui::AX_STATE_HOVERED))
3324 ia_state_ |= STATE_SYSTEM_HOTTRACKED;
3325 if (HasState(ui::AX_STATE_INDETERMINATE))
3326 ia_state_ |= STATE_SYSTEM_INDETERMINATE;
3327 if (HasState(ui::AX_STATE_INVISIBLE))
3328 ia_state_ |= STATE_SYSTEM_INVISIBLE;
3329 if (HasState(ui::AX_STATE_LINKED))
3330 ia_state_ |= STATE_SYSTEM_LINKED;
3331 if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
3332 ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
3333 ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
3334 }
3335 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3336 if (HasState(ui::AX_STATE_OFFSCREEN))
3337 ia_state_ |= STATE_SYSTEM_OFFSCREEN;
3338 if (HasState(ui::AX_STATE_PRESSED))
3339 ia_state_ |= STATE_SYSTEM_PRESSED;
3340 if (HasState(ui::AX_STATE_PROTECTED))
3341 ia_state_ |= STATE_SYSTEM_PROTECTED;
3342 if (HasState(ui::AX_STATE_REQUIRED))
3343 ia2_state_ |= IA2_STATE_REQUIRED;
3344 if (HasState(ui::AX_STATE_SELECTABLE))
3345 ia_state_ |= STATE_SYSTEM_SELECTABLE;
3346 if (HasState(ui::AX_STATE_SELECTED))
3347 ia_state_ |= STATE_SYSTEM_SELECTED;
3348 if (HasState(ui::AX_STATE_VISITED))
3349 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3350 if (!HasState(ui::AX_STATE_ENABLED))
3351 ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
3352 if (HasState(ui::AX_STATE_VERTICAL)) {
3353 ia2_state_ |= IA2_STATE_VERTICAL;
3354 } else {
3355 ia2_state_ |= IA2_STATE_HORIZONTAL;
3356 }
3357 if (HasState(ui::AX_STATE_VISITED))
3358 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3359
3360 // WebKit marks everything as readonly unless it's editable text, so if it's
3361 // not readonly, mark it as editable now. The final computation of the
3362 // READONLY state for MSAA is below, after the switch.
3363 if (!HasState(ui::AX_STATE_READ_ONLY))
3364 ia2_state_ |= IA2_STATE_EDITABLE;
3365
3366 base::string16 invalid;
3367 if (GetHtmlAttribute("aria-invalid", &invalid))
3368 ia2_state_ |= IA2_STATE_INVALID_ENTRY;
3369
3370 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
3371 ia_state_ |= STATE_SYSTEM_MIXED;
3372
3373 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
3374 ia2_state_ |= IA2_STATE_EDITABLE;
3375
3376 base::string16 html_tag = GetString16Attribute(
3377 ui::AX_ATTR_HTML_TAG);
3378 ia_role_ = 0;
3379 ia2_role_ = 0;
3380 switch (GetRole()) {
3381 case ui::AX_ROLE_ALERT:
3382 ia_role_ = ROLE_SYSTEM_ALERT;
3383 break;
3384 case ui::AX_ROLE_ALERT_DIALOG:
3385 ia_role_ = ROLE_SYSTEM_DIALOG;
3386 break;
3387 case ui::AX_ROLE_APPLICATION:
3388 ia_role_ = ROLE_SYSTEM_APPLICATION;
3389 break;
3390 case ui::AX_ROLE_ARTICLE:
3391 ia_role_ = ROLE_SYSTEM_GROUPING;
3392 ia2_role_ = IA2_ROLE_SECTION;
3393 ia_state_ |= STATE_SYSTEM_READONLY;
3394 break;
3395 case ui::AX_ROLE_BUSY_INDICATOR:
3396 ia_role_ = ROLE_SYSTEM_ANIMATION;
3397 ia_state_ |= STATE_SYSTEM_READONLY;
3398 break;
3399 case ui::AX_ROLE_BUTTON:
3400 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3401 bool is_aria_pressed_defined;
3402 bool is_mixed;
3403 if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed))
3404 ia_state_ |= STATE_SYSTEM_PRESSED;
3405 if (is_aria_pressed_defined)
3406 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3407 if (is_mixed)
3408 ia_state_ |= STATE_SYSTEM_MIXED;
3409 break;
3410 case ui::AX_ROLE_CANVAS:
3411 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
3412 role_name_ = L"canvas";
3413 ia2_role_ = IA2_ROLE_CANVAS;
3414 } else {
3415 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3416 }
3417 break;
3418 case ui::AX_ROLE_CELL:
3419 ia_role_ = ROLE_SYSTEM_CELL;
3420 break;
3421 case ui::AX_ROLE_CHECK_BOX:
3422 ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
3423 break;
3424 case ui::AX_ROLE_COLOR_WELL:
3425 ia_role_ = ROLE_SYSTEM_CLIENT;
3426 ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
3427 break;
3428 case ui::AX_ROLE_COLUMN:
3429 ia_role_ = ROLE_SYSTEM_COLUMN;
3430 ia_state_ |= STATE_SYSTEM_READONLY;
3431 break;
3432 case ui::AX_ROLE_COLUMN_HEADER:
3433 ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
3434 ia_state_ |= STATE_SYSTEM_READONLY;
3435 break;
3436 case ui::AX_ROLE_COMBO_BOX:
3437 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3438 break;
3439 case ui::AX_ROLE_DIV:
3440 role_name_ = L"div";
3441 ia2_role_ = IA2_ROLE_SECTION;
3442 break;
3443 case ui::AX_ROLE_DEFINITION:
3444 role_name_ = html_tag;
3445 ia2_role_ = IA2_ROLE_PARAGRAPH;
3446 ia_state_ |= STATE_SYSTEM_READONLY;
3447 break;
3448 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
3449 role_name_ = html_tag;
3450 ia2_role_ = IA2_ROLE_PARAGRAPH;
3451 ia_state_ |= STATE_SYSTEM_READONLY;
3452 break;
3453 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
3454 ia_role_ = ROLE_SYSTEM_LISTITEM;
3455 ia_state_ |= STATE_SYSTEM_READONLY;
3456 break;
3457 case ui::AX_ROLE_DIALOG:
3458 ia_role_ = ROLE_SYSTEM_DIALOG;
3459 ia_state_ |= STATE_SYSTEM_READONLY;
3460 break;
3461 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
3462 ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
3463 ia_state_ |= STATE_SYSTEM_READONLY;
3464 break;
3465 case ui::AX_ROLE_DOCUMENT:
3466 case ui::AX_ROLE_ROOT_WEB_AREA:
3467 case ui::AX_ROLE_WEB_AREA:
3468 ia_role_ = ROLE_SYSTEM_DOCUMENT;
3469 ia_state_ |= STATE_SYSTEM_READONLY;
3470 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3471 break;
3472 case ui::AX_ROLE_EDITABLE_TEXT:
3473 ia_role_ = ROLE_SYSTEM_TEXT;
3474 ia2_state_ |= IA2_STATE_SINGLE_LINE;
3475 ia2_state_ |= IA2_STATE_EDITABLE;
3476 break;
3477 case ui::AX_ROLE_FORM:
3478 role_name_ = L"form";
3479 ia2_role_ = IA2_ROLE_FORM;
3480 break;
3481 case ui::AX_ROLE_FOOTER:
3482 ia_role_ = IA2_ROLE_FOOTER;
3483 ia_state_ |= STATE_SYSTEM_READONLY;
3484 break;
3485 case ui::AX_ROLE_GRID:
3486 ia_role_ = ROLE_SYSTEM_TABLE;
3487 ia_state_ |= STATE_SYSTEM_READONLY;
3488 break;
3489 case ui::AX_ROLE_GROUP: {
3490 base::string16 aria_role = GetString16Attribute(
3491 ui::AX_ATTR_ROLE);
3492 if (aria_role == L"group" || html_tag == L"fieldset") {
3493 ia_role_ = ROLE_SYSTEM_GROUPING;
3494 } else if (html_tag == L"li") {
3495 ia_role_ = ROLE_SYSTEM_LISTITEM;
3496 } else {
3497 if (html_tag.empty())
3498 role_name_ = L"div";
3499 else
3500 role_name_ = html_tag;
3501 ia2_role_ = IA2_ROLE_SECTION;
3502 }
3503 ia_state_ |= STATE_SYSTEM_READONLY;
3504 break;
3505 }
3506 case ui::AX_ROLE_GROW_AREA:
3507 ia_role_ = ROLE_SYSTEM_GRIP;
3508 ia_state_ |= STATE_SYSTEM_READONLY;
3509 break;
3510 case ui::AX_ROLE_HEADING:
3511 role_name_ = html_tag;
3512 ia2_role_ = IA2_ROLE_HEADING;
3513 ia_state_ |= STATE_SYSTEM_READONLY;
3514 break;
3515 case ui::AX_ROLE_HORIZONTAL_RULE:
3516 ia_role_ = ROLE_SYSTEM_SEPARATOR;
3517 break;
3518 case ui::AX_ROLE_IFRAME:
3519 ia_role_ = ROLE_SYSTEM_CLIENT;
3520 ia2_role_ = IA2_ROLE_INTERNAL_FRAME;
3521 break;
3522 case ui::AX_ROLE_IMAGE:
3523 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3524 ia_state_ |= STATE_SYSTEM_READONLY;
3525 break;
3526 case ui::AX_ROLE_IMAGE_MAP:
3527 role_name_ = html_tag;
3528 ia2_role_ = IA2_ROLE_IMAGE_MAP;
3529 ia_state_ |= STATE_SYSTEM_READONLY;
3530 break;
3531 case ui::AX_ROLE_IMAGE_MAP_LINK:
3532 ia_role_ = ROLE_SYSTEM_LINK;
3533 ia_state_ |= STATE_SYSTEM_LINKED;
3534 ia_state_ |= STATE_SYSTEM_READONLY;
3535 break;
3536 case ui::AX_ROLE_LABEL_TEXT:
3537 ia_role_ = ROLE_SYSTEM_TEXT;
3538 ia2_role_ = IA2_ROLE_LABEL;
3539 break;
3540 case ui::AX_ROLE_BANNER:
3541 case ui::AX_ROLE_COMPLEMENTARY:
3542 case ui::AX_ROLE_CONTENT_INFO:
3543 case ui::AX_ROLE_MAIN:
3544 case ui::AX_ROLE_NAVIGATION:
3545 case ui::AX_ROLE_SEARCH:
3546 ia_role_ = ROLE_SYSTEM_GROUPING;
3547 ia2_role_ = IA2_ROLE_SECTION;
3548 ia_state_ |= STATE_SYSTEM_READONLY;
3549 break;
3550 case ui::AX_ROLE_LINK:
3551 ia_role_ = ROLE_SYSTEM_LINK;
3552 ia_state_ |= STATE_SYSTEM_LINKED;
3553 break;
3554 case ui::AX_ROLE_LIST:
3555 ia_role_ = ROLE_SYSTEM_LIST;
3556 ia_state_ |= STATE_SYSTEM_READONLY;
3557 break;
3558 case ui::AX_ROLE_LIST_BOX:
3559 ia_role_ = ROLE_SYSTEM_LIST;
3560 break;
3561 case ui::AX_ROLE_LIST_BOX_OPTION:
3562 ia_role_ = ROLE_SYSTEM_LISTITEM;
3563 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3564 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3565 if (HasState(ui::AX_STATE_FOCUSED))
3566 ia_state_ |= STATE_SYSTEM_FOCUSED;
3567 }
3568 break;
3569 case ui::AX_ROLE_LIST_ITEM:
3570 ia_role_ = ROLE_SYSTEM_LISTITEM;
3571 ia_state_ |= STATE_SYSTEM_READONLY;
3572 break;
3573 case ui::AX_ROLE_MATH_ELEMENT:
3574 ia_role_ = ROLE_SYSTEM_EQUATION;
3575 ia_state_ |= STATE_SYSTEM_READONLY;
3576 break;
3577 case ui::AX_ROLE_MENU:
3578 case ui::AX_ROLE_MENU_BUTTON:
3579 ia_role_ = ROLE_SYSTEM_MENUPOPUP;
3580 break;
3581 case ui::AX_ROLE_MENU_BAR:
3582 ia_role_ = ROLE_SYSTEM_MENUBAR;
3583 break;
3584 case ui::AX_ROLE_MENU_ITEM:
3585 ia_role_ = ROLE_SYSTEM_MENUITEM;
3586 break;
3587 case ui::AX_ROLE_MENU_LIST_POPUP:
3588 ia_role_ = ROLE_SYSTEM_CLIENT;
3589 break;
3590 case ui::AX_ROLE_MENU_LIST_OPTION:
3591 ia_role_ = ROLE_SYSTEM_LISTITEM;
3592 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3593 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3594 if (HasState(ui::AX_STATE_FOCUSED))
3595 ia_state_ |= STATE_SYSTEM_FOCUSED;
3596 }
3597 break;
3598 case ui::AX_ROLE_NOTE:
3599 ia_role_ = ROLE_SYSTEM_GROUPING;
3600 ia2_role_ = IA2_ROLE_NOTE;
3601 ia_state_ |= STATE_SYSTEM_READONLY;
3602 break;
3603 case ui::AX_ROLE_OUTLINE:
3604 ia_role_ = ROLE_SYSTEM_OUTLINE;
3605 ia_state_ |= STATE_SYSTEM_READONLY;
3606 break;
3607 case ui::AX_ROLE_PARAGRAPH:
3608 role_name_ = L"P";
3609 ia2_role_ = IA2_ROLE_PARAGRAPH;
3610 break;
3611 case ui::AX_ROLE_POP_UP_BUTTON:
3612 if (html_tag == L"select") {
3613 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3614 } else {
3615 ia_role_ = ROLE_SYSTEM_BUTTONMENU;
3616 }
3617 break;
3618 case ui::AX_ROLE_PROGRESS_INDICATOR:
3619 ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3620 ia_state_ |= STATE_SYSTEM_READONLY;
3621 break;
3622 case ui::AX_ROLE_RADIO_BUTTON:
3623 ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
3624 break;
3625 case ui::AX_ROLE_RADIO_GROUP:
3626 ia_role_ = ROLE_SYSTEM_GROUPING;
3627 ia2_role_ = IA2_ROLE_SECTION;
3628 break;
3629 case ui::AX_ROLE_REGION:
3630 ia_role_ = ROLE_SYSTEM_GROUPING;
3631 ia2_role_ = IA2_ROLE_SECTION;
3632 ia_state_ |= STATE_SYSTEM_READONLY;
3633 break;
3634 case ui::AX_ROLE_ROW:
3635 ia_role_ = ROLE_SYSTEM_ROW;
3636 ia_state_ |= STATE_SYSTEM_READONLY;
3637 break;
3638 case ui::AX_ROLE_ROW_HEADER:
3639 ia_role_ = ROLE_SYSTEM_ROWHEADER;
3640 ia_state_ |= STATE_SYSTEM_READONLY;
3641 break;
3642 case ui::AX_ROLE_RULER:
3643 ia_role_ = ROLE_SYSTEM_CLIENT;
3644 ia2_role_ = IA2_ROLE_RULER;
3645 ia_state_ |= STATE_SYSTEM_READONLY;
3646 break;
3647 case ui::AX_ROLE_SCROLL_AREA:
3648 ia_role_ = ROLE_SYSTEM_CLIENT;
3649 ia2_role_ = IA2_ROLE_SCROLL_PANE;
3650 ia_state_ |= STATE_SYSTEM_READONLY;
3651 ia2_state_ &= ~(IA2_STATE_EDITABLE);
3652 break;
3653 case ui::AX_ROLE_SCROLL_BAR:
3654 ia_role_ = ROLE_SYSTEM_SCROLLBAR;
3655 break;
3656 case ui::AX_ROLE_SLIDER:
3657 ia_role_ = ROLE_SYSTEM_SLIDER;
3658 break;
3659 case ui::AX_ROLE_SPIN_BUTTON:
3660 ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3661 break;
3662 case ui::AX_ROLE_SPIN_BUTTON_PART:
3663 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3664 break;
3665 case ui::AX_ROLE_SPLIT_GROUP:
3666 ia_role_ = ROLE_SYSTEM_CLIENT;
3667 ia2_role_ = IA2_ROLE_SPLIT_PANE;
3668 ia_state_ |= STATE_SYSTEM_READONLY;
3669 break;
3670 case ui::AX_ROLE_ANNOTATION:
3671 case ui::AX_ROLE_LIST_MARKER:
3672 case ui::AX_ROLE_STATIC_TEXT:
3673 ia_role_ = ROLE_SYSTEM_STATICTEXT;
3674 break;
3675 case ui::AX_ROLE_STATUS:
3676 ia_role_ = ROLE_SYSTEM_STATUSBAR;
3677 ia_state_ |= STATE_SYSTEM_READONLY;
3678 break;
3679 case ui::AX_ROLE_SPLITTER:
3680 ia_role_ = ROLE_SYSTEM_SEPARATOR;
3681 break;
3682 case ui::AX_ROLE_SVG_ROOT:
3683 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3684 break;
3685 case ui::AX_ROLE_TAB:
3686 ia_role_ = ROLE_SYSTEM_PAGETAB;
3687 break;
3688 case ui::AX_ROLE_TABLE: {
3689 base::string16 aria_role = GetString16Attribute(
3690 ui::AX_ATTR_ROLE);
3691 if (aria_role == L"treegrid") {
3692 ia_role_ = ROLE_SYSTEM_OUTLINE;
3693 } else {
3694 ia_role_ = ROLE_SYSTEM_TABLE;
3695 ia_state_ |= STATE_SYSTEM_READONLY;
3696 }
3697 break;
3698 }
3699 case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
3700 ia_role_ = ROLE_SYSTEM_GROUPING;
3701 ia2_role_ = IA2_ROLE_SECTION;
3702 ia_state_ |= STATE_SYSTEM_READONLY;
3703 break;
3704 case ui::AX_ROLE_TAB_LIST:
3705 ia_role_ = ROLE_SYSTEM_PAGETABLIST;
3706 break;
3707 case ui::AX_ROLE_TAB_PANEL:
3708 ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
3709 break;
3710 case ui::AX_ROLE_TOGGLE_BUTTON:
3711 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3712 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3713 break;
3714 case ui::AX_ROLE_TEXT_AREA:
3715 ia_role_ = ROLE_SYSTEM_TEXT;
3716 ia2_state_ |= IA2_STATE_MULTI_LINE;
3717 ia2_state_ |= IA2_STATE_EDITABLE;
3718 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3719 break;
3720 case ui::AX_ROLE_TEXT_FIELD:
3721 ia_role_ = ROLE_SYSTEM_TEXT;
3722 ia2_state_ |= IA2_STATE_SINGLE_LINE;
3723 ia2_state_ |= IA2_STATE_EDITABLE;
3724 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3725 break;
3726 case ui::AX_ROLE_TIMER:
3727 ia_role_ = ROLE_SYSTEM_CLOCK;
3728 ia_state_ |= STATE_SYSTEM_READONLY;
3729 break;
3730 case ui::AX_ROLE_TOOLBAR:
3731 ia_role_ = ROLE_SYSTEM_TOOLBAR;
3732 ia_state_ |= STATE_SYSTEM_READONLY;
3733 break;
3734 case ui::AX_ROLE_TOOLTIP:
3735 ia_role_ = ROLE_SYSTEM_TOOLTIP;
3736 ia_state_ |= STATE_SYSTEM_READONLY;
3737 break;
3738 case ui::AX_ROLE_TREE:
3739 ia_role_ = ROLE_SYSTEM_OUTLINE;
3740 ia_state_ |= STATE_SYSTEM_READONLY;
3741 break;
3742 case ui::AX_ROLE_TREE_GRID:
3743 ia_role_ = ROLE_SYSTEM_OUTLINE;
3744 ia_state_ |= STATE_SYSTEM_READONLY;
3745 break;
3746 case ui::AX_ROLE_TREE_ITEM:
3747 ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
3748 ia_state_ |= STATE_SYSTEM_READONLY;
3749 break;
3750 case ui::AX_ROLE_WINDOW:
3751 ia_role_ = ROLE_SYSTEM_WINDOW;
3752 break;
3753
3754 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3755 case ui::AX_ROLE_BROWSER:
3756 case ui::AX_ROLE_DIRECTORY:
3757 case ui::AX_ROLE_DRAWER:
3758 case ui::AX_ROLE_HELP_TAG:
3759 case ui::AX_ROLE_IGNORED:
3760 case ui::AX_ROLE_INCREMENTOR:
3761 case ui::AX_ROLE_LOG:
3762 case ui::AX_ROLE_MARQUEE:
3763 case ui::AX_ROLE_MATTE:
3764 case ui::AX_ROLE_PRESENTATIONAL:
3765 case ui::AX_ROLE_RULER_MARKER:
3766 case ui::AX_ROLE_SHEET:
3767 case ui::AX_ROLE_SLIDER_THUMB:
3768 case ui::AX_ROLE_SYSTEM_WIDE:
3769 case ui::AX_ROLE_VALUE_INDICATOR:
3770 default:
3771 ia_role_ = ROLE_SYSTEM_CLIENT;
3772 break;
3773 }
3774
3775 // Compute the final value of READONLY for MSAA.
3776 //
3777 // We always set the READONLY state for elements that have the
3778 // aria-readonly attribute and for a few roles (in the switch above).
3779 // We clear the READONLY state on focusable controls and on a document.
3780 // Everything else, the majority of objects, do not have this state set.
3781 if (HasState(ui::AX_STATE_FOCUSABLE) &&
3782 ia_role_ != ROLE_SYSTEM_DOCUMENT) {
3783 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3784 }
3785 if (!HasState(ui::AX_STATE_READ_ONLY))
3786 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3787 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
3788 ia_state_ |= STATE_SYSTEM_READONLY;
3789
3790 // The role should always be set.
3791 DCHECK(!role_name_.empty() || ia_role_);
3792
3793 // If we didn't explicitly set the IAccessible2 role, make it the same
3794 // as the MSAA role.
3795 if (!ia2_role_)
3796 ia2_role_ = ia_role_;
3797 }
3798
3799 } // namespace content
3800