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/base/accessibility/accessible_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 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->GetFromRendererID(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->GetFromRendererID(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 AccessibilityNodeData::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 AccessibilityNodeData::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 AccessibilityNodeData::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 AccessibilityNodeData::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(AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT,
472 &title_elem_id)) {
473 BrowserAccessibility* title_elem =
474 manager()->GetFromRendererID(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(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 = parent()->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
503 // |parent| can only be NULL if the manager was created before the parent
504 // IAccessible was known and it wasn't subsequently set before a client
505 // requested it. Crash hard if this happens so that we get crash reports.
506 CHECK(parent_obj);
507 }
508
509 parent_obj->AddRef();
510 *disp_parent = parent_obj;
511 return S_OK;
512 }
513
get_accRole(VARIANT var_id,VARIANT * role)514 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
515 VARIANT* role) {
516 if (!instance_active())
517 return E_FAIL;
518
519 if (!role)
520 return E_INVALIDARG;
521
522 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
523 if (!target)
524 return E_INVALIDARG;
525
526 if (!target->role_name_.empty()) {
527 role->vt = VT_BSTR;
528 role->bstrVal = SysAllocString(target->role_name_.c_str());
529 } else {
530 role->vt = VT_I4;
531 role->lVal = target->ia_role_;
532 }
533 return S_OK;
534 }
535
get_accState(VARIANT var_id,VARIANT * state)536 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
537 VARIANT* state) {
538 if (!instance_active())
539 return E_FAIL;
540
541 if (!state)
542 return E_INVALIDARG;
543
544 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
545 if (!target)
546 return E_INVALIDARG;
547
548 state->vt = VT_I4;
549 state->lVal = target->ia_state_;
550 if (manager()->GetFocus(NULL) == this)
551 state->lVal |= STATE_SYSTEM_FOCUSED;
552
553 return S_OK;
554 }
555
get_accValue(VARIANT var_id,BSTR * value)556 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
557 BSTR* value) {
558 if (!instance_active())
559 return E_FAIL;
560
561 if (!value)
562 return E_INVALIDARG;
563
564 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
565 if (!target)
566 return E_INVALIDARG;
567
568 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
569 target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
570 target->ia_role() == ROLE_SYSTEM_SLIDER) {
571 base::string16 value_text = target->GetValueText();
572 *value = SysAllocString(value_text.c_str());
573 DCHECK(*value);
574 return S_OK;
575 }
576
577 // Expose color well value.
578 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
579 int r = target->GetIntAttribute(
580 AccessibilityNodeData::ATTR_COLOR_VALUE_RED);
581 int g = target->GetIntAttribute(
582 AccessibilityNodeData::ATTR_COLOR_VALUE_GREEN);
583 int b = target->GetIntAttribute(
584 AccessibilityNodeData::ATTR_COLOR_VALUE_BLUE);
585 base::string16 value_text;
586 value_text = base::IntToString16((r * 100) / 255) + L"% red " +
587 base::IntToString16((g * 100) / 255) + L"% green " +
588 base::IntToString16((b * 100) / 255) + L"% blue";
589 *value = SysAllocString(value_text.c_str());
590 DCHECK(*value);
591 return S_OK;
592 }
593
594 *value = SysAllocString(UTF8ToUTF16(target->value()).c_str());
595 DCHECK(*value);
596 return S_OK;
597 }
598
get_accHelpTopic(BSTR * help_file,VARIANT var_id,LONG * topic_id)599 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
600 VARIANT var_id,
601 LONG* topic_id) {
602 return E_NOTIMPL;
603 }
604
get_accSelection(VARIANT * selected)605 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
606 if (!instance_active())
607 return E_FAIL;
608
609 if (blink_role() != blink::WebAXRoleListBox)
610 return E_NOTIMPL;
611
612 unsigned long selected_count = 0;
613 for (size_t i = 0; i < children().size(); ++i) {
614 if (children()[i]->HasState(blink::WebAXStateSelected))
615 ++selected_count;
616 }
617
618 if (selected_count == 0) {
619 selected->vt = VT_EMPTY;
620 return S_OK;
621 }
622
623 if (selected_count == 1) {
624 for (size_t i = 0; i < children().size(); ++i) {
625 if (children()[i]->HasState(blink::WebAXStateSelected)) {
626 selected->vt = VT_DISPATCH;
627 selected->pdispVal =
628 children()[i]->ToBrowserAccessibilityWin()->NewReference();
629 return S_OK;
630 }
631 }
632 }
633
634 // Multiple items are selected.
635 base::win::EnumVariant* enum_variant =
636 new base::win::EnumVariant(selected_count);
637 enum_variant->AddRef();
638 unsigned long index = 0;
639 for (size_t i = 0; i < children().size(); ++i) {
640 if (children()[i]->HasState(blink::WebAXStateSelected)) {
641 enum_variant->ItemAt(index)->vt = VT_DISPATCH;
642 enum_variant->ItemAt(index)->pdispVal =
643 children()[i]->ToBrowserAccessibilityWin()->NewReference();
644 ++index;
645 }
646 }
647 selected->vt = VT_UNKNOWN;
648 selected->punkVal = static_cast<IUnknown*>(
649 static_cast<base::win::IUnknownImpl*>(enum_variant));
650 return S_OK;
651 }
652
accSelect(LONG flags_sel,VARIANT var_id)653 STDMETHODIMP BrowserAccessibilityWin::accSelect(
654 LONG flags_sel, VARIANT var_id) {
655 if (!instance_active())
656 return E_FAIL;
657
658 if (flags_sel & SELFLAG_TAKEFOCUS) {
659 manager()->SetFocus(this, true);
660 return S_OK;
661 }
662
663 return S_FALSE;
664 }
665
666 //
667 // IAccessible2 methods.
668 //
669
role(LONG * role)670 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
671 if (!instance_active())
672 return E_FAIL;
673
674 if (!role)
675 return E_INVALIDARG;
676
677 *role = ia2_role_;
678
679 return S_OK;
680 }
681
get_attributes(BSTR * attributes)682 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
683 if (!instance_active())
684 return E_FAIL;
685
686 if (!attributes)
687 return E_INVALIDARG;
688
689 // The iaccessible2 attributes are a set of key-value pairs
690 // separated by semicolons, with a colon between the key and the value.
691 base::string16 str;
692 for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) {
693 if (i != 0)
694 str += L';';
695 str += ia2_attributes_[i];
696 }
697
698 if (str.empty())
699 return S_FALSE;
700
701 *attributes = SysAllocString(str.c_str());
702 DCHECK(*attributes);
703 return S_OK;
704 }
705
get_states(AccessibleStates * states)706 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
707 if (!instance_active())
708 return E_FAIL;
709
710 if (!states)
711 return E_INVALIDARG;
712
713 *states = ia2_state_;
714
715 return S_OK;
716 }
717
get_uniqueID(LONG * unique_id)718 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
719 if (!instance_active())
720 return E_FAIL;
721
722 if (!unique_id)
723 return E_INVALIDARG;
724
725 *unique_id = unique_id_win_;
726 return S_OK;
727 }
728
get_windowHandle(HWND * window_handle)729 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
730 if (!instance_active())
731 return E_FAIL;
732
733 if (!window_handle)
734 return E_INVALIDARG;
735
736 *window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
737 return S_OK;
738 }
739
get_indexInParent(LONG * index_in_parent)740 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
741 if (!instance_active())
742 return E_FAIL;
743
744 if (!index_in_parent)
745 return E_INVALIDARG;
746
747 *index_in_parent = this->index_in_parent();
748 return S_OK;
749 }
750
get_nRelations(LONG * n_relations)751 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
752 if (!instance_active())
753 return E_FAIL;
754
755 if (!n_relations)
756 return E_INVALIDARG;
757
758 *n_relations = relations_.size();
759 return S_OK;
760 }
761
get_relation(LONG relation_index,IAccessibleRelation ** relation)762 STDMETHODIMP BrowserAccessibilityWin::get_relation(
763 LONG relation_index,
764 IAccessibleRelation** relation) {
765 if (!instance_active())
766 return E_FAIL;
767
768 if (relation_index < 0 ||
769 relation_index >= static_cast<long>(relations_.size())) {
770 return E_INVALIDARG;
771 }
772
773 if (!relation)
774 return E_INVALIDARG;
775
776 relations_[relation_index]->AddRef();
777 *relation = relations_[relation_index];
778 return S_OK;
779 }
780
get_relations(LONG max_relations,IAccessibleRelation ** relations,LONG * n_relations)781 STDMETHODIMP BrowserAccessibilityWin::get_relations(
782 LONG max_relations,
783 IAccessibleRelation** relations,
784 LONG* n_relations) {
785 if (!instance_active())
786 return E_FAIL;
787
788 if (!relations || !n_relations)
789 return E_INVALIDARG;
790
791 long count = static_cast<long>(relations_.size());
792 *n_relations = count;
793 if (count == 0)
794 return S_FALSE;
795
796 for (long i = 0; i < count; ++i) {
797 relations_[i]->AddRef();
798 relations[i] = relations_[i];
799 }
800
801 return S_OK;
802 }
803
scrollTo(enum IA2ScrollType scroll_type)804 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
805 if (!instance_active())
806 return E_FAIL;
807
808 gfx::Rect r = location();
809 switch(scroll_type) {
810 case IA2_SCROLL_TYPE_TOP_LEFT:
811 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
812 break;
813 case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
814 manager()->ScrollToMakeVisible(
815 *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
816 break;
817 case IA2_SCROLL_TYPE_TOP_EDGE:
818 manager()->ScrollToMakeVisible(
819 *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
820 break;
821 case IA2_SCROLL_TYPE_BOTTOM_EDGE:
822 manager()->ScrollToMakeVisible(
823 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
824 break;
825 case IA2_SCROLL_TYPE_LEFT_EDGE:
826 manager()->ScrollToMakeVisible(
827 *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
828 break;
829 case IA2_SCROLL_TYPE_RIGHT_EDGE:
830 manager()->ScrollToMakeVisible(
831 *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
832 break;
833 case IA2_SCROLL_TYPE_ANYWHERE:
834 default:
835 manager()->ScrollToMakeVisible(*this, r);
836 break;
837 }
838
839 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
840
841 return S_OK;
842 }
843
scrollToPoint(enum IA2CoordinateType coordinate_type,LONG x,LONG y)844 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
845 enum IA2CoordinateType coordinate_type,
846 LONG x,
847 LONG y) {
848 if (!instance_active())
849 return E_FAIL;
850
851 gfx::Point scroll_to(x, y);
852
853 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
854 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
855 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
856 if (parent())
857 scroll_to += parent()->location().OffsetFromOrigin();
858 } else {
859 return E_INVALIDARG;
860 }
861
862 manager()->ScrollToPoint(*this, scroll_to);
863 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
864
865 return S_OK;
866 }
867
get_groupPosition(LONG * group_level,LONG * similar_items_in_group,LONG * position_in_group)868 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
869 LONG* group_level,
870 LONG* similar_items_in_group,
871 LONG* position_in_group) {
872 if (!instance_active())
873 return E_FAIL;
874
875 if (!group_level || !similar_items_in_group || !position_in_group)
876 return E_INVALIDARG;
877
878 if (blink_role() == blink::WebAXRoleListBoxOption &&
879 parent() &&
880 parent()->role() == blink::WebAXRoleListBox) {
881 *group_level = 0;
882 *similar_items_in_group = parent()->PlatformChildCount();
883 *position_in_group = index_in_parent() + 1;
884 return S_OK;
885 }
886
887 return E_NOTIMPL;
888 }
889
890 //
891 // IAccessibleApplication methods.
892 //
893
get_appName(BSTR * app_name)894 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
895 // No need to check |instance_active()| because this interface is
896 // global, and doesn't depend on any local state.
897
898 if (!app_name)
899 return E_INVALIDARG;
900
901 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
902 // the part before the "/".
903 std::vector<std::string> product_components;
904 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
905 DCHECK_EQ(2U, product_components.size());
906 if (product_components.size() != 2)
907 return E_FAIL;
908 *app_name = SysAllocString(UTF8ToUTF16(product_components[0]).c_str());
909 DCHECK(*app_name);
910 return *app_name ? S_OK : E_FAIL;
911 }
912
get_appVersion(BSTR * app_version)913 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
914 // No need to check |instance_active()| because this interface is
915 // global, and doesn't depend on any local state.
916
917 if (!app_version)
918 return E_INVALIDARG;
919
920 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
921 // the part after the "/".
922 std::vector<std::string> product_components;
923 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
924 DCHECK_EQ(2U, product_components.size());
925 if (product_components.size() != 2)
926 return E_FAIL;
927 *app_version = SysAllocString(UTF8ToUTF16(product_components[1]).c_str());
928 DCHECK(*app_version);
929 return *app_version ? S_OK : E_FAIL;
930 }
931
get_toolkitName(BSTR * toolkit_name)932 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
933 // No need to check |instance_active()| because this interface is
934 // global, and doesn't depend on any local state.
935
936 if (!toolkit_name)
937 return E_INVALIDARG;
938
939 // This is hard-coded; all products based on the Chromium engine
940 // will have the same toolkit name, so that assistive technology can
941 // detect any Chrome-based product.
942 *toolkit_name = SysAllocString(L"Chrome");
943 DCHECK(*toolkit_name);
944 return *toolkit_name ? S_OK : E_FAIL;
945 }
946
get_toolkitVersion(BSTR * toolkit_version)947 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
948 BSTR* toolkit_version) {
949 // No need to check |instance_active()| because this interface is
950 // global, and doesn't depend on any local state.
951
952 if (!toolkit_version)
953 return E_INVALIDARG;
954
955 std::string user_agent = GetContentClient()->GetUserAgent();
956 *toolkit_version = SysAllocString(UTF8ToUTF16(user_agent).c_str());
957 DCHECK(*toolkit_version);
958 return *toolkit_version ? S_OK : E_FAIL;
959 }
960
961 //
962 // IAccessibleImage methods.
963 //
964
get_description(BSTR * desc)965 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
966 if (!instance_active())
967 return E_FAIL;
968
969 if (!desc)
970 return E_INVALIDARG;
971
972 return GetStringAttributeAsBstr(
973 AccessibilityNodeData::ATTR_DESCRIPTION, desc);
974 }
975
get_imagePosition(enum IA2CoordinateType coordinate_type,LONG * x,LONG * y)976 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
977 enum IA2CoordinateType coordinate_type,
978 LONG* x,
979 LONG* y) {
980 if (!instance_active())
981 return E_FAIL;
982
983 if (!x || !y)
984 return E_INVALIDARG;
985
986 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
987 HWND parent_hwnd =
988 manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
989 POINT top_left = {0, 0};
990 ::ClientToScreen(parent_hwnd, &top_left);
991 *x = location().x() + top_left.x;
992 *y = location().y() + top_left.y;
993 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
994 *x = location().x();
995 *y = location().y();
996 if (parent()) {
997 *x -= parent()->location().x();
998 *y -= parent()->location().y();
999 }
1000 } else {
1001 return E_INVALIDARG;
1002 }
1003
1004 return S_OK;
1005 }
1006
get_imageSize(LONG * height,LONG * width)1007 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1008 if (!instance_active())
1009 return E_FAIL;
1010
1011 if (!height || !width)
1012 return E_INVALIDARG;
1013
1014 *height = location().height();
1015 *width = location().width();
1016 return S_OK;
1017 }
1018
1019 //
1020 // IAccessibleTable methods.
1021 //
1022
get_accessibleAt(long row,long column,IUnknown ** accessible)1023 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1024 long row,
1025 long column,
1026 IUnknown** accessible) {
1027 if (!instance_active())
1028 return E_FAIL;
1029
1030 if (!accessible)
1031 return E_INVALIDARG;
1032
1033 int columns;
1034 int rows;
1035 if (!GetIntAttribute(
1036 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1037 !GetIntAttribute(
1038 AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1039 columns <= 0 ||
1040 rows <= 0) {
1041 return S_FALSE;
1042 }
1043
1044 if (row < 0 || row >= rows || column < 0 || column >= columns)
1045 return E_INVALIDARG;
1046
1047 const std::vector<int32>& cell_ids = GetIntListAttribute(
1048 AccessibilityNodeData::ATTR_CELL_IDS);
1049 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1050
1051 int cell_id = cell_ids[row * columns + column];
1052 BrowserAccessibilityWin* cell = GetFromRendererID(cell_id);
1053 if (cell) {
1054 *accessible = static_cast<IAccessible*>(cell->NewReference());
1055 return S_OK;
1056 }
1057
1058 *accessible = NULL;
1059 return E_INVALIDARG;
1060 }
1061
get_caption(IUnknown ** accessible)1062 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1063 if (!instance_active())
1064 return E_FAIL;
1065
1066 if (!accessible)
1067 return E_INVALIDARG;
1068
1069 // TODO(dmazzoni): implement
1070 return S_FALSE;
1071 }
1072
get_childIndex(long row,long column,long * cell_index)1073 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1074 long column,
1075 long* cell_index) {
1076 if (!instance_active())
1077 return E_FAIL;
1078
1079 if (!cell_index)
1080 return E_INVALIDARG;
1081
1082 int columns;
1083 int rows;
1084 if (!GetIntAttribute(
1085 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1086 !GetIntAttribute(
1087 AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1088 columns <= 0 ||
1089 rows <= 0) {
1090 return S_FALSE;
1091 }
1092
1093 if (row < 0 || row >= rows || column < 0 || column >= columns)
1094 return E_INVALIDARG;
1095
1096 const std::vector<int32>& cell_ids = GetIntListAttribute(
1097 AccessibilityNodeData::ATTR_CELL_IDS);
1098 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1099 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1100 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1101 int cell_id = cell_ids[row * columns + column];
1102 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1103 if (unique_cell_ids[i] == cell_id) {
1104 *cell_index = (long)i;
1105 return S_OK;
1106 }
1107 }
1108
1109 return S_FALSE;
1110 }
1111
get_columnDescription(long column,BSTR * description)1112 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1113 BSTR* description) {
1114 if (!instance_active())
1115 return E_FAIL;
1116
1117 if (!description)
1118 return E_INVALIDARG;
1119
1120 int columns;
1121 int rows;
1122 if (!GetIntAttribute(
1123 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1124 !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1125 columns <= 0 ||
1126 rows <= 0) {
1127 return S_FALSE;
1128 }
1129
1130 if (column < 0 || column >= columns)
1131 return E_INVALIDARG;
1132
1133 const std::vector<int32>& cell_ids = GetIntListAttribute(
1134 AccessibilityNodeData::ATTR_CELL_IDS);
1135 for (int i = 0; i < rows; ++i) {
1136 int cell_id = cell_ids[i * columns + column];
1137 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1138 manager()->GetFromRendererID(cell_id));
1139 if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader) {
1140 base::string16 cell_name = cell->GetString16Attribute(
1141 AccessibilityNodeData::ATTR_NAME);
1142 if (cell_name.size() > 0) {
1143 *description = SysAllocString(cell_name.c_str());
1144 return S_OK;
1145 }
1146
1147 return cell->GetStringAttributeAsBstr(
1148 AccessibilityNodeData::ATTR_DESCRIPTION, description);
1149 }
1150 }
1151
1152 return S_FALSE;
1153 }
1154
get_columnExtentAt(long row,long column,long * n_columns_spanned)1155 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1156 long row,
1157 long column,
1158 long* n_columns_spanned) {
1159 if (!instance_active())
1160 return E_FAIL;
1161
1162 if (!n_columns_spanned)
1163 return E_INVALIDARG;
1164
1165 int columns;
1166 int rows;
1167 if (!GetIntAttribute(
1168 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1169 !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1170 columns <= 0 ||
1171 rows <= 0) {
1172 return S_FALSE;
1173 }
1174
1175 if (row < 0 || row >= rows || column < 0 || column >= columns)
1176 return E_INVALIDARG;
1177
1178 const std::vector<int32>& cell_ids = GetIntListAttribute(
1179 AccessibilityNodeData::ATTR_CELL_IDS);
1180 int cell_id = cell_ids[row * columns + column];
1181 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1182 manager()->GetFromRendererID(cell_id));
1183 int colspan;
1184 if (cell &&
1185 cell->GetIntAttribute(
1186 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1187 colspan >= 1) {
1188 *n_columns_spanned = colspan;
1189 return S_OK;
1190 }
1191
1192 return S_FALSE;
1193 }
1194
get_columnHeader(IAccessibleTable ** accessible_table,long * starting_row_index)1195 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1196 IAccessibleTable** accessible_table,
1197 long* starting_row_index) {
1198 // TODO(dmazzoni): implement
1199 return E_NOTIMPL;
1200 }
1201
get_columnIndex(long cell_index,long * column_index)1202 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1203 long* column_index) {
1204 if (!instance_active())
1205 return E_FAIL;
1206
1207 if (!column_index)
1208 return E_INVALIDARG;
1209
1210 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1211 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1212 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1213 if (cell_index < 0)
1214 return E_INVALIDARG;
1215 if (cell_index >= cell_id_count)
1216 return S_FALSE;
1217
1218 int cell_id = unique_cell_ids[cell_index];
1219 BrowserAccessibilityWin* cell =
1220 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1221 int col_index;
1222 if (cell &&
1223 cell->GetIntAttribute(
1224 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1225 *column_index = col_index;
1226 return S_OK;
1227 }
1228
1229 return S_FALSE;
1230 }
1231
get_nColumns(long * column_count)1232 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1233 if (!instance_active())
1234 return E_FAIL;
1235
1236 if (!column_count)
1237 return E_INVALIDARG;
1238
1239 int columns;
1240 if (GetIntAttribute(
1241 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns)) {
1242 *column_count = columns;
1243 return S_OK;
1244 }
1245
1246 return S_FALSE;
1247 }
1248
get_nRows(long * row_count)1249 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1250 if (!instance_active())
1251 return E_FAIL;
1252
1253 if (!row_count)
1254 return E_INVALIDARG;
1255
1256 int rows;
1257 if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1258 *row_count = rows;
1259 return S_OK;
1260 }
1261
1262 return S_FALSE;
1263 }
1264
get_nSelectedChildren(long * cell_count)1265 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1266 if (!instance_active())
1267 return E_FAIL;
1268
1269 if (!cell_count)
1270 return E_INVALIDARG;
1271
1272 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1273 *cell_count = 0;
1274 return S_OK;
1275 }
1276
get_nSelectedColumns(long * column_count)1277 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1278 if (!instance_active())
1279 return E_FAIL;
1280
1281 if (!column_count)
1282 return E_INVALIDARG;
1283
1284 *column_count = 0;
1285 return S_OK;
1286 }
1287
get_nSelectedRows(long * row_count)1288 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1289 if (!instance_active())
1290 return E_FAIL;
1291
1292 if (!row_count)
1293 return E_INVALIDARG;
1294
1295 *row_count = 0;
1296 return S_OK;
1297 }
1298
get_rowDescription(long row,BSTR * description)1299 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1300 BSTR* description) {
1301 if (!instance_active())
1302 return E_FAIL;
1303
1304 if (!description)
1305 return E_INVALIDARG;
1306
1307 int columns;
1308 int rows;
1309 if (!GetIntAttribute(
1310 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1311 !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1312 columns <= 0 ||
1313 rows <= 0) {
1314 return S_FALSE;
1315 }
1316
1317 if (row < 0 || row >= rows)
1318 return E_INVALIDARG;
1319
1320 const std::vector<int32>& cell_ids = GetIntListAttribute(
1321 AccessibilityNodeData::ATTR_CELL_IDS);
1322 for (int i = 0; i < columns; ++i) {
1323 int cell_id = cell_ids[row * columns + i];
1324 BrowserAccessibilityWin* cell =
1325 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1326 if (cell && cell->blink_role() == blink::WebAXRoleRowHeader) {
1327 base::string16 cell_name = cell->GetString16Attribute(
1328 AccessibilityNodeData::ATTR_NAME);
1329 if (cell_name.size() > 0) {
1330 *description = SysAllocString(cell_name.c_str());
1331 return S_OK;
1332 }
1333
1334 return cell->GetStringAttributeAsBstr(
1335 AccessibilityNodeData::ATTR_DESCRIPTION, description);
1336 }
1337 }
1338
1339 return S_FALSE;
1340 }
1341
get_rowExtentAt(long row,long column,long * n_rows_spanned)1342 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1343 long column,
1344 long* n_rows_spanned) {
1345 if (!instance_active())
1346 return E_FAIL;
1347
1348 if (!n_rows_spanned)
1349 return E_INVALIDARG;
1350
1351 int columns;
1352 int rows;
1353 if (!GetIntAttribute(
1354 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1355 !GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows) ||
1356 columns <= 0 ||
1357 rows <= 0) {
1358 return S_FALSE;
1359 }
1360
1361 if (row < 0 || row >= rows || column < 0 || column >= columns)
1362 return E_INVALIDARG;
1363
1364 const std::vector<int32>& cell_ids = GetIntListAttribute(
1365 AccessibilityNodeData::ATTR_CELL_IDS);
1366 int cell_id = cell_ids[row * columns + column];
1367 BrowserAccessibilityWin* cell =
1368 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1369 int rowspan;
1370 if (cell &&
1371 cell->GetIntAttribute(
1372 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1373 rowspan >= 1) {
1374 *n_rows_spanned = rowspan;
1375 return S_OK;
1376 }
1377
1378 return S_FALSE;
1379 }
1380
get_rowHeader(IAccessibleTable ** accessible_table,long * starting_column_index)1381 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1382 IAccessibleTable** accessible_table,
1383 long* starting_column_index) {
1384 // TODO(dmazzoni): implement
1385 return E_NOTIMPL;
1386 }
1387
get_rowIndex(long cell_index,long * row_index)1388 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1389 long* row_index) {
1390 if (!instance_active())
1391 return E_FAIL;
1392
1393 if (!row_index)
1394 return E_INVALIDARG;
1395
1396 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1397 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1398 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1399 if (cell_index < 0)
1400 return E_INVALIDARG;
1401 if (cell_index >= cell_id_count)
1402 return S_FALSE;
1403
1404 int cell_id = unique_cell_ids[cell_index];
1405 BrowserAccessibilityWin* cell =
1406 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1407 int cell_row_index;
1408 if (cell &&
1409 cell->GetIntAttribute(
1410 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1411 *row_index = cell_row_index;
1412 return S_OK;
1413 }
1414
1415 return S_FALSE;
1416 }
1417
get_selectedChildren(long max_children,long ** children,long * n_children)1418 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1419 long** children,
1420 long* n_children) {
1421 if (!instance_active())
1422 return E_FAIL;
1423
1424 if (!children || !n_children)
1425 return E_INVALIDARG;
1426
1427 // TODO(dmazzoni): Implement this.
1428 *n_children = 0;
1429 return S_OK;
1430 }
1431
get_selectedColumns(long max_columns,long ** columns,long * n_columns)1432 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1433 long** columns,
1434 long* n_columns) {
1435 if (!instance_active())
1436 return E_FAIL;
1437
1438 if (!columns || !n_columns)
1439 return E_INVALIDARG;
1440
1441 // TODO(dmazzoni): Implement this.
1442 *n_columns = 0;
1443 return S_OK;
1444 }
1445
get_selectedRows(long max_rows,long ** rows,long * n_rows)1446 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1447 long** rows,
1448 long* n_rows) {
1449 if (!instance_active())
1450 return E_FAIL;
1451
1452 if (!rows || !n_rows)
1453 return E_INVALIDARG;
1454
1455 // TODO(dmazzoni): Implement this.
1456 *n_rows = 0;
1457 return S_OK;
1458 }
1459
get_summary(IUnknown ** accessible)1460 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1461 if (!instance_active())
1462 return E_FAIL;
1463
1464 if (!accessible)
1465 return E_INVALIDARG;
1466
1467 // TODO(dmazzoni): implement
1468 return S_FALSE;
1469 }
1470
get_isColumnSelected(long column,boolean * is_selected)1471 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1472 long column,
1473 boolean* is_selected) {
1474 if (!instance_active())
1475 return E_FAIL;
1476
1477 if (!is_selected)
1478 return E_INVALIDARG;
1479
1480 // TODO(dmazzoni): Implement this.
1481 *is_selected = false;
1482 return S_OK;
1483 }
1484
get_isRowSelected(long row,boolean * is_selected)1485 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1486 boolean* is_selected) {
1487 if (!instance_active())
1488 return E_FAIL;
1489
1490 if (!is_selected)
1491 return E_INVALIDARG;
1492
1493 // TODO(dmazzoni): Implement this.
1494 *is_selected = false;
1495 return S_OK;
1496 }
1497
get_isSelected(long row,long column,boolean * is_selected)1498 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1499 long column,
1500 boolean* is_selected) {
1501 if (!instance_active())
1502 return E_FAIL;
1503
1504 if (!is_selected)
1505 return E_INVALIDARG;
1506
1507 // TODO(dmazzoni): Implement this.
1508 *is_selected = false;
1509 return S_OK;
1510 }
1511
get_rowColumnExtentsAtIndex(long index,long * row,long * column,long * row_extents,long * column_extents,boolean * is_selected)1512 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1513 long index,
1514 long* row,
1515 long* column,
1516 long* row_extents,
1517 long* column_extents,
1518 boolean* is_selected) {
1519 if (!instance_active())
1520 return E_FAIL;
1521
1522 if (!row || !column || !row_extents || !column_extents || !is_selected)
1523 return E_INVALIDARG;
1524
1525 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1526 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
1527 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1528 if (index < 0)
1529 return E_INVALIDARG;
1530 if (index >= cell_id_count)
1531 return S_FALSE;
1532
1533 int cell_id = unique_cell_ids[index];
1534 BrowserAccessibilityWin* cell =
1535 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1536 int rowspan;
1537 int colspan;
1538 if (cell &&
1539 cell->GetIntAttribute(
1540 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1541 cell->GetIntAttribute(
1542 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1543 rowspan >= 1 &&
1544 colspan >= 1) {
1545 *row_extents = rowspan;
1546 *column_extents = colspan;
1547 return S_OK;
1548 }
1549
1550 return S_FALSE;
1551 }
1552
1553 //
1554 // IAccessibleTable2 methods.
1555 //
1556
get_cellAt(long row,long column,IUnknown ** cell)1557 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1558 long column,
1559 IUnknown** cell) {
1560 return get_accessibleAt(row, column, cell);
1561 }
1562
get_nSelectedCells(long * cell_count)1563 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1564 return get_nSelectedChildren(cell_count);
1565 }
1566
get_selectedCells(IUnknown *** cells,long * n_selected_cells)1567 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1568 IUnknown*** cells,
1569 long* n_selected_cells) {
1570 if (!instance_active())
1571 return E_FAIL;
1572
1573 if (!cells || !n_selected_cells)
1574 return E_INVALIDARG;
1575
1576 // TODO(dmazzoni): Implement this.
1577 *n_selected_cells = 0;
1578 return S_OK;
1579 }
1580
get_selectedColumns(long ** columns,long * n_columns)1581 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1582 long* n_columns) {
1583 if (!instance_active())
1584 return E_FAIL;
1585
1586 if (!columns || !n_columns)
1587 return E_INVALIDARG;
1588
1589 // TODO(dmazzoni): Implement this.
1590 *n_columns = 0;
1591 return S_OK;
1592 }
1593
get_selectedRows(long ** rows,long * n_rows)1594 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1595 long* n_rows) {
1596 if (!instance_active())
1597 return E_FAIL;
1598
1599 if (!rows || !n_rows)
1600 return E_INVALIDARG;
1601
1602 // TODO(dmazzoni): Implement this.
1603 *n_rows = 0;
1604 return S_OK;
1605 }
1606
1607
1608 //
1609 // IAccessibleTableCell methods.
1610 //
1611
get_columnExtent(long * n_columns_spanned)1612 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1613 long* n_columns_spanned) {
1614 if (!instance_active())
1615 return E_FAIL;
1616
1617 if (!n_columns_spanned)
1618 return E_INVALIDARG;
1619
1620 int colspan;
1621 if (GetIntAttribute(
1622 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1623 colspan >= 1) {
1624 *n_columns_spanned = colspan;
1625 return S_OK;
1626 }
1627
1628 return S_FALSE;
1629 }
1630
get_columnHeaderCells(IUnknown *** cell_accessibles,long * n_column_header_cells)1631 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1632 IUnknown*** cell_accessibles,
1633 long* n_column_header_cells) {
1634 if (!instance_active())
1635 return E_FAIL;
1636
1637 if (!cell_accessibles || !n_column_header_cells)
1638 return E_INVALIDARG;
1639
1640 *n_column_header_cells = 0;
1641
1642 int column;
1643 if (!GetIntAttribute(
1644 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1645 return S_FALSE;
1646 }
1647
1648 BrowserAccessibility* table = parent();
1649 while (table && table->role() != blink::WebAXRoleTable)
1650 table = table->parent();
1651 if (!table) {
1652 NOTREACHED();
1653 return S_FALSE;
1654 }
1655
1656 int columns;
1657 int rows;
1658 if (!table->GetIntAttribute(
1659 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1660 !table->GetIntAttribute(
1661 AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1662 return S_FALSE;
1663 }
1664 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1665 return S_FALSE;
1666
1667 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1668 AccessibilityNodeData::ATTR_CELL_IDS);
1669
1670 for (int i = 0; i < rows; ++i) {
1671 int cell_id = cell_ids[i * columns + column];
1672 BrowserAccessibilityWin* cell =
1673 manager()->GetFromRendererID(cell_id)->ToBrowserAccessibilityWin();
1674 if (cell && cell->blink_role() == blink::WebAXRoleColumnHeader)
1675 (*n_column_header_cells)++;
1676 }
1677
1678 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1679 (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1680 int index = 0;
1681 for (int i = 0; i < rows; ++i) {
1682 int cell_id = cell_ids[i * columns + column];
1683 BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1684 if (cell && cell->role() == blink::WebAXRoleColumnHeader) {
1685 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1686 cell->ToBrowserAccessibilityWin()->NewReference());
1687 ++index;
1688 }
1689 }
1690
1691 return S_OK;
1692 }
1693
get_columnIndex(long * column_index)1694 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1695 if (!instance_active())
1696 return E_FAIL;
1697
1698 if (!column_index)
1699 return E_INVALIDARG;
1700
1701 int column;
1702 if (GetIntAttribute(
1703 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1704 *column_index = column;
1705 return S_OK;
1706 }
1707
1708 return S_FALSE;
1709 }
1710
get_rowExtent(long * n_rows_spanned)1711 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1712 if (!instance_active())
1713 return E_FAIL;
1714
1715 if (!n_rows_spanned)
1716 return E_INVALIDARG;
1717
1718 int rowspan;
1719 if (GetIntAttribute(
1720 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1721 rowspan >= 1) {
1722 *n_rows_spanned = rowspan;
1723 return S_OK;
1724 }
1725
1726 return S_FALSE;
1727 }
1728
get_rowHeaderCells(IUnknown *** cell_accessibles,long * n_row_header_cells)1729 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1730 IUnknown*** cell_accessibles,
1731 long* n_row_header_cells) {
1732 if (!instance_active())
1733 return E_FAIL;
1734
1735 if (!cell_accessibles || !n_row_header_cells)
1736 return E_INVALIDARG;
1737
1738 *n_row_header_cells = 0;
1739
1740 int row;
1741 if (!GetIntAttribute(
1742 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1743 return S_FALSE;
1744 }
1745
1746 BrowserAccessibility* table = parent();
1747 while (table && table->role() != blink::WebAXRoleTable)
1748 table = table->parent();
1749 if (!table) {
1750 NOTREACHED();
1751 return S_FALSE;
1752 }
1753
1754 int columns;
1755 int rows;
1756 if (!table->GetIntAttribute(
1757 AccessibilityNodeData::ATTR_TABLE_COLUMN_COUNT, &columns) ||
1758 !table->GetIntAttribute(
1759 AccessibilityNodeData::ATTR_TABLE_ROW_COUNT, &rows)) {
1760 return S_FALSE;
1761 }
1762 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1763 return S_FALSE;
1764
1765 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1766 AccessibilityNodeData::ATTR_CELL_IDS);
1767
1768 for (int i = 0; i < columns; ++i) {
1769 int cell_id = cell_ids[row * columns + i];
1770 BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1771 if (cell && cell->role() == blink::WebAXRoleRowHeader)
1772 (*n_row_header_cells)++;
1773 }
1774
1775 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1776 (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1777 int index = 0;
1778 for (int i = 0; i < columns; ++i) {
1779 int cell_id = cell_ids[row * columns + i];
1780 BrowserAccessibility* cell = manager()->GetFromRendererID(cell_id);
1781 if (cell && cell->role() == blink::WebAXRoleRowHeader) {
1782 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1783 cell->ToBrowserAccessibilityWin()->NewReference());
1784 ++index;
1785 }
1786 }
1787
1788 return S_OK;
1789 }
1790
get_rowIndex(long * row_index)1791 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1792 if (!instance_active())
1793 return E_FAIL;
1794
1795 if (!row_index)
1796 return E_INVALIDARG;
1797
1798 int row;
1799 if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1800 *row_index = row;
1801 return S_OK;
1802 }
1803 return S_FALSE;
1804 }
1805
get_isSelected(boolean * is_selected)1806 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1807 if (!instance_active())
1808 return E_FAIL;
1809
1810 if (!is_selected)
1811 return E_INVALIDARG;
1812
1813 *is_selected = false;
1814 return S_OK;
1815 }
1816
get_rowColumnExtents(long * row_index,long * column_index,long * row_extents,long * column_extents,boolean * is_selected)1817 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1818 long* row_index,
1819 long* column_index,
1820 long* row_extents,
1821 long* column_extents,
1822 boolean* is_selected) {
1823 if (!instance_active())
1824 return E_FAIL;
1825
1826 if (!row_index ||
1827 !column_index ||
1828 !row_extents ||
1829 !column_extents ||
1830 !is_selected) {
1831 return E_INVALIDARG;
1832 }
1833
1834 int row;
1835 int column;
1836 int rowspan;
1837 int colspan;
1838 if (GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1839 GetIntAttribute(
1840 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1841 GetIntAttribute(
1842 AccessibilityNodeData::ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1843 GetIntAttribute(
1844 AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1845 *row_index = row;
1846 *column_index = column;
1847 *row_extents = rowspan;
1848 *column_extents = colspan;
1849 *is_selected = false;
1850 return S_OK;
1851 }
1852
1853 return S_FALSE;
1854 }
1855
get_table(IUnknown ** table)1856 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1857 if (!instance_active())
1858 return E_FAIL;
1859
1860 if (!table)
1861 return E_INVALIDARG;
1862
1863
1864 int row;
1865 int column;
1866 GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_ROW_INDEX, &row);
1867 GetIntAttribute(AccessibilityNodeData::ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1868
1869 BrowserAccessibility* find_table = parent();
1870 while (find_table && find_table->role() != blink::WebAXRoleTable)
1871 find_table = find_table->parent();
1872 if (!find_table) {
1873 NOTREACHED();
1874 return S_FALSE;
1875 }
1876
1877 *table = static_cast<IAccessibleTable*>(
1878 find_table->ToBrowserAccessibilityWin()->NewReference());
1879
1880 return S_OK;
1881 }
1882
1883 //
1884 // IAccessibleText methods.
1885 //
1886
get_nCharacters(LONG * n_characters)1887 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1888 if (!instance_active())
1889 return E_FAIL;
1890
1891 if (!n_characters)
1892 return E_INVALIDARG;
1893
1894 *n_characters = TextForIAccessibleText().length();
1895 return S_OK;
1896 }
1897
get_caretOffset(LONG * offset)1898 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1899 if (!instance_active())
1900 return E_FAIL;
1901
1902 if (!offset)
1903 return E_INVALIDARG;
1904
1905 *offset = 0;
1906 if (blink_role() == blink::WebAXRoleTextField ||
1907 blink_role() == blink::WebAXRoleTextArea) {
1908 int sel_start = 0;
1909 if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
1910 &sel_start))
1911 *offset = sel_start;
1912 }
1913
1914 return S_OK;
1915 }
1916
get_characterExtents(LONG offset,enum IA2CoordinateType coordinate_type,LONG * out_x,LONG * out_y,LONG * out_width,LONG * out_height)1917 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1918 LONG offset,
1919 enum IA2CoordinateType coordinate_type,
1920 LONG* out_x,
1921 LONG* out_y,
1922 LONG* out_width,
1923 LONG* out_height) {
1924 if (!instance_active())
1925 return E_FAIL;
1926
1927 if (!out_x || !out_y || !out_width || !out_height)
1928 return E_INVALIDARG;
1929
1930 const base::string16& text_str = TextForIAccessibleText();
1931 HandleSpecialTextOffset(text_str, &offset);
1932
1933 if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1934 return E_INVALIDARG;
1935
1936 if (blink_role() != blink::WebAXRoleStaticText)
1937 return E_FAIL;
1938
1939 gfx::Rect character_bounds;
1940 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1941 character_bounds = GetGlobalBoundsForRange(offset, 1);
1942 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1943 character_bounds = GetLocalBoundsForRange(offset, 1);
1944 character_bounds -= location().OffsetFromOrigin();
1945 } else {
1946 return E_INVALIDARG;
1947 }
1948
1949 *out_x = character_bounds.x();
1950 *out_y = character_bounds.y();
1951 *out_width = character_bounds.width();
1952 *out_height = character_bounds.height();
1953
1954 return S_OK;
1955 }
1956
get_nSelections(LONG * n_selections)1957 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
1958 if (!instance_active())
1959 return E_FAIL;
1960
1961 if (!n_selections)
1962 return E_INVALIDARG;
1963
1964 *n_selections = 0;
1965 if (blink_role() == blink::WebAXRoleTextField ||
1966 blink_role() == blink::WebAXRoleTextArea) {
1967 int sel_start = 0;
1968 int sel_end = 0;
1969 if (GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START,
1970 &sel_start) &&
1971 GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end) &&
1972 sel_start != sel_end)
1973 *n_selections = 1;
1974 }
1975
1976 return S_OK;
1977 }
1978
get_selection(LONG selection_index,LONG * start_offset,LONG * end_offset)1979 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
1980 LONG* start_offset,
1981 LONG* end_offset) {
1982 if (!instance_active())
1983 return E_FAIL;
1984
1985 if (!start_offset || !end_offset || selection_index != 0)
1986 return E_INVALIDARG;
1987
1988 *start_offset = 0;
1989 *end_offset = 0;
1990 if (blink_role() == blink::WebAXRoleTextField ||
1991 blink_role() == blink::WebAXRoleTextArea) {
1992 int sel_start = 0;
1993 int sel_end = 0;
1994 if (GetIntAttribute(
1995 AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start) &&
1996 GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end)) {
1997 *start_offset = sel_start;
1998 *end_offset = sel_end;
1999 }
2000 }
2001
2002 return S_OK;
2003 }
2004
get_text(LONG start_offset,LONG end_offset,BSTR * text)2005 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2006 LONG end_offset,
2007 BSTR* text) {
2008 if (!instance_active())
2009 return E_FAIL;
2010
2011 if (!text)
2012 return E_INVALIDARG;
2013
2014 const base::string16& text_str = TextForIAccessibleText();
2015
2016 // Handle special text offsets.
2017 HandleSpecialTextOffset(text_str, &start_offset);
2018 HandleSpecialTextOffset(text_str, &end_offset);
2019
2020 // The spec allows the arguments to be reversed.
2021 if (start_offset > end_offset) {
2022 LONG tmp = start_offset;
2023 start_offset = end_offset;
2024 end_offset = tmp;
2025 }
2026
2027 // The spec does not allow the start or end offsets to be out or range;
2028 // we must return an error if so.
2029 LONG len = text_str.length();
2030 if (start_offset < 0)
2031 return E_INVALIDARG;
2032 if (end_offset > len)
2033 return E_INVALIDARG;
2034
2035 base::string16 substr = text_str.substr(start_offset,
2036 end_offset - start_offset);
2037 if (substr.empty())
2038 return S_FALSE;
2039
2040 *text = SysAllocString(substr.c_str());
2041 DCHECK(*text);
2042 return S_OK;
2043 }
2044
get_textAtOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2045 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2046 LONG offset,
2047 enum IA2TextBoundaryType boundary_type,
2048 LONG* start_offset,
2049 LONG* end_offset,
2050 BSTR* text) {
2051 if (!instance_active())
2052 return E_FAIL;
2053
2054 if (!start_offset || !end_offset || !text)
2055 return E_INVALIDARG;
2056
2057 // The IAccessible2 spec says we don't have to implement the "sentence"
2058 // boundary type, we can just let the screenreader handle it.
2059 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2060 *start_offset = 0;
2061 *end_offset = 0;
2062 *text = NULL;
2063 return S_FALSE;
2064 }
2065
2066 const base::string16& text_str = TextForIAccessibleText();
2067
2068 *start_offset = FindBoundary(
2069 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2070 *end_offset = FindBoundary(
2071 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2072 return get_text(*start_offset, *end_offset, text);
2073 }
2074
get_textBeforeOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2075 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2076 LONG offset,
2077 enum IA2TextBoundaryType boundary_type,
2078 LONG* start_offset,
2079 LONG* end_offset,
2080 BSTR* text) {
2081 if (!instance_active())
2082 return E_FAIL;
2083
2084 if (!start_offset || !end_offset || !text)
2085 return E_INVALIDARG;
2086
2087 // The IAccessible2 spec says we don't have to implement the "sentence"
2088 // boundary type, we can just let the screenreader handle it.
2089 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2090 *start_offset = 0;
2091 *end_offset = 0;
2092 *text = NULL;
2093 return S_FALSE;
2094 }
2095
2096 const base::string16& text_str = TextForIAccessibleText();
2097
2098 *start_offset = FindBoundary(
2099 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2100 *end_offset = offset;
2101 return get_text(*start_offset, *end_offset, text);
2102 }
2103
get_textAfterOffset(LONG offset,enum IA2TextBoundaryType boundary_type,LONG * start_offset,LONG * end_offset,BSTR * text)2104 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2105 LONG offset,
2106 enum IA2TextBoundaryType boundary_type,
2107 LONG* start_offset,
2108 LONG* end_offset,
2109 BSTR* text) {
2110 if (!instance_active())
2111 return E_FAIL;
2112
2113 if (!start_offset || !end_offset || !text)
2114 return E_INVALIDARG;
2115
2116 // The IAccessible2 spec says we don't have to implement the "sentence"
2117 // boundary type, we can just let the screenreader handle it.
2118 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2119 *start_offset = 0;
2120 *end_offset = 0;
2121 *text = NULL;
2122 return S_FALSE;
2123 }
2124
2125 const base::string16& text_str = TextForIAccessibleText();
2126
2127 *start_offset = offset;
2128 *end_offset = FindBoundary(
2129 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2130 return get_text(*start_offset, *end_offset, text);
2131 }
2132
get_newText(IA2TextSegment * new_text)2133 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2134 if (!instance_active())
2135 return E_FAIL;
2136
2137 if (!new_text)
2138 return E_INVALIDARG;
2139
2140 base::string16 text = TextForIAccessibleText();
2141
2142 new_text->text = SysAllocString(text.c_str());
2143 new_text->start = 0;
2144 new_text->end = static_cast<long>(text.size());
2145 return S_OK;
2146 }
2147
get_oldText(IA2TextSegment * old_text)2148 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2149 if (!instance_active())
2150 return E_FAIL;
2151
2152 if (!old_text)
2153 return E_INVALIDARG;
2154
2155 old_text->text = SysAllocString(old_text_.c_str());
2156 old_text->start = 0;
2157 old_text->end = static_cast<long>(old_text_.size());
2158 return S_OK;
2159 }
2160
get_offsetAtPoint(LONG x,LONG y,enum IA2CoordinateType coord_type,LONG * offset)2161 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2162 LONG x,
2163 LONG y,
2164 enum IA2CoordinateType coord_type,
2165 LONG* offset) {
2166 if (!instance_active())
2167 return E_FAIL;
2168
2169 if (!offset)
2170 return E_INVALIDARG;
2171
2172 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2173 // screen readers still return partially accurate results rather than
2174 // completely failing.
2175 *offset = 0;
2176 return S_OK;
2177 }
2178
scrollSubstringTo(LONG start_index,LONG end_index,enum IA2ScrollType scroll_type)2179 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2180 LONG start_index,
2181 LONG end_index,
2182 enum IA2ScrollType scroll_type) {
2183 // TODO(dmazzoni): adjust this for the start and end index, too.
2184 return scrollTo(scroll_type);
2185 }
2186
scrollSubstringToPoint(LONG start_index,LONG end_index,enum IA2CoordinateType coordinate_type,LONG x,LONG y)2187 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2188 LONG start_index,
2189 LONG end_index,
2190 enum IA2CoordinateType coordinate_type,
2191 LONG x, LONG y) {
2192 // TODO(dmazzoni): adjust this for the start and end index, too.
2193 return scrollToPoint(coordinate_type, x, y);
2194 }
2195
addSelection(LONG start_offset,LONG end_offset)2196 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2197 LONG end_offset) {
2198 if (!instance_active())
2199 return E_FAIL;
2200
2201 const base::string16& text_str = TextForIAccessibleText();
2202 HandleSpecialTextOffset(text_str, &start_offset);
2203 HandleSpecialTextOffset(text_str, &end_offset);
2204
2205 manager()->SetTextSelection(*this, start_offset, end_offset);
2206 return S_OK;
2207 }
2208
removeSelection(LONG selection_index)2209 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2210 if (!instance_active())
2211 return E_FAIL;
2212
2213 if (selection_index != 0)
2214 return E_INVALIDARG;
2215
2216 manager()->SetTextSelection(*this, 0, 0);
2217 return S_OK;
2218 }
2219
setCaretOffset(LONG offset)2220 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2221 if (!instance_active())
2222 return E_FAIL;
2223
2224 const base::string16& text_str = TextForIAccessibleText();
2225 HandleSpecialTextOffset(text_str, &offset);
2226 manager()->SetTextSelection(*this, offset, offset);
2227 return S_OK;
2228 }
2229
setSelection(LONG selection_index,LONG start_offset,LONG end_offset)2230 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2231 LONG start_offset,
2232 LONG end_offset) {
2233 if (!instance_active())
2234 return E_FAIL;
2235
2236 if (selection_index != 0)
2237 return E_INVALIDARG;
2238
2239 const base::string16& text_str = TextForIAccessibleText();
2240 HandleSpecialTextOffset(text_str, &start_offset);
2241 HandleSpecialTextOffset(text_str, &end_offset);
2242
2243 manager()->SetTextSelection(*this, start_offset, end_offset);
2244 return S_OK;
2245 }
2246
2247 //
2248 // IAccessibleHypertext methods.
2249 //
2250
get_nHyperlinks(long * hyperlink_count)2251 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2252 if (!instance_active())
2253 return E_FAIL;
2254
2255 if (!hyperlink_count)
2256 return E_INVALIDARG;
2257
2258 *hyperlink_count = hyperlink_offset_to_index_.size();
2259 return S_OK;
2260 }
2261
get_hyperlink(long index,IAccessibleHyperlink ** hyperlink)2262 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2263 long index,
2264 IAccessibleHyperlink** hyperlink) {
2265 if (!instance_active())
2266 return E_FAIL;
2267
2268 if (!hyperlink ||
2269 index < 0 ||
2270 index >= static_cast<long>(hyperlinks_.size())) {
2271 return E_INVALIDARG;
2272 }
2273
2274 BrowserAccessibilityWin* child =
2275 children()[hyperlinks_[index]]->ToBrowserAccessibilityWin();
2276 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2277 return S_OK;
2278 }
2279
get_hyperlinkIndex(long char_index,long * hyperlink_index)2280 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2281 long char_index,
2282 long* hyperlink_index) {
2283 if (!instance_active())
2284 return E_FAIL;
2285
2286 if (!hyperlink_index)
2287 return E_INVALIDARG;
2288
2289 *hyperlink_index = -1;
2290
2291 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
2292 return E_INVALIDARG;
2293
2294 std::map<int32, int32>::iterator it =
2295 hyperlink_offset_to_index_.find(char_index);
2296 if (it == hyperlink_offset_to_index_.end())
2297 return E_FAIL;
2298
2299 *hyperlink_index = it->second;
2300 return S_OK;
2301 }
2302
2303 //
2304 // IAccessibleValue methods.
2305 //
2306
get_currentValue(VARIANT * value)2307 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2308 if (!instance_active())
2309 return E_FAIL;
2310
2311 if (!value)
2312 return E_INVALIDARG;
2313
2314 float float_val;
2315 if (GetFloatAttribute(
2316 AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &float_val)) {
2317 value->vt = VT_R8;
2318 value->dblVal = float_val;
2319 return S_OK;
2320 }
2321
2322 value->vt = VT_EMPTY;
2323 return S_FALSE;
2324 }
2325
get_minimumValue(VARIANT * value)2326 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2327 if (!instance_active())
2328 return E_FAIL;
2329
2330 if (!value)
2331 return E_INVALIDARG;
2332
2333 float float_val;
2334 if (GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
2335 &float_val)) {
2336 value->vt = VT_R8;
2337 value->dblVal = float_val;
2338 return S_OK;
2339 }
2340
2341 value->vt = VT_EMPTY;
2342 return S_FALSE;
2343 }
2344
get_maximumValue(VARIANT * value)2345 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2346 if (!instance_active())
2347 return E_FAIL;
2348
2349 if (!value)
2350 return E_INVALIDARG;
2351
2352 float float_val;
2353 if (GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
2354 &float_val)) {
2355 value->vt = VT_R8;
2356 value->dblVal = float_val;
2357 return S_OK;
2358 }
2359
2360 value->vt = VT_EMPTY;
2361 return S_FALSE;
2362 }
2363
setCurrentValue(VARIANT new_value)2364 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2365 // TODO(dmazzoni): Implement this.
2366 return E_NOTIMPL;
2367 }
2368
2369 //
2370 // ISimpleDOMDocument methods.
2371 //
2372
get_URL(BSTR * url)2373 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2374 if (!instance_active())
2375 return E_FAIL;
2376
2377 if (!url)
2378 return E_INVALIDARG;
2379
2380 return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_URL, url);
2381 }
2382
get_title(BSTR * title)2383 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2384 if (!instance_active())
2385 return E_FAIL;
2386
2387 if (!title)
2388 return E_INVALIDARG;
2389
2390 return GetStringAttributeAsBstr(AccessibilityNodeData::ATTR_DOC_TITLE, title);
2391 }
2392
get_mimeType(BSTR * mime_type)2393 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2394 if (!instance_active())
2395 return E_FAIL;
2396
2397 if (!mime_type)
2398 return E_INVALIDARG;
2399
2400 return GetStringAttributeAsBstr(
2401 AccessibilityNodeData::ATTR_DOC_MIMETYPE, mime_type);
2402 }
2403
get_docType(BSTR * doc_type)2404 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2405 if (!instance_active())
2406 return E_FAIL;
2407
2408 if (!doc_type)
2409 return E_INVALIDARG;
2410
2411 return GetStringAttributeAsBstr(
2412 AccessibilityNodeData::ATTR_DOC_DOCTYPE, doc_type);
2413 }
2414
2415 //
2416 // ISimpleDOMNode methods.
2417 //
2418
get_nodeInfo(BSTR * node_name,short * name_space_id,BSTR * node_value,unsigned int * num_children,unsigned int * unique_id,unsigned short * node_type)2419 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2420 BSTR* node_name,
2421 short* name_space_id,
2422 BSTR* node_value,
2423 unsigned int* num_children,
2424 unsigned int* unique_id,
2425 unsigned short* node_type) {
2426 if (!instance_active())
2427 return E_FAIL;
2428
2429 if (!node_name || !name_space_id || !node_value || !num_children ||
2430 !unique_id || !node_type) {
2431 return E_INVALIDARG;
2432 }
2433
2434 base::string16 tag;
2435 if (GetString16Attribute(AccessibilityNodeData::ATTR_HTML_TAG, &tag))
2436 *node_name = SysAllocString(tag.c_str());
2437 else
2438 *node_name = NULL;
2439
2440 *name_space_id = 0;
2441 *node_value = SysAllocString(UTF8ToUTF16(value()).c_str());
2442 *num_children = PlatformChildCount();
2443 *unique_id = unique_id_win_;
2444
2445 if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
2446 *node_type = NODETYPE_DOCUMENT;
2447 } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
2448 ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
2449 *node_type = NODETYPE_TEXT;
2450 } else {
2451 *node_type = NODETYPE_ELEMENT;
2452 }
2453
2454 return S_OK;
2455 }
2456
get_attributes(unsigned short max_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values,unsigned short * num_attribs)2457 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2458 unsigned short max_attribs,
2459 BSTR* attrib_names,
2460 short* name_space_id,
2461 BSTR* attrib_values,
2462 unsigned short* num_attribs) {
2463 if (!instance_active())
2464 return E_FAIL;
2465
2466 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2467 return E_INVALIDARG;
2468
2469 *num_attribs = max_attribs;
2470 if (*num_attribs > html_attributes().size())
2471 *num_attribs = html_attributes().size();
2472
2473 for (unsigned short i = 0; i < *num_attribs; ++i) {
2474 attrib_names[i] = SysAllocString(
2475 UTF8ToUTF16(html_attributes()[i].first).c_str());
2476 name_space_id[i] = 0;
2477 attrib_values[i] = SysAllocString(
2478 UTF8ToUTF16(html_attributes()[i].second).c_str());
2479 }
2480 return S_OK;
2481 }
2482
get_attributesForNames(unsigned short num_attribs,BSTR * attrib_names,short * name_space_id,BSTR * attrib_values)2483 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2484 unsigned short num_attribs,
2485 BSTR* attrib_names,
2486 short* name_space_id,
2487 BSTR* attrib_values) {
2488 if (!instance_active())
2489 return E_FAIL;
2490
2491 if (!attrib_names || !name_space_id || !attrib_values)
2492 return E_INVALIDARG;
2493
2494 for (unsigned short i = 0; i < num_attribs; ++i) {
2495 name_space_id[i] = 0;
2496 bool found = false;
2497 std::string name = UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2498 for (unsigned int j = 0; j < html_attributes().size(); ++j) {
2499 if (html_attributes()[j].first == name) {
2500 attrib_values[i] = SysAllocString(
2501 UTF8ToUTF16(html_attributes()[j].second).c_str());
2502 found = true;
2503 break;
2504 }
2505 }
2506 if (!found) {
2507 attrib_values[i] = NULL;
2508 }
2509 }
2510 return S_OK;
2511 }
2512
get_computedStyle(unsigned short max_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values,unsigned short * num_style_properties)2513 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2514 unsigned short max_style_properties,
2515 boolean use_alternate_view,
2516 BSTR* style_properties,
2517 BSTR* style_values,
2518 unsigned short *num_style_properties) {
2519 if (!instance_active())
2520 return E_FAIL;
2521
2522 if (!style_properties || !style_values)
2523 return E_INVALIDARG;
2524
2525 // We only cache a single style property for now: DISPLAY
2526
2527 base::string16 display;
2528 if (max_style_properties == 0 ||
2529 !GetString16Attribute(AccessibilityNodeData::ATTR_DISPLAY, &display)) {
2530 *num_style_properties = 0;
2531 return S_OK;
2532 }
2533
2534 *num_style_properties = 1;
2535 style_properties[0] = SysAllocString(L"display");
2536 style_values[0] = SysAllocString(display.c_str());
2537
2538 return S_OK;
2539 }
2540
get_computedStyleForProperties(unsigned short num_style_properties,boolean use_alternate_view,BSTR * style_properties,BSTR * style_values)2541 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2542 unsigned short num_style_properties,
2543 boolean use_alternate_view,
2544 BSTR* style_properties,
2545 BSTR* style_values) {
2546 if (!instance_active())
2547 return E_FAIL;
2548
2549 if (!style_properties || !style_values)
2550 return E_INVALIDARG;
2551
2552 // We only cache a single style property for now: DISPLAY
2553
2554 for (unsigned short i = 0; i < num_style_properties; ++i) {
2555 base::string16 name = (LPCWSTR)style_properties[i];
2556 StringToLowerASCII(&name);
2557 if (name == L"display") {
2558 base::string16 display = GetString16Attribute(
2559 AccessibilityNodeData::ATTR_DISPLAY);
2560 style_values[i] = SysAllocString(display.c_str());
2561 } else {
2562 style_values[i] = NULL;
2563 }
2564 }
2565
2566 return S_OK;
2567 }
2568
scrollTo(boolean placeTopLeft)2569 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2570 return scrollTo(placeTopLeft ?
2571 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2572 }
2573
get_parentNode(ISimpleDOMNode ** node)2574 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2575 if (!instance_active())
2576 return E_FAIL;
2577
2578 if (!node)
2579 return E_INVALIDARG;
2580
2581 *node = parent()->ToBrowserAccessibilityWin()->NewReference();
2582 return S_OK;
2583 }
2584
get_firstChild(ISimpleDOMNode ** node)2585 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
2586 if (!instance_active())
2587 return E_FAIL;
2588
2589 if (!node)
2590 return E_INVALIDARG;
2591
2592 if (PlatformChildCount() == 0) {
2593 *node = NULL;
2594 return S_FALSE;
2595 }
2596
2597 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2598 return S_OK;
2599 }
2600
get_lastChild(ISimpleDOMNode ** node)2601 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2602 if (!instance_active())
2603 return E_FAIL;
2604
2605 if (!node)
2606 return E_INVALIDARG;
2607
2608 if (PlatformChildCount() == 0) {
2609 *node = NULL;
2610 return S_FALSE;
2611 }
2612
2613 *node = PlatformGetChild(PlatformChildCount() - 1)
2614 ->ToBrowserAccessibilityWin()->NewReference();
2615 return S_OK;
2616 }
2617
get_previousSibling(ISimpleDOMNode ** node)2618 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2619 ISimpleDOMNode** node) {
2620 if (!instance_active())
2621 return E_FAIL;
2622
2623 if (!node)
2624 return E_INVALIDARG;
2625
2626 if (!parent() || index_in_parent() <= 0) {
2627 *node = NULL;
2628 return S_FALSE;
2629 }
2630
2631 *node = parent()->children()[index_in_parent() - 1]->
2632 ToBrowserAccessibilityWin()->NewReference();
2633 return S_OK;
2634 }
2635
get_nextSibling(ISimpleDOMNode ** node)2636 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2637 if (!instance_active())
2638 return E_FAIL;
2639
2640 if (!node)
2641 return E_INVALIDARG;
2642
2643 if (!parent() ||
2644 index_in_parent() < 0 ||
2645 index_in_parent() >= static_cast<int>(parent()->children().size()) - 1) {
2646 *node = NULL;
2647 return S_FALSE;
2648 }
2649
2650 *node = parent()->children()[index_in_parent() + 1]->
2651 ToBrowserAccessibilityWin()->NewReference();
2652 return S_OK;
2653 }
2654
get_childAt(unsigned int child_index,ISimpleDOMNode ** node)2655 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2656 unsigned int child_index,
2657 ISimpleDOMNode** node) {
2658 if (!instance_active())
2659 return E_FAIL;
2660
2661 if (!node)
2662 return E_INVALIDARG;
2663
2664 if (child_index >= PlatformChildCount())
2665 return E_INVALIDARG;
2666
2667 BrowserAccessibility* child = PlatformGetChild(child_index);
2668 if (!child) {
2669 *node = NULL;
2670 return S_FALSE;
2671 }
2672
2673 *node = child->ToBrowserAccessibilityWin()->NewReference();
2674 return S_OK;
2675 }
2676
2677 //
2678 // ISimpleDOMText methods.
2679 //
2680
get_domText(BSTR * dom_text)2681 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2682 if (!instance_active())
2683 return E_FAIL;
2684
2685 if (!dom_text)
2686 return E_INVALIDARG;
2687
2688 return GetStringAttributeAsBstr(
2689 AccessibilityNodeData::ATTR_NAME, dom_text);
2690 }
2691
get_clippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2692 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2693 unsigned int start_index,
2694 unsigned int end_index,
2695 int* out_x,
2696 int* out_y,
2697 int* out_width,
2698 int* out_height) {
2699 // TODO(dmazzoni): fully support this API by intersecting the
2700 // rect with the container's rect.
2701 return get_unclippedSubstringBounds(
2702 start_index, end_index, out_x, out_y, out_width, out_height);
2703 }
2704
get_unclippedSubstringBounds(unsigned int start_index,unsigned int end_index,int * out_x,int * out_y,int * out_width,int * out_height)2705 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2706 unsigned int start_index,
2707 unsigned int end_index,
2708 int* out_x,
2709 int* out_y,
2710 int* out_width,
2711 int* out_height) {
2712 if (!instance_active())
2713 return E_FAIL;
2714
2715 if (!out_x || !out_y || !out_width || !out_height)
2716 return E_INVALIDARG;
2717
2718 const base::string16& text_str = TextForIAccessibleText();
2719 if (start_index > text_str.size() ||
2720 end_index > text_str.size() ||
2721 start_index > end_index) {
2722 return E_INVALIDARG;
2723 }
2724
2725 if (blink_role() != blink::WebAXRoleStaticText)
2726 return E_FAIL;
2727
2728 gfx::Rect bounds = GetGlobalBoundsForRange(
2729 start_index, end_index - start_index);
2730 *out_x = bounds.x();
2731 *out_y = bounds.y();
2732 *out_width = bounds.width();
2733 *out_height = bounds.height();
2734 return S_OK;
2735 }
2736
scrollToSubstring(unsigned int start_index,unsigned int end_index)2737 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2738 unsigned int start_index,
2739 unsigned int end_index) {
2740 if (!instance_active())
2741 return E_FAIL;
2742
2743 const base::string16& text_str = TextForIAccessibleText();
2744 if (start_index > text_str.size() ||
2745 end_index > text_str.size() ||
2746 start_index > end_index) {
2747 return E_INVALIDARG;
2748 }
2749
2750 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2751 start_index, end_index - start_index));
2752 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2753
2754 return S_OK;
2755 }
2756
2757 //
2758 // IServiceProvider methods.
2759 //
2760
QueryService(REFGUID guidService,REFIID riid,void ** object)2761 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2762 REFIID riid,
2763 void** object) {
2764 if (!instance_active())
2765 return E_FAIL;
2766
2767 // The system uses IAccessible APIs for many purposes, but only
2768 // assistive technology like screen readers uses IAccessible2.
2769 // Enable full accessibility support when IAccessible2 APIs are queried.
2770 if (riid == IID_IAccessible2)
2771 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2772
2773 if (guidService == GUID_IAccessibleContentDocument) {
2774 // Special Mozilla extension: return the accessible for the root document.
2775 // Screen readers use this to distinguish between a document loaded event
2776 // on the root document vs on an iframe.
2777 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2778 IID_IAccessible2, object);
2779 }
2780
2781 if (guidService == IID_IAccessible ||
2782 guidService == IID_IAccessible2 ||
2783 guidService == IID_IAccessibleAction ||
2784 guidService == IID_IAccessibleApplication ||
2785 guidService == IID_IAccessibleHyperlink ||
2786 guidService == IID_IAccessibleHypertext ||
2787 guidService == IID_IAccessibleImage ||
2788 guidService == IID_IAccessibleTable ||
2789 guidService == IID_IAccessibleTable2 ||
2790 guidService == IID_IAccessibleTableCell ||
2791 guidService == IID_IAccessibleText ||
2792 guidService == IID_IAccessibleValue ||
2793 guidService == IID_ISimpleDOMDocument ||
2794 guidService == IID_ISimpleDOMNode ||
2795 guidService == IID_ISimpleDOMText ||
2796 guidService == GUID_ISimpleDOM) {
2797 return QueryInterface(riid, object);
2798 }
2799
2800 // We only support the IAccessibleEx interface on Windows 8 and above. This
2801 // is needed for the on-screen Keyboard to show up in metro mode, when the
2802 // user taps an editable portion on the page.
2803 // All methods in the IAccessibleEx interface are unimplemented.
2804 if (riid == IID_IAccessibleEx &&
2805 base::win::GetVersion() >= base::win::VERSION_WIN8) {
2806 return QueryInterface(riid, object);
2807 }
2808
2809 *object = NULL;
2810 return E_FAIL;
2811 }
2812
GetPatternProvider(PATTERNID id,IUnknown ** provider)2813 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2814 IUnknown** provider) {
2815 DVLOG(1) << "In Function: "
2816 << __FUNCTION__
2817 << " for pattern id: "
2818 << id;
2819 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2820 if (IsEditableText()) {
2821 // The BrowserAccessibilityManager keeps track of instances when
2822 // we don't want to show the on-screen keyboard.
2823 if (!manager()->IsOSKAllowed(GetGlobalBoundsRect()))
2824 return E_NOTIMPL;
2825
2826 DVLOG(1) << "Returning UIA text provider";
2827 base::win::UIATextProvider::CreateTextProvider(true, provider);
2828 return S_OK;
2829 }
2830 }
2831 return E_NOTIMPL;
2832 }
2833
GetPropertyValue(PROPERTYID id,VARIANT * ret)2834 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2835 VARIANT* ret) {
2836 DVLOG(1) << "In Function: "
2837 << __FUNCTION__
2838 << " for property id: "
2839 << id;
2840 V_VT(ret) = VT_EMPTY;
2841 if (id == UIA_ControlTypePropertyId) {
2842 if (IsEditableText()) {
2843 V_VT(ret) = VT_I4;
2844 ret->lVal = UIA_EditControlTypeId;
2845 DVLOG(1) << "Returning Edit control type";
2846 } else {
2847 DVLOG(1) << "Returning empty control type";
2848 }
2849 }
2850 return S_OK;
2851 }
2852
2853 //
2854 // CComObjectRootEx methods.
2855 //
2856
InternalQueryInterface(void * this_ptr,const _ATL_INTMAP_ENTRY * entries,REFIID iid,void ** object)2857 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2858 void* this_ptr,
2859 const _ATL_INTMAP_ENTRY* entries,
2860 REFIID iid,
2861 void** object) {
2862 if (iid == IID_IAccessibleImage) {
2863 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) {
2864 *object = NULL;
2865 return E_NOINTERFACE;
2866 }
2867 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2868 if (ia_role_ != ROLE_SYSTEM_TABLE) {
2869 *object = NULL;
2870 return E_NOINTERFACE;
2871 }
2872 } else if (iid == IID_IAccessibleTableCell) {
2873 if (ia_role_ != ROLE_SYSTEM_CELL) {
2874 *object = NULL;
2875 return E_NOINTERFACE;
2876 }
2877 } else if (iid == IID_IAccessibleValue) {
2878 if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR &&
2879 ia_role_ != ROLE_SYSTEM_SCROLLBAR &&
2880 ia_role_ != ROLE_SYSTEM_SLIDER) {
2881 *object = NULL;
2882 return E_NOINTERFACE;
2883 }
2884 } else if (iid == IID_ISimpleDOMDocument) {
2885 if (ia_role_ != ROLE_SYSTEM_DOCUMENT) {
2886 *object = NULL;
2887 return E_NOINTERFACE;
2888 }
2889 }
2890
2891 return CComObjectRootBase::InternalQueryInterface(
2892 this_ptr, entries, iid, object);
2893 }
2894
2895 //
2896 // Private methods.
2897 //
2898
2899 // Initialize this object and mark it as active.
PreInitialize()2900 void BrowserAccessibilityWin::PreInitialize() {
2901 BrowserAccessibility::PreInitialize();
2902
2903 InitRoleAndState();
2904
2905 // Expose the "display" and "tag" attributes.
2906 StringAttributeToIA2(AccessibilityNodeData::ATTR_DISPLAY, "display");
2907 StringAttributeToIA2(AccessibilityNodeData::ATTR_HTML_TAG, "tag");
2908 StringAttributeToIA2(AccessibilityNodeData::ATTR_ROLE, "xml-roles");
2909
2910 // Expose "level" attribute for headings, trees, etc.
2911 IntAttributeToIA2(AccessibilityNodeData::ATTR_HIERARCHICAL_LEVEL, "level");
2912
2913 // Expose the set size and position in set for listbox options.
2914 if (blink_role() == blink::WebAXRoleListBoxOption &&
2915 parent() &&
2916 parent()->role() == blink::WebAXRoleListBox) {
2917 ia2_attributes_.push_back(
2918 L"setsize:" + base::IntToString16(parent()->PlatformChildCount()));
2919 ia2_attributes_.push_back(
2920 L"setsize:" + base::IntToString16(index_in_parent() + 1));
2921 }
2922
2923 if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
2924 ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
2925 ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
2926 ia2_attributes_.push_back(L"checkable:true");
2927 }
2928
2929 // Expose live region attributes.
2930 StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_STATUS, "live");
2931 StringAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_RELEVANT, "relevant");
2932 BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_ATOMIC, "atomic");
2933 BoolAttributeToIA2(AccessibilityNodeData::ATTR_LIVE_BUSY, "busy");
2934
2935 // Expose container live region attributes.
2936 StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS,
2937 "container-live");
2938 StringAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_RELEVANT,
2939 "container-relevant");
2940 BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_ATOMIC,
2941 "container-atomic");
2942 BoolAttributeToIA2(AccessibilityNodeData::ATTR_CONTAINER_LIVE_BUSY,
2943 "container-busy");
2944
2945 // Expose slider value.
2946 if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
2947 ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
2948 ia_role_ == ROLE_SYSTEM_SLIDER) {
2949 ia2_attributes_.push_back(L"valuetext:" + GetValueText());
2950 }
2951
2952 // Expose table cell index.
2953 if (ia_role_ == ROLE_SYSTEM_CELL) {
2954 BrowserAccessibility* table = parent();
2955 while (table && table->role() != blink::WebAXRoleTable)
2956 table = table->parent();
2957 if (table) {
2958 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
2959 AccessibilityNodeData::ATTR_UNIQUE_CELL_IDS);
2960 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
2961 if (unique_cell_ids[i] == renderer_id()) {
2962 ia2_attributes_.push_back(
2963 base::string16(L"table-cell-index:") + base::IntToString16(i));
2964 }
2965 }
2966 }
2967 }
2968
2969 // The calculation of the accessible name of an element has been
2970 // standardized in the HTML to Platform Accessibility APIs Implementation
2971 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2972 // appropriate accessible name on Windows, we need to apply some logic
2973 // to the fields we get from WebKit.
2974 //
2975 // TODO(dmazzoni): move most of this logic into WebKit.
2976 //
2977 // WebKit gives us:
2978 //
2979 // name: the default name, e.g. inner text
2980 // title ui element: a reference to a <label> element on the same
2981 // page that labels this node.
2982 // description: accessible labels that override the default name:
2983 // aria-label or aria-labelledby or aria-describedby
2984 // help: the value of the "title" attribute
2985 //
2986 // On Windows, the logic we apply lets some fields take precedence and
2987 // always returns the primary name in "name" and the secondary name,
2988 // if any, in "description".
2989
2990 int title_elem_id = GetIntAttribute(
2991 AccessibilityNodeData::ATTR_TITLE_UI_ELEMENT);
2992 std::string help = GetStringAttribute(AccessibilityNodeData::ATTR_HELP);
2993 std::string description = GetStringAttribute(
2994 AccessibilityNodeData::ATTR_DESCRIPTION);
2995
2996 // WebKit annoyingly puts the title in the description if there's no other
2997 // description, which just confuses the rest of the logic. Put it back.
2998 // Now "help" is always the value of the "title" attribute, if present.
2999 std::string title_attr;
3000 if (GetHtmlAttribute("title", &title_attr) &&
3001 description == title_attr &&
3002 help.empty()) {
3003 help = description;
3004 description.clear();
3005 }
3006
3007 // Now implement the main logic: the descripion should become the name if
3008 // it's nonempty, and the help should become the description if
3009 // there's no description - or the name if there's no name or description.
3010 if (!description.empty()) {
3011 set_name(description);
3012 description.clear();
3013 }
3014 if (!help.empty() && description.empty()) {
3015 description = help;
3016 help.clear();
3017 }
3018 if (!description.empty() && name().empty() && !title_elem_id) {
3019 set_name(description);
3020 description.clear();
3021 }
3022
3023 // If it's a text field, also consider the placeholder.
3024 std::string placeholder;
3025 if (blink_role() == blink::WebAXRoleTextField &&
3026 HasState(blink::WebAXStateFocusable) &&
3027 GetHtmlAttribute("placeholder", &placeholder)) {
3028 if (name().empty() && !title_elem_id) {
3029 set_name(placeholder);
3030 } else if (description.empty()) {
3031 description = placeholder;
3032 }
3033 }
3034
3035 SetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, description);
3036 SetStringAttribute(AccessibilityNodeData::ATTR_HELP, help);
3037
3038 // On Windows, the value of a document should be its url.
3039 if (blink_role() == blink::WebAXRoleRootWebArea ||
3040 blink_role() == blink::WebAXRoleWebArea) {
3041 set_value(GetStringAttribute(AccessibilityNodeData::ATTR_DOC_URL));
3042 }
3043
3044 // For certain roles (listbox option, static text, and list marker)
3045 // WebKit stores the main accessible text in the "value" - swap it so
3046 // that it's the "name".
3047 if (name().empty() &&
3048 (blink_role() == blink::WebAXRoleListBoxOption ||
3049 blink_role() == blink::WebAXRoleStaticText ||
3050 blink_role() == blink::WebAXRoleListMarker)) {
3051 std::string tmp = value();
3052 set_value(name());
3053 set_name(tmp);
3054 }
3055
3056 // If this doesn't have a value and is linked then set its value to the url
3057 // attribute. This allows screen readers to read an empty link's destination.
3058 if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
3059 set_value(GetStringAttribute(AccessibilityNodeData::ATTR_URL));
3060
3061 // Clear any old relationships between this node and other nodes.
3062 for (size_t i = 0; i < relations_.size(); ++i)
3063 relations_[i]->Release();
3064 relations_.clear();
3065
3066 // Handle title UI element.
3067 if (title_elem_id) {
3068 // Add a labelled by relationship.
3069 CComObject<BrowserAccessibilityRelation>* relation;
3070 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3071 &relation);
3072 DCHECK(SUCCEEDED(hr));
3073 relation->AddRef();
3074 relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3075 relation->AddTarget(title_elem_id);
3076 relations_.push_back(relation);
3077 }
3078 }
3079
PostInitialize()3080 void BrowserAccessibilityWin::PostInitialize() {
3081 BrowserAccessibility::PostInitialize();
3082
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->role() == blink::WebAXRoleStaticText) {
3090 hypertext_ += 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 (blink_role() == blink::WebAXRoleAlert && first_time_)
3101 manager()->NotifyAccessibilityEvent(blink::WebAXEventAlert, 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 blink::WebAXEventShow, 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 (blink_role() == blink::WebAXRoleListBoxOption &&
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(AccessibilityNodeData::ATTR_SCROLL_X, &sx) &&
3151 GetIntAttribute(AccessibilityNodeData::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
SetLocation(const gfx::Rect & new_location)3176 void BrowserAccessibilityWin::SetLocation(const gfx::Rect& new_location) {
3177 BrowserAccessibility::SetLocation(new_location);
3178 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3179 EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
3180 }
3181
NewReference()3182 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3183 AddRef();
3184 return this;
3185 }
3186
GetTargetFromChildID(const VARIANT & var_id)3187 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3188 const VARIANT& var_id) {
3189 if (var_id.vt != VT_I4)
3190 return NULL;
3191
3192 LONG child_id = var_id.lVal;
3193 if (child_id == CHILDID_SELF)
3194 return this;
3195
3196 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3197 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3198
3199 return manager()->ToBrowserAccessibilityManagerWin()->
3200 GetFromUniqueIdWin(child_id);
3201 }
3202
GetStringAttributeAsBstr(AccessibilityNodeData::StringAttribute attribute,BSTR * value_bstr)3203 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3204 AccessibilityNodeData::StringAttribute attribute,
3205 BSTR* value_bstr) {
3206 base::string16 str;
3207
3208 if (!GetString16Attribute(attribute, &str))
3209 return S_FALSE;
3210
3211 if (str.empty())
3212 return S_FALSE;
3213
3214 *value_bstr = SysAllocString(str.c_str());
3215 DCHECK(*value_bstr);
3216
3217 return S_OK;
3218 }
3219
StringAttributeToIA2(AccessibilityNodeData::StringAttribute attribute,const char * ia2_attr)3220 void BrowserAccessibilityWin::StringAttributeToIA2(
3221 AccessibilityNodeData::StringAttribute attribute,
3222 const char* ia2_attr) {
3223 base::string16 value;
3224 if (GetString16Attribute(attribute, &value))
3225 ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value);
3226 }
3227
BoolAttributeToIA2(AccessibilityNodeData::BoolAttribute attribute,const char * ia2_attr)3228 void BrowserAccessibilityWin::BoolAttributeToIA2(
3229 AccessibilityNodeData::BoolAttribute attribute,
3230 const char* ia2_attr) {
3231 bool value;
3232 if (GetBoolAttribute(attribute, &value)) {
3233 ia2_attributes_.push_back((ASCIIToUTF16(ia2_attr) + L":") +
3234 (value ? L"true" : L"false"));
3235 }
3236 }
3237
IntAttributeToIA2(AccessibilityNodeData::IntAttribute attribute,const char * ia2_attr)3238 void BrowserAccessibilityWin::IntAttributeToIA2(
3239 AccessibilityNodeData::IntAttribute attribute,
3240 const char* ia2_attr) {
3241 int value;
3242 if (GetIntAttribute(attribute, &value)) {
3243 ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" +
3244 base::IntToString16(value));
3245 }
3246 }
3247
GetValueText()3248 base::string16 BrowserAccessibilityWin::GetValueText() {
3249 float fval;
3250 base::string16 value = UTF8ToUTF16(this->value());
3251
3252 if (value.empty() &&
3253 GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE, &fval)) {
3254 value = UTF8ToUTF16(base::DoubleToString(fval));
3255 }
3256 return value;
3257 }
3258
TextForIAccessibleText()3259 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3260 if (IsEditableText())
3261 return UTF8ToUTF16(value());
3262 return (blink_role() == blink::WebAXRoleStaticText) ?
3263 UTF8ToUTF16(name()) : hypertext_;
3264 }
3265
HandleSpecialTextOffset(const base::string16 & text,LONG * offset)3266 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3267 const base::string16& text,
3268 LONG* offset) {
3269 if (*offset == IA2_TEXT_OFFSET_LENGTH)
3270 *offset = static_cast<LONG>(text.size());
3271 else if (*offset == IA2_TEXT_OFFSET_CARET)
3272 get_caretOffset(offset);
3273 }
3274
IA2TextBoundaryToTextBoundary(IA2TextBoundaryType ia2_boundary)3275 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3276 IA2TextBoundaryType ia2_boundary) {
3277 switch(ia2_boundary) {
3278 case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY;
3279 case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY;
3280 case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY;
3281 case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY;
3282 case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY;
3283 case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY;
3284 default:
3285 NOTREACHED();
3286 return ui::CHAR_BOUNDARY;
3287 }
3288 }
3289
FindBoundary(const base::string16 & text,IA2TextBoundaryType ia2_boundary,LONG start_offset,ui::TextBoundaryDirection direction)3290 LONG BrowserAccessibilityWin::FindBoundary(
3291 const base::string16& text,
3292 IA2TextBoundaryType ia2_boundary,
3293 LONG start_offset,
3294 ui::TextBoundaryDirection direction) {
3295 HandleSpecialTextOffset(text, &start_offset);
3296 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3297 const std::vector<int32>& line_breaks = GetIntListAttribute(
3298 AccessibilityNodeData::ATTR_LINE_BREAKS);
3299 return ui::FindAccessibleTextBoundary(
3300 text, line_breaks, boundary, start_offset, direction);
3301 }
3302
GetFromRendererID(int32 renderer_id)3303 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID(
3304 int32 renderer_id) {
3305 return manager()->GetFromRendererID(renderer_id)->ToBrowserAccessibilityWin();
3306 }
3307
InitRoleAndState()3308 void BrowserAccessibilityWin::InitRoleAndState() {
3309 ia_state_ = 0;
3310 ia2_state_ = IA2_STATE_OPAQUE;
3311 ia2_attributes_.clear();
3312
3313 if (HasState(blink::WebAXStateBusy))
3314 ia_state_ |= STATE_SYSTEM_BUSY;
3315 if (HasState(blink::WebAXStateChecked))
3316 ia_state_ |= STATE_SYSTEM_CHECKED;
3317 if (HasState(blink::WebAXStateCollapsed))
3318 ia_state_ |= STATE_SYSTEM_COLLAPSED;
3319 if (HasState(blink::WebAXStateExpanded))
3320 ia_state_ |= STATE_SYSTEM_EXPANDED;
3321 if (HasState(blink::WebAXStateFocusable))
3322 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3323 if (HasState(blink::WebAXStateHaspopup))
3324 ia_state_ |= STATE_SYSTEM_HASPOPUP;
3325 if (HasState(blink::WebAXStateHovered))
3326 ia_state_ |= STATE_SYSTEM_HOTTRACKED;
3327 if (HasState(blink::WebAXStateIndeterminate))
3328 ia_state_ |= STATE_SYSTEM_INDETERMINATE;
3329 if (HasState(blink::WebAXStateInvisible))
3330 ia_state_ |= STATE_SYSTEM_INVISIBLE;
3331 if (HasState(blink::WebAXStateLinked))
3332 ia_state_ |= STATE_SYSTEM_LINKED;
3333 if (HasState(blink::WebAXStateMultiselectable)) {
3334 ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
3335 ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
3336 }
3337 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3338 if (HasState(blink::WebAXStateOffscreen))
3339 ia_state_ |= STATE_SYSTEM_OFFSCREEN;
3340 if (HasState(blink::WebAXStatePressed))
3341 ia_state_ |= STATE_SYSTEM_PRESSED;
3342 if (HasState(blink::WebAXStateProtected))
3343 ia_state_ |= STATE_SYSTEM_PROTECTED;
3344 if (HasState(blink::WebAXStateRequired))
3345 ia2_state_ |= IA2_STATE_REQUIRED;
3346 if (HasState(blink::WebAXStateSelectable))
3347 ia_state_ |= STATE_SYSTEM_SELECTABLE;
3348 if (HasState(blink::WebAXStateSelected))
3349 ia_state_ |= STATE_SYSTEM_SELECTED;
3350 if (HasState(blink::WebAXStateVisited))
3351 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3352 if (!HasState(blink::WebAXStateEnabled))
3353 ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
3354 if (HasState(blink::WebAXStateVertical)) {
3355 ia2_state_ |= IA2_STATE_VERTICAL;
3356 } else {
3357 ia2_state_ |= IA2_STATE_HORIZONTAL;
3358 }
3359 if (HasState(blink::WebAXStateVisited))
3360 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3361
3362 // WebKit marks everything as readonly unless it's editable text, so if it's
3363 // not readonly, mark it as editable now. The final computation of the
3364 // READONLY state for MSAA is below, after the switch.
3365 if (!HasState(blink::WebAXStateReadonly))
3366 ia2_state_ |= IA2_STATE_EDITABLE;
3367
3368 base::string16 invalid;
3369 if (GetHtmlAttribute("aria-invalid", &invalid))
3370 ia2_state_ |= IA2_STATE_INVALID_ENTRY;
3371
3372 if (GetBoolAttribute(AccessibilityNodeData::ATTR_BUTTON_MIXED))
3373 ia_state_ |= STATE_SYSTEM_MIXED;
3374
3375 if (GetBoolAttribute(AccessibilityNodeData::ATTR_CAN_SET_VALUE))
3376 ia2_state_ |= IA2_STATE_EDITABLE;
3377
3378 base::string16 html_tag = GetString16Attribute(
3379 AccessibilityNodeData::ATTR_HTML_TAG);
3380 ia_role_ = 0;
3381 ia2_role_ = 0;
3382 switch (blink_role()) {
3383 case blink::WebAXRoleAlert:
3384 ia_role_ = ROLE_SYSTEM_ALERT;
3385 break;
3386 case blink::WebAXRoleAlertDialog:
3387 ia_role_ = ROLE_SYSTEM_DIALOG;
3388 break;
3389 case blink::WebAXRoleApplication:
3390 ia_role_ = ROLE_SYSTEM_APPLICATION;
3391 break;
3392 case blink::WebAXRoleArticle:
3393 ia_role_ = ROLE_SYSTEM_GROUPING;
3394 ia2_role_ = IA2_ROLE_SECTION;
3395 ia_state_ |= STATE_SYSTEM_READONLY;
3396 break;
3397 case blink::WebAXRoleBusyIndicator:
3398 ia_role_ = ROLE_SYSTEM_ANIMATION;
3399 ia_state_ |= STATE_SYSTEM_READONLY;
3400 break;
3401 case blink::WebAXRoleButton:
3402 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3403 bool is_aria_pressed_defined;
3404 bool is_mixed;
3405 if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed))
3406 ia_state_ |= STATE_SYSTEM_PRESSED;
3407 if (is_aria_pressed_defined)
3408 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3409 if (is_mixed)
3410 ia_state_ |= STATE_SYSTEM_MIXED;
3411 break;
3412 case blink::WebAXRoleCanvas:
3413 if (GetBoolAttribute(AccessibilityNodeData::ATTR_CANVAS_HAS_FALLBACK)) {
3414 role_name_ = L"canvas";
3415 ia2_role_ = IA2_ROLE_CANVAS;
3416 } else {
3417 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3418 }
3419 break;
3420 case blink::WebAXRoleCell:
3421 ia_role_ = ROLE_SYSTEM_CELL;
3422 break;
3423 case blink::WebAXRoleCheckBox:
3424 ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
3425 break;
3426 case blink::WebAXRoleColorWell:
3427 ia_role_ = ROLE_SYSTEM_CLIENT;
3428 ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
3429 break;
3430 case blink::WebAXRoleColumn:
3431 ia_role_ = ROLE_SYSTEM_COLUMN;
3432 ia_state_ |= STATE_SYSTEM_READONLY;
3433 break;
3434 case blink::WebAXRoleColumnHeader:
3435 ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
3436 ia_state_ |= STATE_SYSTEM_READONLY;
3437 break;
3438 case blink::WebAXRoleComboBox:
3439 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3440 break;
3441 case blink::WebAXRoleDiv:
3442 role_name_ = L"div";
3443 ia2_role_ = IA2_ROLE_SECTION;
3444 break;
3445 case blink::WebAXRoleDefinition:
3446 role_name_ = html_tag;
3447 ia2_role_ = IA2_ROLE_PARAGRAPH;
3448 ia_state_ |= STATE_SYSTEM_READONLY;
3449 break;
3450 case blink::WebAXRoleDescriptionListDetail:
3451 role_name_ = html_tag;
3452 ia2_role_ = IA2_ROLE_PARAGRAPH;
3453 ia_state_ |= STATE_SYSTEM_READONLY;
3454 break;
3455 case blink::WebAXRoleDescriptionListTerm:
3456 ia_role_ = ROLE_SYSTEM_LISTITEM;
3457 ia_state_ |= STATE_SYSTEM_READONLY;
3458 break;
3459 case blink::WebAXRoleDialog:
3460 ia_role_ = ROLE_SYSTEM_DIALOG;
3461 ia_state_ |= STATE_SYSTEM_READONLY;
3462 break;
3463 case blink::WebAXRoleDisclosureTriangle:
3464 ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON;
3465 ia_state_ |= STATE_SYSTEM_READONLY;
3466 break;
3467 case blink::WebAXRoleDocument:
3468 case blink::WebAXRoleRootWebArea:
3469 case blink::WebAXRoleWebArea:
3470 ia_role_ = ROLE_SYSTEM_DOCUMENT;
3471 ia_state_ |= STATE_SYSTEM_READONLY;
3472 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3473 break;
3474 case blink::WebAXRoleEditableText:
3475 ia_role_ = ROLE_SYSTEM_TEXT;
3476 ia2_state_ |= IA2_STATE_SINGLE_LINE;
3477 ia2_state_ |= IA2_STATE_EDITABLE;
3478 break;
3479 case blink::WebAXRoleForm:
3480 role_name_ = L"form";
3481 ia2_role_ = IA2_ROLE_FORM;
3482 break;
3483 case blink::WebAXRoleFooter:
3484 ia_role_ = IA2_ROLE_FOOTER;
3485 ia_state_ |= STATE_SYSTEM_READONLY;
3486 break;
3487 case blink::WebAXRoleGrid:
3488 ia_role_ = ROLE_SYSTEM_TABLE;
3489 ia_state_ |= STATE_SYSTEM_READONLY;
3490 break;
3491 case blink::WebAXRoleGroup: {
3492 base::string16 aria_role = GetString16Attribute(
3493 AccessibilityNodeData::ATTR_ROLE);
3494 if (aria_role == L"group" || html_tag == L"fieldset") {
3495 ia_role_ = ROLE_SYSTEM_GROUPING;
3496 } else if (html_tag == L"li") {
3497 ia_role_ = ROLE_SYSTEM_LISTITEM;
3498 } else {
3499 if (html_tag.empty())
3500 role_name_ = L"div";
3501 else
3502 role_name_ = html_tag;
3503 ia2_role_ = IA2_ROLE_SECTION;
3504 }
3505 ia_state_ |= STATE_SYSTEM_READONLY;
3506 break;
3507 }
3508 case blink::WebAXRoleGrowArea:
3509 ia_role_ = ROLE_SYSTEM_GRIP;
3510 ia_state_ |= STATE_SYSTEM_READONLY;
3511 break;
3512 case blink::WebAXRoleHeading:
3513 role_name_ = html_tag;
3514 ia2_role_ = IA2_ROLE_HEADING;
3515 ia_state_ |= STATE_SYSTEM_READONLY;
3516 break;
3517 case blink::WebAXRoleHorizontalRule:
3518 ia_role_ = ROLE_SYSTEM_SEPARATOR;
3519 break;
3520 case blink::WebAXRoleImage:
3521 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3522 ia_state_ |= STATE_SYSTEM_READONLY;
3523 break;
3524 case blink::WebAXRoleImageMap:
3525 role_name_ = html_tag;
3526 ia2_role_ = IA2_ROLE_IMAGE_MAP;
3527 ia_state_ |= STATE_SYSTEM_READONLY;
3528 break;
3529 case blink::WebAXRoleImageMapLink:
3530 ia_role_ = ROLE_SYSTEM_LINK;
3531 ia_state_ |= STATE_SYSTEM_LINKED;
3532 ia_state_ |= STATE_SYSTEM_READONLY;
3533 break;
3534 case blink::WebAXRoleLabel:
3535 ia_role_ = ROLE_SYSTEM_TEXT;
3536 ia2_role_ = IA2_ROLE_LABEL;
3537 break;
3538 case blink::WebAXRoleBanner:
3539 case blink::WebAXRoleComplementary:
3540 case blink::WebAXRoleContentInfo:
3541 case blink::WebAXRoleMain:
3542 case blink::WebAXRoleNavigation:
3543 case blink::WebAXRoleSearch:
3544 ia_role_ = ROLE_SYSTEM_GROUPING;
3545 ia2_role_ = IA2_ROLE_SECTION;
3546 ia_state_ |= STATE_SYSTEM_READONLY;
3547 break;
3548 case blink::WebAXRoleLink:
3549 ia_role_ = ROLE_SYSTEM_LINK;
3550 ia_state_ |= STATE_SYSTEM_LINKED;
3551 break;
3552 case blink::WebAXRoleList:
3553 ia_role_ = ROLE_SYSTEM_LIST;
3554 ia_state_ |= STATE_SYSTEM_READONLY;
3555 break;
3556 case blink::WebAXRoleListBox:
3557 ia_role_ = ROLE_SYSTEM_LIST;
3558 break;
3559 case blink::WebAXRoleListBoxOption:
3560 ia_role_ = ROLE_SYSTEM_LISTITEM;
3561 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3562 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3563 if (HasState(blink::WebAXStateFocused))
3564 ia_state_ |= STATE_SYSTEM_FOCUSED;
3565 }
3566 break;
3567 case blink::WebAXRoleListItem:
3568 ia_role_ = ROLE_SYSTEM_LISTITEM;
3569 ia_state_ |= STATE_SYSTEM_READONLY;
3570 break;
3571 case blink::WebAXRoleListMarker:
3572 ia_role_ = ROLE_SYSTEM_TEXT;
3573 ia_state_ |= STATE_SYSTEM_READONLY;
3574 break;
3575 case blink::WebAXRoleMath:
3576 ia_role_ = ROLE_SYSTEM_EQUATION;
3577 ia_state_ |= STATE_SYSTEM_READONLY;
3578 break;
3579 case blink::WebAXRoleMenu:
3580 case blink::WebAXRoleMenuButton:
3581 ia_role_ = ROLE_SYSTEM_MENUPOPUP;
3582 break;
3583 case blink::WebAXRoleMenuBar:
3584 ia_role_ = ROLE_SYSTEM_MENUBAR;
3585 break;
3586 case blink::WebAXRoleMenuItem:
3587 ia_role_ = ROLE_SYSTEM_MENUITEM;
3588 break;
3589 case blink::WebAXRoleMenuListPopup:
3590 ia_role_ = ROLE_SYSTEM_CLIENT;
3591 break;
3592 case blink::WebAXRoleMenuListOption:
3593 ia_role_ = ROLE_SYSTEM_LISTITEM;
3594 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3595 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3596 if (HasState(blink::WebAXStateFocused))
3597 ia_state_ |= STATE_SYSTEM_FOCUSED;
3598 }
3599 break;
3600 case blink::WebAXRoleNote:
3601 ia_role_ = ROLE_SYSTEM_GROUPING;
3602 ia2_role_ = IA2_ROLE_NOTE;
3603 ia_state_ |= STATE_SYSTEM_READONLY;
3604 break;
3605 case blink::WebAXRoleOutline:
3606 ia_role_ = ROLE_SYSTEM_OUTLINE;
3607 ia_state_ |= STATE_SYSTEM_READONLY;
3608 break;
3609 case blink::WebAXRoleParagraph:
3610 role_name_ = L"P";
3611 ia2_role_ = IA2_ROLE_PARAGRAPH;
3612 break;
3613 case blink::WebAXRolePopUpButton:
3614 if (html_tag == L"select") {
3615 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3616 } else {
3617 ia_role_ = ROLE_SYSTEM_BUTTONMENU;
3618 }
3619 break;
3620 case blink::WebAXRoleProgressIndicator:
3621 ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3622 ia_state_ |= STATE_SYSTEM_READONLY;
3623 break;
3624 case blink::WebAXRoleRadioButton:
3625 ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
3626 break;
3627 case blink::WebAXRoleRadioGroup:
3628 ia_role_ = ROLE_SYSTEM_GROUPING;
3629 ia2_role_ = IA2_ROLE_SECTION;
3630 break;
3631 case blink::WebAXRoleRegion:
3632 ia_role_ = ROLE_SYSTEM_GROUPING;
3633 ia2_role_ = IA2_ROLE_SECTION;
3634 ia_state_ |= STATE_SYSTEM_READONLY;
3635 break;
3636 case blink::WebAXRoleRow:
3637 ia_role_ = ROLE_SYSTEM_ROW;
3638 ia_state_ |= STATE_SYSTEM_READONLY;
3639 break;
3640 case blink::WebAXRoleRowHeader:
3641 ia_role_ = ROLE_SYSTEM_ROWHEADER;
3642 ia_state_ |= STATE_SYSTEM_READONLY;
3643 break;
3644 case blink::WebAXRoleRuler:
3645 ia_role_ = ROLE_SYSTEM_CLIENT;
3646 ia2_role_ = IA2_ROLE_RULER;
3647 ia_state_ |= STATE_SYSTEM_READONLY;
3648 break;
3649 case blink::WebAXRoleScrollArea:
3650 ia_role_ = ROLE_SYSTEM_CLIENT;
3651 ia2_role_ = IA2_ROLE_SCROLL_PANE;
3652 ia_state_ |= STATE_SYSTEM_READONLY;
3653 break;
3654 case blink::WebAXRoleScrollBar:
3655 ia_role_ = ROLE_SYSTEM_SCROLLBAR;
3656 break;
3657 case blink::WebAXRoleSlider:
3658 ia_role_ = ROLE_SYSTEM_SLIDER;
3659 break;
3660 case blink::WebAXRoleSpinButton:
3661 ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3662 break;
3663 case blink::WebAXRoleSpinButtonPart:
3664 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3665 break;
3666 case blink::WebAXRoleSplitGroup:
3667 ia_role_ = ROLE_SYSTEM_CLIENT;
3668 ia2_role_ = IA2_ROLE_SPLIT_PANE;
3669 ia_state_ |= STATE_SYSTEM_READONLY;
3670 break;
3671 case blink::WebAXRoleAnnotation:
3672 case blink::WebAXRoleStaticText:
3673 ia_role_ = ROLE_SYSTEM_TEXT;
3674 ia_state_ |= STATE_SYSTEM_READONLY;
3675 break;
3676 case blink::WebAXRoleStatus:
3677 ia_role_ = ROLE_SYSTEM_STATUSBAR;
3678 ia_state_ |= STATE_SYSTEM_READONLY;
3679 break;
3680 case blink::WebAXRoleSplitter:
3681 ia_role_ = ROLE_SYSTEM_SEPARATOR;
3682 break;
3683 case blink::WebAXRoleSVGRoot:
3684 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3685 break;
3686 case blink::WebAXRoleTab:
3687 ia_role_ = ROLE_SYSTEM_PAGETAB;
3688 break;
3689 case blink::WebAXRoleTable: {
3690 base::string16 aria_role = GetString16Attribute(
3691 AccessibilityNodeData::ATTR_ROLE);
3692 if (aria_role == L"treegrid") {
3693 ia_role_ = ROLE_SYSTEM_OUTLINE;
3694 } else {
3695 ia_role_ = ROLE_SYSTEM_TABLE;
3696 ia_state_ |= STATE_SYSTEM_READONLY;
3697 }
3698 break;
3699 }
3700 case blink::WebAXRoleTableHeaderContainer:
3701 ia_role_ = ROLE_SYSTEM_GROUPING;
3702 ia2_role_ = IA2_ROLE_SECTION;
3703 ia_state_ |= STATE_SYSTEM_READONLY;
3704 break;
3705 case blink::WebAXRoleTabList:
3706 ia_role_ = ROLE_SYSTEM_PAGETABLIST;
3707 break;
3708 case blink::WebAXRoleTabPanel:
3709 ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
3710 break;
3711 case blink::WebAXRoleToggleButton:
3712 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3713 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3714 break;
3715 case blink::WebAXRoleTextArea:
3716 ia_role_ = ROLE_SYSTEM_TEXT;
3717 ia2_state_ |= IA2_STATE_MULTI_LINE;
3718 ia2_state_ |= IA2_STATE_EDITABLE;
3719 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3720 break;
3721 case blink::WebAXRoleTextField:
3722 ia_role_ = ROLE_SYSTEM_TEXT;
3723 ia2_state_ |= IA2_STATE_SINGLE_LINE;
3724 ia2_state_ |= IA2_STATE_EDITABLE;
3725 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3726 break;
3727 case blink::WebAXRoleTimer:
3728 ia_role_ = ROLE_SYSTEM_CLOCK;
3729 ia_state_ |= STATE_SYSTEM_READONLY;
3730 break;
3731 case blink::WebAXRoleToolbar:
3732 ia_role_ = ROLE_SYSTEM_TOOLBAR;
3733 ia_state_ |= STATE_SYSTEM_READONLY;
3734 break;
3735 case blink::WebAXRoleUserInterfaceTooltip:
3736 ia_role_ = ROLE_SYSTEM_TOOLTIP;
3737 ia_state_ |= STATE_SYSTEM_READONLY;
3738 break;
3739 case blink::WebAXRoleTree:
3740 ia_role_ = ROLE_SYSTEM_OUTLINE;
3741 ia_state_ |= STATE_SYSTEM_READONLY;
3742 break;
3743 case blink::WebAXRoleTreeGrid:
3744 ia_role_ = ROLE_SYSTEM_OUTLINE;
3745 ia_state_ |= STATE_SYSTEM_READONLY;
3746 break;
3747 case blink::WebAXRoleTreeItem:
3748 ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
3749 ia_state_ |= STATE_SYSTEM_READONLY;
3750 break;
3751 case blink::WebAXRoleWindow:
3752 ia_role_ = ROLE_SYSTEM_WINDOW;
3753 break;
3754
3755 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3756 case blink::WebAXRoleBrowser:
3757 case blink::WebAXRoleDirectory:
3758 case blink::WebAXRoleDrawer:
3759 case blink::WebAXRoleHelpTag:
3760 case blink::WebAXRoleIgnored:
3761 case blink::WebAXRoleIncrementor:
3762 case blink::WebAXRoleLog:
3763 case blink::WebAXRoleMarquee:
3764 case blink::WebAXRoleMatte:
3765 case blink::WebAXRolePresentational:
3766 case blink::WebAXRoleRulerMarker:
3767 case blink::WebAXRoleSheet:
3768 case blink::WebAXRoleSliderThumb:
3769 case blink::WebAXRoleSystemWide:
3770 case blink::WebAXRoleValueIndicator:
3771 default:
3772 ia_role_ = ROLE_SYSTEM_CLIENT;
3773 break;
3774 }
3775
3776 // Compute the final value of READONLY for MSAA.
3777 //
3778 // We always set the READONLY state for elements that have the
3779 // aria-readonly attribute and for a few roles (in the switch above).
3780 // We clear the READONLY state on focusable controls and on a document.
3781 // Everything else, the majority of objects, do not have this state set.
3782 if (HasState(blink::WebAXStateFocusable) &&
3783 ia_role_ != ROLE_SYSTEM_DOCUMENT) {
3784 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3785 }
3786 if (!HasState(blink::WebAXStateReadonly))
3787 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3788 if (GetBoolAttribute(AccessibilityNodeData::ATTR_ARIA_READONLY))
3789 ia_state_ |= STATE_SYSTEM_READONLY;
3790
3791 // The role should always be set.
3792 DCHECK(!role_name_.empty() || ia_role_);
3793
3794 // If we didn't explicitly set the IAccessible2 role, make it the same
3795 // as the MSAA role.
3796 if (!ia2_role_)
3797 ia2_role_ = ia_role_;
3798 }
3799
3800 } // namespace content
3801