Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_win.cc
blob1d84b79e211f8e2c25a12a25274f8a3ccdf77a01
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.
5 #include "content/browser/accessibility/browser_accessibility_win.h"
7 #include <UIAutomationClient.h>
8 #include <UIAutomationCoreApi.h>
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/enum_variant.h"
15 #include "base/win/scoped_comptr.h"
16 #include "base/win/windows_version.h"
17 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
18 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
19 #include "content/common/accessibility_messages.h"
20 #include "content/public/common/content_client.h"
21 #include "ui/accessibility/ax_text_utils.h"
22 #include "ui/base/win/accessibility_ids_win.h"
23 #include "ui/base/win/accessibility_misc_utils.h"
24 #include "ui/base/win/atl_module.h"
26 namespace content {
28 // These nonstandard GUIDs are taken directly from the Mozilla sources
29 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
30 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
31 const GUID GUID_ISimpleDOM = {
32 0x0c539790, 0x12e4, 0x11cf,
33 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
34 const GUID GUID_IAccessibleContentDocument = {
35 0xa5d8e1f3, 0x3571, 0x4d8f,
36 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
38 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter = L'\xfffc';
40 // static
41 LONG BrowserAccessibilityWin::next_unique_id_win_ =
42 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
45 // BrowserAccessibilityRelation
47 // A simple implementation of IAccessibleRelation, used to represent
48 // a relationship between two accessible nodes in the tree.
51 class BrowserAccessibilityRelation
52 : public CComObjectRootEx<CComMultiThreadModel>,
53 public IAccessibleRelation {
54 BEGIN_COM_MAP(BrowserAccessibilityRelation)
55 COM_INTERFACE_ENTRY(IAccessibleRelation)
56 END_COM_MAP()
58 CONTENT_EXPORT BrowserAccessibilityRelation() {}
59 CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {}
61 CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner,
62 const base::string16& type);
63 CONTENT_EXPORT void AddTarget(int target_id);
65 // IAccessibleRelation methods.
66 CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type);
67 CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets);
68 CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target);
69 CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets,
70 IUnknown** targets,
71 long* n_targets);
73 // IAccessibleRelation methods not implemented.
74 CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) {
75 return E_NOTIMPL;
78 private:
79 base::string16 type_;
80 base::win::ScopedComPtr<BrowserAccessibilityWin> owner_;
81 std::vector<int> target_ids_;
84 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner,
85 const base::string16& type) {
86 owner_ = owner;
87 type_ = type;
90 void BrowserAccessibilityRelation::AddTarget(int target_id) {
91 target_ids_.push_back(target_id);
94 STDMETHODIMP BrowserAccessibilityRelation::get_relationType(
95 BSTR* relation_type) {
96 if (!relation_type)
97 return E_INVALIDARG;
99 if (!owner_->instance_active())
100 return E_FAIL;
102 *relation_type = SysAllocString(type_.c_str());
103 DCHECK(*relation_type);
104 return S_OK;
107 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) {
108 if (!n_targets)
109 return E_INVALIDARG;
111 if (!owner_->instance_active())
112 return E_FAIL;
114 *n_targets = static_cast<long>(target_ids_.size());
116 BrowserAccessibilityManager* manager = owner_->manager();
117 for (long i = *n_targets - 1; i >= 0; --i) {
118 BrowserAccessibility* result = manager->GetFromID(target_ids_[i]);
119 if (!result || !result->instance_active()) {
120 *n_targets = 0;
121 break;
124 return S_OK;
127 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index,
128 IUnknown** target) {
129 if (!target)
130 return E_INVALIDARG;
132 if (!owner_->instance_active())
133 return E_FAIL;
135 if (target_index < 0 ||
136 target_index >= static_cast<long>(target_ids_.size())) {
137 return E_INVALIDARG;
140 BrowserAccessibilityManager* manager = owner_->manager();
141 BrowserAccessibility* result =
142 manager->GetFromID(target_ids_[target_index]);
143 if (!result || !result->instance_active())
144 return E_FAIL;
146 *target = static_cast<IAccessible*>(
147 result->ToBrowserAccessibilityWin()->NewReference());
148 return S_OK;
151 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets,
152 IUnknown** targets,
153 long* n_targets) {
154 if (!targets || !n_targets)
155 return E_INVALIDARG;
157 if (!owner_->instance_active())
158 return E_FAIL;
160 long count = static_cast<long>(target_ids_.size());
161 if (count > max_targets)
162 count = max_targets;
164 *n_targets = count;
165 if (count == 0)
166 return S_FALSE;
168 for (long i = 0; i < count; ++i) {
169 HRESULT result = get_target(i, &targets[i]);
170 if (result != S_OK)
171 return result;
174 return S_OK;
178 // BrowserAccessibilityWin::WinAttributes
181 BrowserAccessibilityWin::WinAttributes::WinAttributes()
182 : ia_role(0),
183 ia_state(0),
184 ia2_role(0),
185 ia2_state(0) {
189 // BrowserAccessibilityWin
192 // static
193 BrowserAccessibility* BrowserAccessibility::Create() {
194 ui::win::CreateATLModuleIfNeeded();
195 CComObject<BrowserAccessibilityWin>* instance;
196 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
197 DCHECK(SUCCEEDED(hr));
198 return instance->NewReference();
201 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
202 return static_cast<BrowserAccessibilityWin*>(this);
205 BrowserAccessibilityWin::BrowserAccessibilityWin()
206 : win_attributes_(new WinAttributes()),
207 previous_scroll_x_(0),
208 previous_scroll_y_(0) {
209 // Start unique IDs at -1 and decrement each time, because get_accChild
210 // uses positive IDs to enumerate children, so we use negative IDs to
211 // clearly distinguish between indices and unique IDs.
212 unique_id_win_ = next_unique_id_win_;
213 if (next_unique_id_win_ ==
214 base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
215 next_unique_id_win_ =
216 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
218 next_unique_id_win_--;
221 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
222 for (size_t i = 0; i < relations_.size(); ++i)
223 relations_[i]->Release();
227 // IAccessible methods.
229 // Conventions:
230 // * Always test for instance_active() first and return E_FAIL if it's false.
231 // * Always check for invalid arguments first, even if they're unused.
232 // * Return S_FALSE if the only output is a string argument and it's empty.
235 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
236 if (!instance_active())
237 return E_FAIL;
239 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
240 if (!target)
241 return E_INVALIDARG;
243 manager()->DoDefaultAction(*target);
244 return S_OK;
247 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
248 LONG y_top,
249 VARIANT* child) {
250 if (!instance_active())
251 return E_FAIL;
253 if (!child)
254 return E_INVALIDARG;
256 gfx::Point point(x_left, y_top);
257 if (!GetGlobalBoundsRect().Contains(point)) {
258 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
259 child->vt = VT_EMPTY;
260 return S_FALSE;
263 BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
264 if (result == this) {
265 // Point is within this object.
266 child->vt = VT_I4;
267 child->lVal = CHILDID_SELF;
268 } else {
269 child->vt = VT_DISPATCH;
270 child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
272 return S_OK;
275 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
276 LONG* y_top,
277 LONG* width,
278 LONG* height,
279 VARIANT var_id) {
280 if (!instance_active())
281 return E_FAIL;
283 if (!x_left || !y_top || !width || !height)
284 return E_INVALIDARG;
286 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
287 if (!target)
288 return E_INVALIDARG;
290 gfx::Rect bounds = target->GetGlobalBoundsRect();
291 *x_left = bounds.x();
292 *y_top = bounds.y();
293 *width = bounds.width();
294 *height = bounds.height();
296 return S_OK;
299 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
300 VARIANT start,
301 VARIANT* end) {
302 BrowserAccessibilityWin* target = GetTargetFromChildID(start);
303 if (!target)
304 return E_INVALIDARG;
306 if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
307 start.lVal != CHILDID_SELF) {
308 // MSAA states that navigating to first/last child can only be from self.
309 return E_INVALIDARG;
312 uint32 child_count = target->PlatformChildCount();
314 BrowserAccessibility* result = NULL;
315 switch (nav_dir) {
316 case NAVDIR_DOWN:
317 case NAVDIR_UP:
318 case NAVDIR_LEFT:
319 case NAVDIR_RIGHT:
320 // These directions are not implemented, matching Mozilla and IE.
321 return E_NOTIMPL;
322 case NAVDIR_FIRSTCHILD:
323 if (child_count > 0)
324 result = target->PlatformGetChild(0);
325 break;
326 case NAVDIR_LASTCHILD:
327 if (child_count > 0)
328 result = target->PlatformGetChild(child_count - 1);
329 break;
330 case NAVDIR_NEXT:
331 result = target->GetNextSibling();
332 break;
333 case NAVDIR_PREVIOUS:
334 result = target->GetPreviousSibling();
335 break;
338 if (!result) {
339 end->vt = VT_EMPTY;
340 return S_FALSE;
343 end->vt = VT_DISPATCH;
344 end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
345 return S_OK;
348 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
349 IDispatch** disp_child) {
350 if (!instance_active())
351 return E_FAIL;
353 if (!disp_child)
354 return E_INVALIDARG;
356 *disp_child = NULL;
358 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
359 if (!target)
360 return E_INVALIDARG;
362 (*disp_child) = target->NewReference();
363 return S_OK;
366 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
367 if (!instance_active())
368 return E_FAIL;
370 if (!child_count)
371 return E_INVALIDARG;
373 *child_count = PlatformChildCount();
375 return S_OK;
378 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
379 BSTR* def_action) {
380 if (!instance_active())
381 return E_FAIL;
383 if (!def_action)
384 return E_INVALIDARG;
386 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
387 if (!target)
388 return E_INVALIDARG;
390 return target->GetStringAttributeAsBstr(
391 ui::AX_ATTR_ACTION, def_action);
394 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
395 BSTR* desc) {
396 if (!instance_active())
397 return E_FAIL;
399 if (!desc)
400 return E_INVALIDARG;
402 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
403 if (!target)
404 return E_INVALIDARG;
406 base::string16 description_str = target->description();
407 if (description_str.empty())
408 return S_FALSE;
410 *desc = SysAllocString(description_str.c_str());
412 DCHECK(*desc);
413 return S_OK;
416 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
417 if (!instance_active())
418 return E_FAIL;
420 if (!focus_child)
421 return E_INVALIDARG;
423 BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
424 manager()->GetFocus(this));
425 if (focus == this) {
426 focus_child->vt = VT_I4;
427 focus_child->lVal = CHILDID_SELF;
428 } else if (focus == NULL) {
429 focus_child->vt = VT_EMPTY;
430 } else {
431 focus_child->vt = VT_DISPATCH;
432 focus_child->pdispVal = focus->NewReference();
435 return S_OK;
438 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
439 if (!instance_active())
440 return E_FAIL;
442 if (!help)
443 return E_INVALIDARG;
445 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
446 if (!target)
447 return E_INVALIDARG;
449 base::string16 help_str = target->help();
450 if (help_str.empty())
451 return S_FALSE;
453 *help = SysAllocString(help_str.c_str());
455 DCHECK(*help);
456 return S_OK;
459 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
460 BSTR* acc_key) {
461 if (!instance_active())
462 return E_FAIL;
464 if (!acc_key)
465 return E_INVALIDARG;
467 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
468 if (!target)
469 return E_INVALIDARG;
471 return target->GetStringAttributeAsBstr(
472 ui::AX_ATTR_SHORTCUT, acc_key);
475 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
476 if (!instance_active())
477 return E_FAIL;
479 if (!name)
480 return E_INVALIDARG;
482 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
483 if (!target)
484 return E_INVALIDARG;
486 base::string16 name_str = target->name();
488 // If the name is empty, see if it's labeled by another element.
489 if (name_str.empty()) {
490 int title_elem_id;
491 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
492 &title_elem_id)) {
493 BrowserAccessibilityWin* title_elem =
494 manager()->GetFromID(title_elem_id)->ToBrowserAccessibilityWin();
495 if (title_elem)
496 name_str = title_elem->GetNameRecursive();
500 if (name_str.empty())
501 return S_FALSE;
503 *name = SysAllocString(name_str.c_str());
505 DCHECK(*name);
506 return S_OK;
509 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
510 if (!instance_active())
511 return E_FAIL;
513 if (!disp_parent)
514 return E_INVALIDARG;
516 IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
517 if (parent_obj == NULL) {
518 // This happens if we're the root of the tree;
519 // return the IAccessible for the window.
520 parent_obj =
521 manager()->ToBrowserAccessibilityManagerWin()->GetParentIAccessible();
522 // |parent| can only be NULL if the manager was created before the parent
523 // IAccessible was known and it wasn't subsequently set before a client
524 // requested it. This has been fixed. |parent| may also be NULL during
525 // destruction. Possible cases where this could occur include tabs being
526 // dragged to a new window, etc.
527 if (!parent_obj) {
528 DVLOG(1) << "In Function: "
529 << __FUNCTION__
530 << ". Parent IAccessible interface is NULL. Returning failure";
531 return E_FAIL;
534 parent_obj->AddRef();
535 *disp_parent = parent_obj;
536 return S_OK;
539 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
540 VARIANT* role) {
541 if (!instance_active())
542 return E_FAIL;
544 if (!role)
545 return E_INVALIDARG;
547 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
548 if (!target)
549 return E_INVALIDARG;
551 if (!target->role_name().empty()) {
552 role->vt = VT_BSTR;
553 role->bstrVal = SysAllocString(target->role_name().c_str());
554 } else {
555 role->vt = VT_I4;
556 role->lVal = target->ia_role();
558 return S_OK;
561 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
562 VARIANT* state) {
563 if (!instance_active())
564 return E_FAIL;
566 if (!state)
567 return E_INVALIDARG;
569 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
570 if (!target)
571 return E_INVALIDARG;
573 state->vt = VT_I4;
574 state->lVal = target->ia_state();
575 if (manager()->GetFocus(NULL) == this)
576 state->lVal |= STATE_SYSTEM_FOCUSED;
578 return S_OK;
581 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
582 BSTR* value) {
583 if (!instance_active())
584 return E_FAIL;
586 if (!value)
587 return E_INVALIDARG;
589 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
590 if (!target)
591 return E_INVALIDARG;
593 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
594 target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
595 target->ia_role() == ROLE_SYSTEM_SLIDER) {
596 base::string16 value_text = target->GetValueText();
597 *value = SysAllocString(value_text.c_str());
598 DCHECK(*value);
599 return S_OK;
602 // Expose color well value.
603 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
604 int r = target->GetIntAttribute(
605 ui::AX_ATTR_COLOR_VALUE_RED);
606 int g = target->GetIntAttribute(
607 ui::AX_ATTR_COLOR_VALUE_GREEN);
608 int b = target->GetIntAttribute(
609 ui::AX_ATTR_COLOR_VALUE_BLUE);
610 base::string16 value_text;
611 value_text = base::IntToString16((r * 100) / 255) + L"% red " +
612 base::IntToString16((g * 100) / 255) + L"% green " +
613 base::IntToString16((b * 100) / 255) + L"% blue";
614 *value = SysAllocString(value_text.c_str());
615 DCHECK(*value);
616 return S_OK;
619 *value = SysAllocString(target->value().c_str());
620 DCHECK(*value);
621 return S_OK;
624 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
625 VARIANT var_id,
626 LONG* topic_id) {
627 return E_NOTIMPL;
630 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
631 if (!instance_active())
632 return E_FAIL;
634 if (GetRole() != ui::AX_ROLE_LIST_BOX)
635 return E_NOTIMPL;
637 unsigned long selected_count = 0;
638 for (size_t i = 0; i < InternalChildCount(); ++i) {
639 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
640 ++selected_count;
643 if (selected_count == 0) {
644 selected->vt = VT_EMPTY;
645 return S_OK;
648 if (selected_count == 1) {
649 for (size_t i = 0; i < InternalChildCount(); ++i) {
650 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
651 selected->vt = VT_DISPATCH;
652 selected->pdispVal =
653 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
654 return S_OK;
659 // Multiple items are selected.
660 base::win::EnumVariant* enum_variant =
661 new base::win::EnumVariant(selected_count);
662 enum_variant->AddRef();
663 unsigned long index = 0;
664 for (size_t i = 0; i < InternalChildCount(); ++i) {
665 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
666 enum_variant->ItemAt(index)->vt = VT_DISPATCH;
667 enum_variant->ItemAt(index)->pdispVal =
668 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
669 ++index;
672 selected->vt = VT_UNKNOWN;
673 selected->punkVal = static_cast<IUnknown*>(
674 static_cast<base::win::IUnknownImpl*>(enum_variant));
675 return S_OK;
678 STDMETHODIMP BrowserAccessibilityWin::accSelect(
679 LONG flags_sel, VARIANT var_id) {
680 if (!instance_active())
681 return E_FAIL;
683 if (flags_sel & SELFLAG_TAKEFOCUS) {
684 manager()->SetFocus(this, true);
685 return S_OK;
688 return S_FALSE;
692 // IAccessible2 methods.
695 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
696 if (!instance_active())
697 return E_FAIL;
699 if (!role)
700 return E_INVALIDARG;
702 *role = ia2_role();
704 return S_OK;
707 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
708 if (!instance_active())
709 return E_FAIL;
711 if (!attributes)
712 return E_INVALIDARG;
714 // The iaccessible2 attributes are a set of key-value pairs
715 // separated by semicolons, with a colon between the key and the value.
716 base::string16 str;
717 const std::vector<base::string16>& attributes_list = ia2_attributes();
718 for (unsigned int i = 0; i < attributes_list.size(); ++i) {
719 str += attributes_list[i] + L';';
722 if (str.empty())
723 return S_FALSE;
725 *attributes = SysAllocString(str.c_str());
726 DCHECK(*attributes);
727 return S_OK;
730 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
731 if (!instance_active())
732 return E_FAIL;
734 if (!states)
735 return E_INVALIDARG;
737 *states = ia2_state();
739 return S_OK;
742 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
743 if (!instance_active())
744 return E_FAIL;
746 if (!unique_id)
747 return E_INVALIDARG;
749 *unique_id = unique_id_win_;
750 return S_OK;
753 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
754 if (!instance_active())
755 return E_FAIL;
757 if (!window_handle)
758 return E_INVALIDARG;
760 *window_handle =
761 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
762 if (!*window_handle)
763 return E_FAIL;
765 return S_OK;
768 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
769 if (!instance_active())
770 return E_FAIL;
772 if (!index_in_parent)
773 return E_INVALIDARG;
775 *index_in_parent = this->GetIndexInParent();
776 return S_OK;
779 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
780 if (!instance_active())
781 return E_FAIL;
783 if (!n_relations)
784 return E_INVALIDARG;
786 *n_relations = relations_.size();
787 return S_OK;
790 STDMETHODIMP BrowserAccessibilityWin::get_relation(
791 LONG relation_index,
792 IAccessibleRelation** relation) {
793 if (!instance_active())
794 return E_FAIL;
796 if (relation_index < 0 ||
797 relation_index >= static_cast<long>(relations_.size())) {
798 return E_INVALIDARG;
801 if (!relation)
802 return E_INVALIDARG;
804 relations_[relation_index]->AddRef();
805 *relation = relations_[relation_index];
806 return S_OK;
809 STDMETHODIMP BrowserAccessibilityWin::get_relations(
810 LONG max_relations,
811 IAccessibleRelation** relations,
812 LONG* n_relations) {
813 if (!instance_active())
814 return E_FAIL;
816 if (!relations || !n_relations)
817 return E_INVALIDARG;
819 long count = static_cast<long>(relations_.size());
820 *n_relations = count;
821 if (count == 0)
822 return S_FALSE;
824 for (long i = 0; i < count; ++i) {
825 relations_[i]->AddRef();
826 relations[i] = relations_[i];
829 return S_OK;
832 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
833 if (!instance_active())
834 return E_FAIL;
836 gfx::Rect r = GetLocation();
837 switch(scroll_type) {
838 case IA2_SCROLL_TYPE_TOP_LEFT:
839 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
840 break;
841 case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
842 manager()->ScrollToMakeVisible(
843 *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
844 break;
845 case IA2_SCROLL_TYPE_TOP_EDGE:
846 manager()->ScrollToMakeVisible(
847 *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
848 break;
849 case IA2_SCROLL_TYPE_BOTTOM_EDGE:
850 manager()->ScrollToMakeVisible(
851 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
852 break;
853 case IA2_SCROLL_TYPE_LEFT_EDGE:
854 manager()->ScrollToMakeVisible(
855 *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
856 break;
857 case IA2_SCROLL_TYPE_RIGHT_EDGE:
858 manager()->ScrollToMakeVisible(
859 *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
860 break;
861 case IA2_SCROLL_TYPE_ANYWHERE:
862 default:
863 manager()->ScrollToMakeVisible(*this, r);
864 break;
867 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
869 return S_OK;
872 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
873 enum IA2CoordinateType coordinate_type,
874 LONG x,
875 LONG y) {
876 if (!instance_active())
877 return E_FAIL;
879 gfx::Point scroll_to(x, y);
881 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
882 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
883 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
884 if (GetParent())
885 scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
886 } else {
887 return E_INVALIDARG;
890 manager()->ScrollToPoint(*this, scroll_to);
891 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
893 return S_OK;
896 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
897 LONG* group_level,
898 LONG* similar_items_in_group,
899 LONG* position_in_group) {
900 if (!instance_active())
901 return E_FAIL;
903 if (!group_level || !similar_items_in_group || !position_in_group)
904 return E_INVALIDARG;
906 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
907 GetParent() &&
908 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
909 *group_level = 0;
910 *similar_items_in_group = GetParent()->PlatformChildCount();
911 *position_in_group = GetIndexInParent() + 1;
912 return S_OK;
915 return E_NOTIMPL;
919 // IAccessibleApplication methods.
922 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
923 // No need to check |instance_active()| because this interface is
924 // global, and doesn't depend on any local state.
926 if (!app_name)
927 return E_INVALIDARG;
929 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
930 // the part before the "/".
931 std::vector<std::string> product_components;
932 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
933 DCHECK_EQ(2U, product_components.size());
934 if (product_components.size() != 2)
935 return E_FAIL;
936 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
937 DCHECK(*app_name);
938 return *app_name ? S_OK : E_FAIL;
941 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
942 // No need to check |instance_active()| because this interface is
943 // global, and doesn't depend on any local state.
945 if (!app_version)
946 return E_INVALIDARG;
948 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
949 // the part after the "/".
950 std::vector<std::string> product_components;
951 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
952 DCHECK_EQ(2U, product_components.size());
953 if (product_components.size() != 2)
954 return E_FAIL;
955 *app_version =
956 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
957 DCHECK(*app_version);
958 return *app_version ? S_OK : E_FAIL;
961 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
962 // No need to check |instance_active()| because this interface is
963 // global, and doesn't depend on any local state.
965 if (!toolkit_name)
966 return E_INVALIDARG;
968 // This is hard-coded; all products based on the Chromium engine
969 // will have the same toolkit name, so that assistive technology can
970 // detect any Chrome-based product.
971 *toolkit_name = SysAllocString(L"Chrome");
972 DCHECK(*toolkit_name);
973 return *toolkit_name ? S_OK : E_FAIL;
976 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
977 BSTR* toolkit_version) {
978 // No need to check |instance_active()| because this interface is
979 // global, and doesn't depend on any local state.
981 if (!toolkit_version)
982 return E_INVALIDARG;
984 std::string user_agent = GetContentClient()->GetUserAgent();
985 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
986 DCHECK(*toolkit_version);
987 return *toolkit_version ? S_OK : E_FAIL;
991 // IAccessibleImage methods.
994 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
995 if (!instance_active())
996 return E_FAIL;
998 if (!desc)
999 return E_INVALIDARG;
1001 if (description().empty())
1002 return S_FALSE;
1004 *desc = SysAllocString(description().c_str());
1006 DCHECK(*desc);
1007 return S_OK;
1010 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
1011 enum IA2CoordinateType coordinate_type,
1012 LONG* x,
1013 LONG* y) {
1014 if (!instance_active())
1015 return E_FAIL;
1017 if (!x || !y)
1018 return E_INVALIDARG;
1020 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1021 HWND parent_hwnd =
1022 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
1023 if (!parent_hwnd)
1024 return E_FAIL;
1025 POINT top_left = {0, 0};
1026 ::ClientToScreen(parent_hwnd, &top_left);
1027 *x = GetLocation().x() + top_left.x;
1028 *y = GetLocation().y() + top_left.y;
1029 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1030 *x = GetLocation().x();
1031 *y = GetLocation().y();
1032 if (GetParent()) {
1033 *x -= GetParent()->GetLocation().x();
1034 *y -= GetParent()->GetLocation().y();
1036 } else {
1037 return E_INVALIDARG;
1040 return S_OK;
1043 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1044 if (!instance_active())
1045 return E_FAIL;
1047 if (!height || !width)
1048 return E_INVALIDARG;
1050 *height = GetLocation().height();
1051 *width = GetLocation().width();
1052 return S_OK;
1056 // IAccessibleTable methods.
1059 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1060 long row,
1061 long column,
1062 IUnknown** accessible) {
1063 if (!instance_active())
1064 return E_FAIL;
1066 if (!accessible)
1067 return E_INVALIDARG;
1069 int columns;
1070 int rows;
1071 if (!GetIntAttribute(
1072 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1073 !GetIntAttribute(
1074 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1075 columns <= 0 ||
1076 rows <= 0) {
1077 return S_FALSE;
1080 if (row < 0 || row >= rows || column < 0 || column >= columns)
1081 return E_INVALIDARG;
1083 const std::vector<int32>& cell_ids = GetIntListAttribute(
1084 ui::AX_ATTR_CELL_IDS);
1085 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1087 int cell_id = cell_ids[row * columns + column];
1088 BrowserAccessibilityWin* cell = GetFromID(cell_id);
1089 if (cell) {
1090 *accessible = static_cast<IAccessible*>(cell->NewReference());
1091 return S_OK;
1094 *accessible = NULL;
1095 return E_INVALIDARG;
1098 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1099 if (!instance_active())
1100 return E_FAIL;
1102 if (!accessible)
1103 return E_INVALIDARG;
1105 // TODO(dmazzoni): implement
1106 return S_FALSE;
1109 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1110 long column,
1111 long* cell_index) {
1112 if (!instance_active())
1113 return E_FAIL;
1115 if (!cell_index)
1116 return E_INVALIDARG;
1118 int columns;
1119 int rows;
1120 if (!GetIntAttribute(
1121 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1122 !GetIntAttribute(
1123 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1124 columns <= 0 ||
1125 rows <= 0) {
1126 return S_FALSE;
1129 if (row < 0 || row >= rows || column < 0 || column >= columns)
1130 return E_INVALIDARG;
1132 const std::vector<int32>& cell_ids = GetIntListAttribute(
1133 ui::AX_ATTR_CELL_IDS);
1134 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1135 ui::AX_ATTR_UNIQUE_CELL_IDS);
1136 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1137 int cell_id = cell_ids[row * columns + column];
1138 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1139 if (unique_cell_ids[i] == cell_id) {
1140 *cell_index = (long)i;
1141 return S_OK;
1145 return S_FALSE;
1148 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1149 BSTR* description) {
1150 if (!instance_active())
1151 return E_FAIL;
1153 if (!description)
1154 return E_INVALIDARG;
1156 int columns;
1157 int rows;
1158 if (!GetIntAttribute(
1159 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1160 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1161 columns <= 0 ||
1162 rows <= 0) {
1163 return S_FALSE;
1166 if (column < 0 || column >= columns)
1167 return E_INVALIDARG;
1169 const std::vector<int32>& cell_ids = GetIntListAttribute(
1170 ui::AX_ATTR_CELL_IDS);
1171 for (int i = 0; i < rows; ++i) {
1172 int cell_id = cell_ids[i * columns + column];
1173 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1174 manager()->GetFromID(cell_id));
1175 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1176 base::string16 cell_name = cell->GetString16Attribute(
1177 ui::AX_ATTR_NAME);
1178 if (cell_name.size() > 0) {
1179 *description = SysAllocString(cell_name.c_str());
1180 return S_OK;
1183 if (cell->description().size() > 0) {
1184 *description = SysAllocString(cell->description().c_str());
1185 return S_OK;
1190 return S_FALSE;
1193 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1194 long row,
1195 long column,
1196 long* n_columns_spanned) {
1197 if (!instance_active())
1198 return E_FAIL;
1200 if (!n_columns_spanned)
1201 return E_INVALIDARG;
1203 int columns;
1204 int rows;
1205 if (!GetIntAttribute(
1206 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1207 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1208 columns <= 0 ||
1209 rows <= 0) {
1210 return S_FALSE;
1213 if (row < 0 || row >= rows || column < 0 || column >= columns)
1214 return E_INVALIDARG;
1216 const std::vector<int32>& cell_ids = GetIntListAttribute(
1217 ui::AX_ATTR_CELL_IDS);
1218 int cell_id = cell_ids[row * columns + column];
1219 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1220 manager()->GetFromID(cell_id));
1221 int colspan;
1222 if (cell &&
1223 cell->GetIntAttribute(
1224 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1225 colspan >= 1) {
1226 *n_columns_spanned = colspan;
1227 return S_OK;
1230 return S_FALSE;
1233 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1234 IAccessibleTable** accessible_table,
1235 long* starting_row_index) {
1236 // TODO(dmazzoni): implement
1237 return E_NOTIMPL;
1240 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1241 long* column_index) {
1242 if (!instance_active())
1243 return E_FAIL;
1245 if (!column_index)
1246 return E_INVALIDARG;
1248 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1249 ui::AX_ATTR_UNIQUE_CELL_IDS);
1250 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1251 if (cell_index < 0)
1252 return E_INVALIDARG;
1253 if (cell_index >= cell_id_count)
1254 return S_FALSE;
1256 int cell_id = unique_cell_ids[cell_index];
1257 BrowserAccessibilityWin* cell =
1258 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1259 int col_index;
1260 if (cell &&
1261 cell->GetIntAttribute(
1262 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1263 *column_index = col_index;
1264 return S_OK;
1267 return S_FALSE;
1270 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1271 if (!instance_active())
1272 return E_FAIL;
1274 if (!column_count)
1275 return E_INVALIDARG;
1277 int columns;
1278 if (GetIntAttribute(
1279 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
1280 *column_count = columns;
1281 return S_OK;
1284 return S_FALSE;
1287 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1288 if (!instance_active())
1289 return E_FAIL;
1291 if (!row_count)
1292 return E_INVALIDARG;
1294 int rows;
1295 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1296 *row_count = rows;
1297 return S_OK;
1300 return S_FALSE;
1303 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1304 if (!instance_active())
1305 return E_FAIL;
1307 if (!cell_count)
1308 return E_INVALIDARG;
1310 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1311 *cell_count = 0;
1312 return S_OK;
1315 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1316 if (!instance_active())
1317 return E_FAIL;
1319 if (!column_count)
1320 return E_INVALIDARG;
1322 *column_count = 0;
1323 return S_OK;
1326 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1327 if (!instance_active())
1328 return E_FAIL;
1330 if (!row_count)
1331 return E_INVALIDARG;
1333 *row_count = 0;
1334 return S_OK;
1337 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1338 BSTR* description) {
1339 if (!instance_active())
1340 return E_FAIL;
1342 if (!description)
1343 return E_INVALIDARG;
1345 int columns;
1346 int rows;
1347 if (!GetIntAttribute(
1348 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1349 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1350 columns <= 0 ||
1351 rows <= 0) {
1352 return S_FALSE;
1355 if (row < 0 || row >= rows)
1356 return E_INVALIDARG;
1358 const std::vector<int32>& cell_ids = GetIntListAttribute(
1359 ui::AX_ATTR_CELL_IDS);
1360 for (int i = 0; i < columns; ++i) {
1361 int cell_id = cell_ids[row * columns + i];
1362 BrowserAccessibilityWin* cell =
1363 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1364 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1365 base::string16 cell_name = cell->GetString16Attribute(
1366 ui::AX_ATTR_NAME);
1367 if (cell_name.size() > 0) {
1368 *description = SysAllocString(cell_name.c_str());
1369 return S_OK;
1372 if (cell->description().size() > 0) {
1373 *description = SysAllocString(cell->description().c_str());
1374 return S_OK;
1379 return S_FALSE;
1382 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1383 long column,
1384 long* n_rows_spanned) {
1385 if (!instance_active())
1386 return E_FAIL;
1388 if (!n_rows_spanned)
1389 return E_INVALIDARG;
1391 int columns;
1392 int rows;
1393 if (!GetIntAttribute(
1394 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1395 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1396 columns <= 0 ||
1397 rows <= 0) {
1398 return S_FALSE;
1401 if (row < 0 || row >= rows || column < 0 || column >= columns)
1402 return E_INVALIDARG;
1404 const std::vector<int32>& cell_ids = GetIntListAttribute(
1405 ui::AX_ATTR_CELL_IDS);
1406 int cell_id = cell_ids[row * columns + column];
1407 BrowserAccessibilityWin* cell =
1408 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1409 int rowspan;
1410 if (cell &&
1411 cell->GetIntAttribute(
1412 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1413 rowspan >= 1) {
1414 *n_rows_spanned = rowspan;
1415 return S_OK;
1418 return S_FALSE;
1421 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1422 IAccessibleTable** accessible_table,
1423 long* starting_column_index) {
1424 // TODO(dmazzoni): implement
1425 return E_NOTIMPL;
1428 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1429 long* row_index) {
1430 if (!instance_active())
1431 return E_FAIL;
1433 if (!row_index)
1434 return E_INVALIDARG;
1436 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1437 ui::AX_ATTR_UNIQUE_CELL_IDS);
1438 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1439 if (cell_index < 0)
1440 return E_INVALIDARG;
1441 if (cell_index >= cell_id_count)
1442 return S_FALSE;
1444 int cell_id = unique_cell_ids[cell_index];
1445 BrowserAccessibilityWin* cell =
1446 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1447 int cell_row_index;
1448 if (cell &&
1449 cell->GetIntAttribute(
1450 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1451 *row_index = cell_row_index;
1452 return S_OK;
1455 return S_FALSE;
1458 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1459 long** children,
1460 long* n_children) {
1461 if (!instance_active())
1462 return E_FAIL;
1464 if (!children || !n_children)
1465 return E_INVALIDARG;
1467 // TODO(dmazzoni): Implement this.
1468 *n_children = 0;
1469 return S_OK;
1472 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1473 long** columns,
1474 long* n_columns) {
1475 if (!instance_active())
1476 return E_FAIL;
1478 if (!columns || !n_columns)
1479 return E_INVALIDARG;
1481 // TODO(dmazzoni): Implement this.
1482 *n_columns = 0;
1483 return S_OK;
1486 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1487 long** rows,
1488 long* n_rows) {
1489 if (!instance_active())
1490 return E_FAIL;
1492 if (!rows || !n_rows)
1493 return E_INVALIDARG;
1495 // TODO(dmazzoni): Implement this.
1496 *n_rows = 0;
1497 return S_OK;
1500 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1501 if (!instance_active())
1502 return E_FAIL;
1504 if (!accessible)
1505 return E_INVALIDARG;
1507 // TODO(dmazzoni): implement
1508 return S_FALSE;
1511 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1512 long column,
1513 boolean* is_selected) {
1514 if (!instance_active())
1515 return E_FAIL;
1517 if (!is_selected)
1518 return E_INVALIDARG;
1520 // TODO(dmazzoni): Implement this.
1521 *is_selected = false;
1522 return S_OK;
1525 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1526 boolean* is_selected) {
1527 if (!instance_active())
1528 return E_FAIL;
1530 if (!is_selected)
1531 return E_INVALIDARG;
1533 // TODO(dmazzoni): Implement this.
1534 *is_selected = false;
1535 return S_OK;
1538 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
1539 long column,
1540 boolean* is_selected) {
1541 if (!instance_active())
1542 return E_FAIL;
1544 if (!is_selected)
1545 return E_INVALIDARG;
1547 // TODO(dmazzoni): Implement this.
1548 *is_selected = false;
1549 return S_OK;
1552 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1553 long index,
1554 long* row,
1555 long* column,
1556 long* row_extents,
1557 long* column_extents,
1558 boolean* is_selected) {
1559 if (!instance_active())
1560 return E_FAIL;
1562 if (!row || !column || !row_extents || !column_extents || !is_selected)
1563 return E_INVALIDARG;
1565 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1566 ui::AX_ATTR_UNIQUE_CELL_IDS);
1567 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1568 if (index < 0)
1569 return E_INVALIDARG;
1570 if (index >= cell_id_count)
1571 return S_FALSE;
1573 int cell_id = unique_cell_ids[index];
1574 BrowserAccessibilityWin* cell =
1575 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1576 int rowspan;
1577 int colspan;
1578 if (cell &&
1579 cell->GetIntAttribute(
1580 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1581 cell->GetIntAttribute(
1582 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1583 rowspan >= 1 &&
1584 colspan >= 1) {
1585 *row_extents = rowspan;
1586 *column_extents = colspan;
1587 return S_OK;
1590 return S_FALSE;
1594 // IAccessibleTable2 methods.
1597 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1598 long column,
1599 IUnknown** cell) {
1600 return get_accessibleAt(row, column, cell);
1603 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1604 return get_nSelectedChildren(cell_count);
1607 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1608 IUnknown*** cells,
1609 long* n_selected_cells) {
1610 if (!instance_active())
1611 return E_FAIL;
1613 if (!cells || !n_selected_cells)
1614 return E_INVALIDARG;
1616 // TODO(dmazzoni): Implement this.
1617 *n_selected_cells = 0;
1618 return S_OK;
1621 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1622 long* n_columns) {
1623 if (!instance_active())
1624 return E_FAIL;
1626 if (!columns || !n_columns)
1627 return E_INVALIDARG;
1629 // TODO(dmazzoni): Implement this.
1630 *n_columns = 0;
1631 return S_OK;
1634 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1635 long* n_rows) {
1636 if (!instance_active())
1637 return E_FAIL;
1639 if (!rows || !n_rows)
1640 return E_INVALIDARG;
1642 // TODO(dmazzoni): Implement this.
1643 *n_rows = 0;
1644 return S_OK;
1649 // IAccessibleTableCell methods.
1652 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1653 long* n_columns_spanned) {
1654 if (!instance_active())
1655 return E_FAIL;
1657 if (!n_columns_spanned)
1658 return E_INVALIDARG;
1660 int colspan;
1661 if (GetIntAttribute(
1662 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1663 colspan >= 1) {
1664 *n_columns_spanned = colspan;
1665 return S_OK;
1668 return S_FALSE;
1671 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1672 IUnknown*** cell_accessibles,
1673 long* n_column_header_cells) {
1674 if (!instance_active())
1675 return E_FAIL;
1677 if (!cell_accessibles || !n_column_header_cells)
1678 return E_INVALIDARG;
1680 *n_column_header_cells = 0;
1682 int column;
1683 if (!GetIntAttribute(
1684 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1685 return S_FALSE;
1688 BrowserAccessibility* table = GetParent();
1689 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1690 table = table->GetParent();
1691 if (!table) {
1692 NOTREACHED();
1693 return S_FALSE;
1696 int columns;
1697 int rows;
1698 if (!table->GetIntAttribute(
1699 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1700 !table->GetIntAttribute(
1701 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1702 return S_FALSE;
1704 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1705 return S_FALSE;
1707 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1708 ui::AX_ATTR_CELL_IDS);
1710 for (int i = 0; i < rows; ++i) {
1711 int cell_id = cell_ids[i * columns + column];
1712 BrowserAccessibilityWin* cell =
1713 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1714 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
1715 (*n_column_header_cells)++;
1718 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1719 (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1720 int index = 0;
1721 for (int i = 0; i < rows; ++i) {
1722 int cell_id = cell_ids[i * columns + column];
1723 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1724 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1725 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1726 cell->ToBrowserAccessibilityWin()->NewReference());
1727 ++index;
1731 return S_OK;
1734 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1735 if (!instance_active())
1736 return E_FAIL;
1738 if (!column_index)
1739 return E_INVALIDARG;
1741 int column;
1742 if (GetIntAttribute(
1743 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1744 *column_index = column;
1745 return S_OK;
1748 return S_FALSE;
1751 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1752 if (!instance_active())
1753 return E_FAIL;
1755 if (!n_rows_spanned)
1756 return E_INVALIDARG;
1758 int rowspan;
1759 if (GetIntAttribute(
1760 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1761 rowspan >= 1) {
1762 *n_rows_spanned = rowspan;
1763 return S_OK;
1766 return S_FALSE;
1769 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1770 IUnknown*** cell_accessibles,
1771 long* n_row_header_cells) {
1772 if (!instance_active())
1773 return E_FAIL;
1775 if (!cell_accessibles || !n_row_header_cells)
1776 return E_INVALIDARG;
1778 *n_row_header_cells = 0;
1780 int row;
1781 if (!GetIntAttribute(
1782 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1783 return S_FALSE;
1786 BrowserAccessibility* table = GetParent();
1787 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1788 table = table->GetParent();
1789 if (!table) {
1790 NOTREACHED();
1791 return S_FALSE;
1794 int columns;
1795 int rows;
1796 if (!table->GetIntAttribute(
1797 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1798 !table->GetIntAttribute(
1799 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1800 return S_FALSE;
1802 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1803 return S_FALSE;
1805 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1806 ui::AX_ATTR_CELL_IDS);
1808 for (int i = 0; i < columns; ++i) {
1809 int cell_id = cell_ids[row * columns + i];
1810 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1811 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1812 (*n_row_header_cells)++;
1815 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1816 (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1817 int index = 0;
1818 for (int i = 0; i < columns; ++i) {
1819 int cell_id = cell_ids[row * columns + i];
1820 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1821 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1822 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1823 cell->ToBrowserAccessibilityWin()->NewReference());
1824 ++index;
1828 return S_OK;
1831 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1832 if (!instance_active())
1833 return E_FAIL;
1835 if (!row_index)
1836 return E_INVALIDARG;
1838 int row;
1839 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1840 *row_index = row;
1841 return S_OK;
1843 return S_FALSE;
1846 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1847 if (!instance_active())
1848 return E_FAIL;
1850 if (!is_selected)
1851 return E_INVALIDARG;
1853 *is_selected = false;
1854 return S_OK;
1857 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1858 long* row_index,
1859 long* column_index,
1860 long* row_extents,
1861 long* column_extents,
1862 boolean* is_selected) {
1863 if (!instance_active())
1864 return E_FAIL;
1866 if (!row_index ||
1867 !column_index ||
1868 !row_extents ||
1869 !column_extents ||
1870 !is_selected) {
1871 return E_INVALIDARG;
1874 int row;
1875 int column;
1876 int rowspan;
1877 int colspan;
1878 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1879 GetIntAttribute(
1880 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1881 GetIntAttribute(
1882 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1883 GetIntAttribute(
1884 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1885 *row_index = row;
1886 *column_index = column;
1887 *row_extents = rowspan;
1888 *column_extents = colspan;
1889 *is_selected = false;
1890 return S_OK;
1893 return S_FALSE;
1896 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1897 if (!instance_active())
1898 return E_FAIL;
1900 if (!table)
1901 return E_INVALIDARG;
1904 int row;
1905 int column;
1906 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1907 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1909 BrowserAccessibility* find_table = GetParent();
1910 while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
1911 find_table = find_table->GetParent();
1912 if (!find_table) {
1913 NOTREACHED();
1914 return S_FALSE;
1917 *table = static_cast<IAccessibleTable*>(
1918 find_table->ToBrowserAccessibilityWin()->NewReference());
1920 return S_OK;
1924 // IAccessibleText methods.
1927 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1928 if (!instance_active())
1929 return E_FAIL;
1931 if (!n_characters)
1932 return E_INVALIDARG;
1934 *n_characters = TextForIAccessibleText().length();
1935 return S_OK;
1938 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1939 if (!instance_active())
1940 return E_FAIL;
1942 if (!offset)
1943 return E_INVALIDARG;
1945 // IA2 spec says that caret offset should be -1 if the object is not focused.
1946 if (manager()->GetFocus(this) != this) {
1947 *offset = -1;
1948 return S_FALSE;
1951 *offset = 0;
1952 if (IsEditableText()) {
1953 int sel_start = 0;
1954 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1955 &sel_start))
1956 *offset = sel_start;
1959 return S_OK;
1962 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1963 LONG offset,
1964 enum IA2CoordinateType coordinate_type,
1965 LONG* out_x,
1966 LONG* out_y,
1967 LONG* out_width,
1968 LONG* out_height) {
1969 if (!instance_active())
1970 return E_FAIL;
1972 if (!out_x || !out_y || !out_width || !out_height)
1973 return E_INVALIDARG;
1975 const base::string16& text_str = TextForIAccessibleText();
1976 HandleSpecialTextOffset(text_str, &offset);
1978 if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1979 return E_INVALIDARG;
1981 gfx::Rect character_bounds;
1982 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1983 character_bounds = GetGlobalBoundsForRange(offset, 1);
1984 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1985 character_bounds = GetLocalBoundsForRange(offset, 1);
1986 character_bounds -= GetLocation().OffsetFromOrigin();
1987 } else {
1988 return E_INVALIDARG;
1991 *out_x = character_bounds.x();
1992 *out_y = character_bounds.y();
1993 *out_width = character_bounds.width();
1994 *out_height = character_bounds.height();
1996 return S_OK;
1999 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
2000 if (!instance_active())
2001 return E_FAIL;
2003 if (!n_selections)
2004 return E_INVALIDARG;
2006 *n_selections = 0;
2007 if (IsEditableText()) {
2008 int sel_start = 0;
2009 int sel_end = 0;
2010 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
2011 &sel_start) &&
2012 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
2013 sel_start != sel_end)
2014 *n_selections = 1;
2017 return S_OK;
2020 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
2021 LONG* start_offset,
2022 LONG* end_offset) {
2023 if (!instance_active())
2024 return E_FAIL;
2026 if (!start_offset || !end_offset || selection_index != 0)
2027 return E_INVALIDARG;
2029 LONG n_selections = 0;
2030 if (FAILED(get_nSelections(&n_selections)) || n_selections < 1)
2031 return E_INVALIDARG;
2033 *start_offset = 0;
2034 *end_offset = 0;
2035 if (IsEditableText()) {
2036 int sel_start = 0;
2037 int sel_end = 0;
2038 if (GetIntAttribute(
2039 ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
2040 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
2041 *start_offset = sel_start;
2042 *end_offset = sel_end;
2046 return S_OK;
2049 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2050 LONG end_offset,
2051 BSTR* text) {
2052 if (!instance_active())
2053 return E_FAIL;
2055 if (!text)
2056 return E_INVALIDARG;
2058 const base::string16& text_str = TextForIAccessibleText();
2060 // Handle special text offsets.
2061 HandleSpecialTextOffset(text_str, &start_offset);
2062 HandleSpecialTextOffset(text_str, &end_offset);
2064 // The spec allows the arguments to be reversed.
2065 if (start_offset > end_offset) {
2066 LONG tmp = start_offset;
2067 start_offset = end_offset;
2068 end_offset = tmp;
2071 // The spec does not allow the start or end offsets to be out or range;
2072 // we must return an error if so.
2073 LONG len = text_str.length();
2074 if (start_offset < 0)
2075 return E_INVALIDARG;
2076 if (end_offset > len)
2077 return E_INVALIDARG;
2079 base::string16 substr = text_str.substr(start_offset,
2080 end_offset - start_offset);
2081 if (substr.empty())
2082 return S_FALSE;
2084 *text = SysAllocString(substr.c_str());
2085 DCHECK(*text);
2086 return S_OK;
2089 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2090 LONG offset,
2091 enum IA2TextBoundaryType boundary_type,
2092 LONG* start_offset,
2093 LONG* end_offset,
2094 BSTR* text) {
2095 if (!instance_active())
2096 return E_FAIL;
2098 if (!start_offset || !end_offset || !text)
2099 return E_INVALIDARG;
2101 const base::string16& text_str = TextForIAccessibleText();
2102 HandleSpecialTextOffset(text_str, &offset);
2103 if (offset < 0)
2104 return E_INVALIDARG;
2106 LONG text_len = text_str.length();
2107 if (offset > text_len)
2108 return E_INVALIDARG;
2110 // The IAccessible2 spec says we don't have to implement the "sentence"
2111 // boundary type, we can just let the screenreader handle it.
2112 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2113 *start_offset = 0;
2114 *end_offset = 0;
2115 *text = NULL;
2116 return S_FALSE;
2119 // According to the IA2 Spec, only line boundaries should succeed when
2120 // the offset is one past the end of the text.
2121 if (offset == text_len && boundary_type != IA2_TEXT_BOUNDARY_LINE) {
2122 *start_offset = 0;
2123 *end_offset = 0;
2124 *text = nullptr;
2125 return S_FALSE;
2128 *start_offset = FindBoundary(
2129 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2130 *end_offset = FindBoundary(
2131 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2132 return get_text(*start_offset, *end_offset, text);
2135 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2136 LONG offset,
2137 enum IA2TextBoundaryType boundary_type,
2138 LONG* start_offset,
2139 LONG* end_offset,
2140 BSTR* text) {
2141 if (!instance_active())
2142 return E_FAIL;
2144 if (!start_offset || !end_offset || !text)
2145 return E_INVALIDARG;
2147 // The IAccessible2 spec says we don't have to implement the "sentence"
2148 // boundary type, we can just let the screenreader handle it.
2149 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2150 *start_offset = 0;
2151 *end_offset = 0;
2152 *text = NULL;
2153 return S_FALSE;
2156 const base::string16& text_str = TextForIAccessibleText();
2158 *start_offset = FindBoundary(
2159 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2160 *end_offset = offset;
2161 return get_text(*start_offset, *end_offset, text);
2164 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2165 LONG offset,
2166 enum IA2TextBoundaryType boundary_type,
2167 LONG* start_offset,
2168 LONG* end_offset,
2169 BSTR* text) {
2170 if (!instance_active())
2171 return E_FAIL;
2173 if (!start_offset || !end_offset || !text)
2174 return E_INVALIDARG;
2176 // The IAccessible2 spec says we don't have to implement the "sentence"
2177 // boundary type, we can just let the screenreader handle it.
2178 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2179 *start_offset = 0;
2180 *end_offset = 0;
2181 *text = NULL;
2182 return S_FALSE;
2185 const base::string16& text_str = TextForIAccessibleText();
2187 *start_offset = offset;
2188 *end_offset = FindBoundary(
2189 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2190 return get_text(*start_offset, *end_offset, text);
2193 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2194 if (!instance_active())
2195 return E_FAIL;
2197 if (!new_text)
2198 return E_INVALIDARG;
2200 if (!old_win_attributes_)
2201 return E_FAIL;
2203 int start, old_len, new_len;
2204 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
2205 if (new_len == 0)
2206 return E_FAIL;
2208 base::string16 substr = hypertext().substr(start, new_len);
2209 new_text->text = SysAllocString(substr.c_str());
2210 new_text->start = static_cast<long>(start);
2211 new_text->end = static_cast<long>(start + new_len);
2212 return S_OK;
2215 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2216 if (!instance_active())
2217 return E_FAIL;
2219 if (!old_text)
2220 return E_INVALIDARG;
2222 if (!old_win_attributes_)
2223 return E_FAIL;
2225 int start, old_len, new_len;
2226 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
2227 if (old_len == 0)
2228 return E_FAIL;
2230 base::string16 old_hypertext = old_win_attributes_->hypertext;
2231 base::string16 substr = old_hypertext.substr(start, old_len);
2232 old_text->text = SysAllocString(substr.c_str());
2233 old_text->start = static_cast<long>(start);
2234 old_text->end = static_cast<long>(start + old_len);
2235 return S_OK;
2238 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2239 LONG x,
2240 LONG y,
2241 enum IA2CoordinateType coord_type,
2242 LONG* offset) {
2243 if (!instance_active())
2244 return E_FAIL;
2246 if (!offset)
2247 return E_INVALIDARG;
2249 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2250 // screen readers still return partially accurate results rather than
2251 // completely failing.
2252 *offset = 0;
2253 return S_OK;
2256 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2257 LONG start_index,
2258 LONG end_index,
2259 enum IA2ScrollType scroll_type) {
2260 // TODO(dmazzoni): adjust this for the start and end index, too.
2261 return scrollTo(scroll_type);
2264 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2265 LONG start_index,
2266 LONG end_index,
2267 enum IA2CoordinateType coordinate_type,
2268 LONG x, LONG y) {
2269 // TODO(dmazzoni): adjust this for the start and end index, too.
2270 return scrollToPoint(coordinate_type, x, y);
2273 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2274 LONG end_offset) {
2275 if (!instance_active())
2276 return E_FAIL;
2278 const base::string16& text_str = TextForIAccessibleText();
2279 HandleSpecialTextOffset(text_str, &start_offset);
2280 HandleSpecialTextOffset(text_str, &end_offset);
2282 manager()->SetTextSelection(*this, start_offset, end_offset);
2283 return S_OK;
2286 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2287 if (!instance_active())
2288 return E_FAIL;
2290 if (selection_index != 0)
2291 return E_INVALIDARG;
2293 manager()->SetTextSelection(*this, 0, 0);
2294 return S_OK;
2297 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2298 if (!instance_active())
2299 return E_FAIL;
2301 const base::string16& text_str = TextForIAccessibleText();
2302 HandleSpecialTextOffset(text_str, &offset);
2303 manager()->SetTextSelection(*this, offset, offset);
2304 return S_OK;
2307 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2308 LONG start_offset,
2309 LONG end_offset) {
2310 if (!instance_active())
2311 return E_FAIL;
2313 if (selection_index != 0)
2314 return E_INVALIDARG;
2316 const base::string16& text_str = TextForIAccessibleText();
2317 HandleSpecialTextOffset(text_str, &start_offset);
2318 HandleSpecialTextOffset(text_str, &end_offset);
2320 manager()->SetTextSelection(*this, start_offset, end_offset);
2321 return S_OK;
2325 // IAccessibleHypertext methods.
2328 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2329 if (!instance_active())
2330 return E_FAIL;
2332 if (!hyperlink_count)
2333 return E_INVALIDARG;
2335 *hyperlink_count = hyperlink_offset_to_index().size();
2336 return S_OK;
2339 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2340 long index,
2341 IAccessibleHyperlink** hyperlink) {
2342 if (!instance_active())
2343 return E_FAIL;
2345 if (!hyperlink ||
2346 index < 0 ||
2347 index >= static_cast<long>(hyperlinks().size())) {
2348 return E_INVALIDARG;
2351 int32 id = hyperlinks()[index];
2352 BrowserAccessibilityWin* child =
2353 manager()->GetFromID(id)->ToBrowserAccessibilityWin();
2354 if (child) {
2355 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2356 return S_OK;
2359 return E_FAIL;
2362 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2363 long char_index,
2364 long* hyperlink_index) {
2365 if (!instance_active())
2366 return E_FAIL;
2368 if (!hyperlink_index)
2369 return E_INVALIDARG;
2371 *hyperlink_index = -1;
2373 if (char_index < 0 ||
2374 char_index >= static_cast<long>(hypertext().size())) {
2375 return E_INVALIDARG;
2378 std::map<int32, int32>::iterator it =
2379 hyperlink_offset_to_index().find(char_index);
2380 if (it == hyperlink_offset_to_index().end())
2381 return E_FAIL;
2383 *hyperlink_index = it->second;
2384 return S_OK;
2388 // IAccessibleValue methods.
2391 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2392 if (!instance_active())
2393 return E_FAIL;
2395 if (!value)
2396 return E_INVALIDARG;
2398 float float_val;
2399 if (GetFloatAttribute(
2400 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
2401 value->vt = VT_R8;
2402 value->dblVal = float_val;
2403 return S_OK;
2406 value->vt = VT_EMPTY;
2407 return S_FALSE;
2410 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2411 if (!instance_active())
2412 return E_FAIL;
2414 if (!value)
2415 return E_INVALIDARG;
2417 float float_val;
2418 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
2419 &float_val)) {
2420 value->vt = VT_R8;
2421 value->dblVal = float_val;
2422 return S_OK;
2425 value->vt = VT_EMPTY;
2426 return S_FALSE;
2429 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2430 if (!instance_active())
2431 return E_FAIL;
2433 if (!value)
2434 return E_INVALIDARG;
2436 float float_val;
2437 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
2438 &float_val)) {
2439 value->vt = VT_R8;
2440 value->dblVal = float_val;
2441 return S_OK;
2444 value->vt = VT_EMPTY;
2445 return S_FALSE;
2448 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2449 // TODO(dmazzoni): Implement this.
2450 return E_NOTIMPL;
2454 // ISimpleDOMDocument methods.
2457 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2458 if (!instance_active())
2459 return E_FAIL;
2461 if (!url)
2462 return E_INVALIDARG;
2464 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
2467 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2468 if (!instance_active())
2469 return E_FAIL;
2471 if (!title)
2472 return E_INVALIDARG;
2474 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
2477 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2478 if (!instance_active())
2479 return E_FAIL;
2481 if (!mime_type)
2482 return E_INVALIDARG;
2484 return GetStringAttributeAsBstr(
2485 ui::AX_ATTR_DOC_MIMETYPE, mime_type);
2488 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2489 if (!instance_active())
2490 return E_FAIL;
2492 if (!doc_type)
2493 return E_INVALIDARG;
2495 return GetStringAttributeAsBstr(
2496 ui::AX_ATTR_DOC_DOCTYPE, doc_type);
2500 // ISimpleDOMNode methods.
2503 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2504 BSTR* node_name,
2505 short* name_space_id,
2506 BSTR* node_value,
2507 unsigned int* num_children,
2508 unsigned int* unique_id,
2509 unsigned short* node_type) {
2510 if (!instance_active())
2511 return E_FAIL;
2513 if (!node_name || !name_space_id || !node_value || !num_children ||
2514 !unique_id || !node_type) {
2515 return E_INVALIDARG;
2518 base::string16 tag;
2519 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
2520 *node_name = SysAllocString(tag.c_str());
2521 else
2522 *node_name = NULL;
2524 *name_space_id = 0;
2525 *node_value = SysAllocString(value().c_str());
2526 *num_children = PlatformChildCount();
2527 *unique_id = unique_id_win_;
2529 if (ia_role() == ROLE_SYSTEM_DOCUMENT) {
2530 *node_type = NODETYPE_DOCUMENT;
2531 } else if (ia_role() == ROLE_SYSTEM_TEXT &&
2532 ((ia2_state() & IA2_STATE_EDITABLE) == 0)) {
2533 *node_type = NODETYPE_TEXT;
2534 } else {
2535 *node_type = NODETYPE_ELEMENT;
2538 return S_OK;
2541 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2542 unsigned short max_attribs,
2543 BSTR* attrib_names,
2544 short* name_space_id,
2545 BSTR* attrib_values,
2546 unsigned short* num_attribs) {
2547 if (!instance_active())
2548 return E_FAIL;
2550 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2551 return E_INVALIDARG;
2553 *num_attribs = max_attribs;
2554 if (*num_attribs > GetHtmlAttributes().size())
2555 *num_attribs = GetHtmlAttributes().size();
2557 for (unsigned short i = 0; i < *num_attribs; ++i) {
2558 attrib_names[i] = SysAllocString(
2559 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
2560 name_space_id[i] = 0;
2561 attrib_values[i] = SysAllocString(
2562 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
2564 return S_OK;
2567 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2568 unsigned short num_attribs,
2569 BSTR* attrib_names,
2570 short* name_space_id,
2571 BSTR* attrib_values) {
2572 if (!instance_active())
2573 return E_FAIL;
2575 if (!attrib_names || !name_space_id || !attrib_values)
2576 return E_INVALIDARG;
2578 for (unsigned short i = 0; i < num_attribs; ++i) {
2579 name_space_id[i] = 0;
2580 bool found = false;
2581 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2582 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) {
2583 if (GetHtmlAttributes()[j].first == name) {
2584 attrib_values[i] = SysAllocString(
2585 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
2586 found = true;
2587 break;
2590 if (!found) {
2591 attrib_values[i] = NULL;
2594 return S_OK;
2597 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2598 unsigned short max_style_properties,
2599 boolean use_alternate_view,
2600 BSTR* style_properties,
2601 BSTR* style_values,
2602 unsigned short *num_style_properties) {
2603 if (!instance_active())
2604 return E_FAIL;
2606 if (!style_properties || !style_values)
2607 return E_INVALIDARG;
2609 // We only cache a single style property for now: DISPLAY
2611 base::string16 display;
2612 if (max_style_properties == 0 ||
2613 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
2614 *num_style_properties = 0;
2615 return S_OK;
2618 *num_style_properties = 1;
2619 style_properties[0] = SysAllocString(L"display");
2620 style_values[0] = SysAllocString(display.c_str());
2622 return S_OK;
2625 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2626 unsigned short num_style_properties,
2627 boolean use_alternate_view,
2628 BSTR* style_properties,
2629 BSTR* style_values) {
2630 if (!instance_active())
2631 return E_FAIL;
2633 if (!style_properties || !style_values)
2634 return E_INVALIDARG;
2636 // We only cache a single style property for now: DISPLAY
2638 for (unsigned short i = 0; i < num_style_properties; ++i) {
2639 base::string16 name = (LPCWSTR)style_properties[i];
2640 base::StringToLowerASCII(&name);
2641 if (name == L"display") {
2642 base::string16 display = GetString16Attribute(
2643 ui::AX_ATTR_DISPLAY);
2644 style_values[i] = SysAllocString(display.c_str());
2645 } else {
2646 style_values[i] = NULL;
2650 return S_OK;
2653 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2654 return scrollTo(placeTopLeft ?
2655 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2658 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2659 if (!instance_active())
2660 return E_FAIL;
2662 if (!node)
2663 return E_INVALIDARG;
2665 *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
2666 return S_OK;
2669 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
2670 if (!instance_active())
2671 return E_FAIL;
2673 if (!node)
2674 return E_INVALIDARG;
2676 if (PlatformChildCount() == 0) {
2677 *node = NULL;
2678 return S_FALSE;
2681 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2682 return S_OK;
2685 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2686 if (!instance_active())
2687 return E_FAIL;
2689 if (!node)
2690 return E_INVALIDARG;
2692 if (PlatformChildCount() == 0) {
2693 *node = NULL;
2694 return S_FALSE;
2697 *node = PlatformGetChild(PlatformChildCount() - 1)
2698 ->ToBrowserAccessibilityWin()->NewReference();
2699 return S_OK;
2702 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2703 ISimpleDOMNode** node) {
2704 if (!instance_active())
2705 return E_FAIL;
2707 if (!node)
2708 return E_INVALIDARG;
2710 if (!GetParent() || GetIndexInParent() <= 0) {
2711 *node = NULL;
2712 return S_FALSE;
2715 *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2716 ToBrowserAccessibilityWin()->NewReference();
2717 return S_OK;
2720 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2721 if (!instance_active())
2722 return E_FAIL;
2724 if (!node)
2725 return E_INVALIDARG;
2727 if (!GetParent() ||
2728 GetIndexInParent() < 0 ||
2729 GetIndexInParent() >= static_cast<int>(
2730 GetParent()->InternalChildCount()) - 1) {
2731 *node = NULL;
2732 return S_FALSE;
2735 *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2736 ToBrowserAccessibilityWin()->NewReference();
2737 return S_OK;
2740 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2741 unsigned int child_index,
2742 ISimpleDOMNode** node) {
2743 if (!instance_active())
2744 return E_FAIL;
2746 if (!node)
2747 return E_INVALIDARG;
2749 if (child_index >= PlatformChildCount())
2750 return E_INVALIDARG;
2752 BrowserAccessibility* child = PlatformGetChild(child_index);
2753 if (!child) {
2754 *node = NULL;
2755 return S_FALSE;
2758 *node = child->ToBrowserAccessibilityWin()->NewReference();
2759 return S_OK;
2763 // ISimpleDOMText methods.
2766 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2767 if (!instance_active())
2768 return E_FAIL;
2770 if (!dom_text)
2771 return E_INVALIDARG;
2773 return GetStringAttributeAsBstr(
2774 ui::AX_ATTR_NAME, dom_text);
2777 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2778 unsigned int start_index,
2779 unsigned int end_index,
2780 int* out_x,
2781 int* out_y,
2782 int* out_width,
2783 int* out_height) {
2784 // TODO(dmazzoni): fully support this API by intersecting the
2785 // rect with the container's rect.
2786 return get_unclippedSubstringBounds(
2787 start_index, end_index, out_x, out_y, out_width, out_height);
2790 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2791 unsigned int start_index,
2792 unsigned int end_index,
2793 int* out_x,
2794 int* out_y,
2795 int* out_width,
2796 int* out_height) {
2797 if (!instance_active())
2798 return E_FAIL;
2800 if (!out_x || !out_y || !out_width || !out_height)
2801 return E_INVALIDARG;
2803 const base::string16& text_str = TextForIAccessibleText();
2804 if (start_index > text_str.size() ||
2805 end_index > text_str.size() ||
2806 start_index > end_index) {
2807 return E_INVALIDARG;
2810 gfx::Rect bounds = GetGlobalBoundsForRange(
2811 start_index, end_index - start_index);
2812 *out_x = bounds.x();
2813 *out_y = bounds.y();
2814 *out_width = bounds.width();
2815 *out_height = bounds.height();
2816 return S_OK;
2819 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2820 unsigned int start_index,
2821 unsigned int end_index) {
2822 if (!instance_active())
2823 return E_FAIL;
2825 const base::string16& text_str = TextForIAccessibleText();
2826 if (start_index > text_str.size() ||
2827 end_index > text_str.size() ||
2828 start_index > end_index) {
2829 return E_INVALIDARG;
2832 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2833 start_index, end_index - start_index));
2834 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2836 return S_OK;
2840 // IServiceProvider methods.
2843 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2844 REFIID riid,
2845 void** object) {
2846 if (!instance_active())
2847 return E_FAIL;
2849 // The system uses IAccessible APIs for many purposes, but only
2850 // assistive technology like screen readers uses IAccessible2.
2851 // Enable full accessibility support when IAccessible2 APIs are queried.
2852 if (riid == IID_IAccessible2)
2853 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2855 if (guidService == GUID_IAccessibleContentDocument) {
2856 // Special Mozilla extension: return the accessible for the root document.
2857 // Screen readers use this to distinguish between a document loaded event
2858 // on the root document vs on an iframe.
2859 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2860 IID_IAccessible2, object);
2863 if (guidService == IID_IAccessible ||
2864 guidService == IID_IAccessible2 ||
2865 guidService == IID_IAccessibleAction ||
2866 guidService == IID_IAccessibleApplication ||
2867 guidService == IID_IAccessibleHyperlink ||
2868 guidService == IID_IAccessibleHypertext ||
2869 guidService == IID_IAccessibleImage ||
2870 guidService == IID_IAccessibleTable ||
2871 guidService == IID_IAccessibleTable2 ||
2872 guidService == IID_IAccessibleTableCell ||
2873 guidService == IID_IAccessibleText ||
2874 guidService == IID_IAccessibleValue ||
2875 guidService == IID_ISimpleDOMDocument ||
2876 guidService == IID_ISimpleDOMNode ||
2877 guidService == IID_ISimpleDOMText ||
2878 guidService == GUID_ISimpleDOM) {
2879 return QueryInterface(riid, object);
2882 // We only support the IAccessibleEx interface on Windows 8 and above. This
2883 // is needed for the on-screen Keyboard to show up in metro mode, when the
2884 // user taps an editable portion on the page.
2885 // All methods in the IAccessibleEx interface are unimplemented.
2886 if (riid == IID_IAccessibleEx &&
2887 base::win::GetVersion() >= base::win::VERSION_WIN8) {
2888 return QueryInterface(riid, object);
2891 *object = NULL;
2892 return E_FAIL;
2895 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2896 IUnknown** provider) {
2897 DVLOG(1) << "In Function: "
2898 << __FUNCTION__
2899 << " for pattern id: "
2900 << id;
2901 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2902 if (IsEditableText()) {
2903 DVLOG(1) << "Returning UIA text provider";
2904 base::win::UIATextProvider::CreateTextProvider(
2905 GetValueText(), true, provider);
2906 return S_OK;
2909 return E_NOTIMPL;
2912 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2913 VARIANT* ret) {
2914 DVLOG(1) << "In Function: "
2915 << __FUNCTION__
2916 << " for property id: "
2917 << id;
2918 V_VT(ret) = VT_EMPTY;
2919 if (id == UIA_ControlTypePropertyId) {
2920 if (IsEditableText()) {
2921 V_VT(ret) = VT_I4;
2922 ret->lVal = UIA_EditControlTypeId;
2923 DVLOG(1) << "Returning Edit control type";
2924 } else {
2925 DVLOG(1) << "Returning empty control type";
2928 return S_OK;
2932 // CComObjectRootEx methods.
2935 // static
2936 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2937 void* this_ptr,
2938 const _ATL_INTMAP_ENTRY* entries,
2939 REFIID iid,
2940 void** object) {
2941 BrowserAccessibilityWin* accessibility =
2942 reinterpret_cast<BrowserAccessibilityWin*>(this_ptr);
2943 int32 ia_role = accessibility->ia_role();
2944 if (iid == IID_IAccessibleImage) {
2945 if (ia_role != ROLE_SYSTEM_GRAPHIC) {
2946 *object = NULL;
2947 return E_NOINTERFACE;
2949 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2950 if (ia_role != ROLE_SYSTEM_TABLE) {
2951 *object = NULL;
2952 return E_NOINTERFACE;
2954 } else if (iid == IID_IAccessibleTableCell) {
2955 if (!accessibility->IsCellOrTableHeaderRole()) {
2956 *object = NULL;
2957 return E_NOINTERFACE;
2959 } else if (iid == IID_IAccessibleValue) {
2960 if (ia_role != ROLE_SYSTEM_PROGRESSBAR &&
2961 ia_role != ROLE_SYSTEM_SCROLLBAR &&
2962 ia_role != ROLE_SYSTEM_SLIDER) {
2963 *object = NULL;
2964 return E_NOINTERFACE;
2966 } else if (iid == IID_ISimpleDOMDocument) {
2967 if (ia_role != ROLE_SYSTEM_DOCUMENT) {
2968 *object = NULL;
2969 return E_NOINTERFACE;
2973 return CComObjectRootBase::InternalQueryInterface(
2974 this_ptr, entries, iid, object);
2978 // Private methods.
2981 void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() {
2982 // Swap win_attributes_ to old_win_attributes_, allowing us to see
2983 // exactly what changed and fire appropriate events. Note that
2984 // old_win_attributes_ is cleared at the end of UpdateStep3FireEvents.
2985 old_win_attributes_.swap(win_attributes_);
2986 win_attributes_.reset(new WinAttributes());
2988 InitRoleAndState();
2990 win_attributes_->ia2_attributes.clear();
2992 // Expose autocomplete attribute for combobox and textbox.
2993 StringAttributeToIA2(ui::AX_ATTR_AUTO_COMPLETE, "autocomplete");
2995 // Expose the "display" and "tag" attributes.
2996 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
2997 StringAttributeToIA2(ui::AX_ATTR_DROPEFFECT, "dropeffect");
2998 StringAttributeToIA2(ui::AX_ATTR_TEXT_INPUT_TYPE, "text-input-type");
2999 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
3000 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
3002 // Expose "level" attribute for headings, trees, etc.
3003 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
3005 // Expose the set size and position in set for listbox options.
3006 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3007 GetParent() &&
3008 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
3009 win_attributes_->ia2_attributes.push_back(
3010 L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
3011 win_attributes_->ia2_attributes.push_back(
3012 L"setsize:" + base::IntToString16(GetIndexInParent() + 1));
3015 if (ia_role() == ROLE_SYSTEM_CHECKBUTTON ||
3016 ia_role() == ROLE_SYSTEM_RADIOBUTTON ||
3017 ia2_role() == IA2_ROLE_CHECK_MENU_ITEM ||
3018 ia2_role() == IA2_ROLE_RADIO_MENU_ITEM ||
3019 ia2_role() == IA2_ROLE_TOGGLE_BUTTON) {
3020 win_attributes_->ia2_attributes.push_back(L"checkable:true");
3023 // Expose live region attributes.
3024 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
3025 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
3026 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
3027 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
3029 // Expose aria-grabbed attributes.
3030 BoolAttributeToIA2(ui::AX_ATTR_GRABBED, "grabbed");
3032 // Expose container live region attributes.
3033 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
3034 "container-live");
3035 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
3036 "container-relevant");
3037 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
3038 "container-atomic");
3039 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
3040 "container-busy");
3042 // Expose table cell index.
3043 if (IsCellOrTableHeaderRole()) {
3044 BrowserAccessibility* table = GetParent();
3045 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
3046 table = table->GetParent();
3047 if (table) {
3048 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
3049 ui::AX_ATTR_UNIQUE_CELL_IDS);
3050 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
3051 if (unique_cell_ids[i] == GetId()) {
3052 win_attributes_->ia2_attributes.push_back(
3053 base::string16(L"table-cell-index:") + base::IntToString16(i));
3059 // Expose invalid state for form controls and elements with aria-invalid.
3060 int invalid_state;
3061 if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE, &invalid_state)) {
3062 // TODO(nektar): Handle the possibility of having multiple aria-invalid
3063 // attributes defined, e.g., "invalid:spelling,grammar".
3064 switch (invalid_state) {
3065 case ui::AX_INVALID_STATE_FALSE:
3066 win_attributes_->ia2_attributes.push_back(L"invalid:false");
3067 break;
3068 case ui::AX_INVALID_STATE_TRUE:
3069 win_attributes_->ia2_attributes.push_back(L"invalid:true");
3070 break;
3071 case ui::AX_INVALID_STATE_SPELLING:
3072 win_attributes_->ia2_attributes.push_back(L"invalid:spelling");
3073 break;
3074 case ui::AX_INVALID_STATE_GRAMMAR:
3075 win_attributes_->ia2_attributes.push_back(L"invalid:grammar");
3076 break;
3077 case ui::AX_INVALID_STATE_OTHER:
3079 base::string16 aria_invalid_value;
3080 if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE,
3081 &aria_invalid_value)) {
3082 win_attributes_->ia2_attributes.push_back(
3083 L"invalid:" + aria_invalid_value);
3084 } else {
3085 // Set the attribute to L"true", since we cannot be more specific.
3086 win_attributes_->ia2_attributes.push_back(L"invalid:true");
3089 break;
3090 default:
3091 NOTREACHED();
3095 // Expose row or column header sort direction.
3096 int32 sort_direction;
3097 if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER ||
3098 ia_role() == ROLE_SYSTEM_ROWHEADER) &&
3099 GetIntAttribute(ui::AX_ATTR_SORT_DIRECTION, &sort_direction)) {
3100 switch (sort_direction) {
3101 case ui::AX_SORT_DIRECTION_UNSORTED:
3102 win_attributes_->ia2_attributes.push_back(L"sort:none");
3103 break;
3104 case ui::AX_SORT_DIRECTION_ASCENDING:
3105 win_attributes_->ia2_attributes.push_back(L"sort:ascending");
3106 break;
3107 case ui::AX_SORT_DIRECTION_DESCENDING:
3108 win_attributes_->ia2_attributes.push_back(L"sort:descending");
3109 break;
3110 case ui::AX_SORT_DIRECTION_OTHER:
3111 win_attributes_->ia2_attributes.push_back(L"sort:other");
3112 break;
3113 default:
3114 NOTREACHED();
3118 // The calculation of the accessible name of an element has been
3119 // standardized in the HTML to Platform Accessibility APIs Implementation
3120 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
3121 // appropriate accessible name on Windows, we need to apply some logic
3122 // to the fields we get from WebKit.
3124 // TODO(dmazzoni): move most of this logic into WebKit.
3126 // WebKit gives us:
3128 // name: the default name, e.g. inner text
3129 // title ui element: a reference to a <label> element on the same
3130 // page that labels this node.
3131 // description: accessible labels that override the default name:
3132 // aria-label or aria-labelledby or aria-describedby
3133 // help: the value of the "title" attribute
3135 // On Windows, the logic we apply lets some fields take precedence and
3136 // always returns the primary name in "name" and the secondary name,
3137 // if any, in "description".
3139 int title_elem_id = GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT);
3140 base::string16 name = GetString16Attribute(ui::AX_ATTR_NAME);
3141 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION);
3142 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP);
3143 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE);
3145 // WebKit annoyingly puts the title in the description if there's no other
3146 // description, which just confuses the rest of the logic. Put it back.
3147 // Now "help" is always the value of the "title" attribute, if present.
3148 base::string16 title_attr;
3149 if (GetHtmlAttribute("title", &title_attr) &&
3150 description == title_attr &&
3151 help.empty()) {
3152 help = description;
3153 description.clear();
3156 // Now implement the main logic: the descripion should become the name if
3157 // it's nonempty, and the help should become the description if
3158 // there's no description - or the name if there's no name or description.
3159 if (!description.empty()) {
3160 name = description;
3161 description.clear();
3163 if (!help.empty() && description.empty()) {
3164 description = help;
3165 help.clear();
3167 if (!description.empty() && name.empty() && !title_elem_id) {
3168 name = description;
3169 description.clear();
3172 // If it's a text field, also consider the placeholder.
3173 base::string16 placeholder;
3174 if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
3175 HasState(ui::AX_STATE_FOCUSABLE) &&
3176 GetHtmlAttribute("placeholder", &placeholder)) {
3177 if (name.empty() && !title_elem_id) {
3178 name = placeholder;
3179 } else if (description.empty()) {
3180 description = placeholder;
3184 // On Windows, the value of a document should be its url.
3185 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
3186 GetRole() == ui::AX_ROLE_WEB_AREA) {
3187 value = GetString16Attribute(ui::AX_ATTR_DOC_URL);
3190 // For certain roles (listbox option, static text, and list marker)
3191 // WebKit stores the main accessible text in the "value" - swap it so
3192 // that it's the "name".
3193 if (name.empty() &&
3194 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
3195 GetRole() == ui::AX_ROLE_STATIC_TEXT ||
3196 GetRole() == ui::AX_ROLE_LIST_MARKER)) {
3197 base::string16 tmp = value;
3198 value = name;
3199 name = tmp;
3202 // If this doesn't have a value and is linked then set its value to the url
3203 // attribute. This allows screen readers to read an empty link's destination.
3204 if (value.empty() && (ia_state() & STATE_SYSTEM_LINKED))
3205 value = GetString16Attribute(ui::AX_ATTR_URL);
3207 win_attributes_->name = name;
3208 win_attributes_->description = description;
3209 win_attributes_->help = help;
3210 win_attributes_->value = value;
3212 // Clear any old relationships between this node and other nodes.
3213 for (size_t i = 0; i < relations_.size(); ++i)
3214 relations_[i]->Release();
3215 relations_.clear();
3217 // Handle title UI element.
3218 if (title_elem_id) {
3219 // Add a labelled by relationship.
3220 CComObject<BrowserAccessibilityRelation>* relation;
3221 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3222 &relation);
3223 DCHECK(SUCCEEDED(hr));
3224 relation->AddRef();
3225 relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3226 relation->AddTarget(title_elem_id);
3227 relations_.push_back(relation);
3230 // Expose slider value.
3231 if (ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
3232 ia_role() == ROLE_SYSTEM_SCROLLBAR ||
3233 ia_role() == ROLE_SYSTEM_SLIDER) {
3234 win_attributes_->ia2_attributes.push_back(L"valuetext:" + GetValueText());
3237 // If this is a web area for a presentational iframe, give it a role of
3238 // something other than DOCUMENT so that the fact that it's a separate doc
3239 // is not exposed to AT.
3240 if (IsWebAreaForPresentationalIframe()) {
3241 win_attributes_->ia_role = ROLE_SYSTEM_GROUPING;
3242 win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING;
3246 void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() {
3247 // Construct the hypertext for this node, which contains the concatenation
3248 // of all of the static text of this node's children and an embedded object
3249 // character for all non-static-text children. Build up a map from the
3250 // character index of each embedded object character to the id of the
3251 // child object it points to.
3252 for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
3253 BrowserAccessibilityWin* child =
3254 PlatformGetChild(i)->ToBrowserAccessibilityWin();
3255 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
3256 win_attributes_->hypertext += child->name();
3257 } else {
3258 int32 char_offset = hypertext().size();
3259 int32 child_id = child->GetId();
3260 int32 index = hyperlinks().size();
3261 win_attributes_->hyperlink_offset_to_index[char_offset] = index;
3262 win_attributes_->hyperlinks.push_back(child_id);
3263 win_attributes_->hypertext += kEmbeddedCharacter;
3268 void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation) {
3269 BrowserAccessibilityManagerWin* manager =
3270 this->manager()->ToBrowserAccessibilityManagerWin();
3272 // Fire an event when an alert first appears.
3273 if (ia_role() == ROLE_SYSTEM_ALERT &&
3274 old_win_attributes_->ia_role != ROLE_SYSTEM_ALERT) {
3275 manager->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
3278 // Fire an event when a new subtree is created.
3279 if (is_subtree_creation)
3280 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW, this);
3282 // The rest of the events only fire on changes, not on new objects.
3283 if (old_win_attributes_->ia_role != 0 ||
3284 !old_win_attributes_->role_name.empty()) {
3285 // Fire an event if the name, description, help, or value changes.
3286 if (name() != old_win_attributes_->name)
3287 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, this);
3288 if (description() != old_win_attributes_->description)
3289 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE, this);
3290 if (help() != old_win_attributes_->help)
3291 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_HELPCHANGE, this);
3292 if (value() != old_win_attributes_->value)
3293 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, this);
3294 if (ia_state() != old_win_attributes_->ia_state)
3295 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE, this);
3297 // Normally focus events are handled elsewhere, however
3298 // focus for managed descendants is platform-specific.
3299 // Fire a focus event if the focused descendant in a multi-select
3300 // list box changes.
3301 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3302 (ia_state() & STATE_SYSTEM_FOCUSABLE) &&
3303 (ia_state() & STATE_SYSTEM_SELECTABLE) &&
3304 (ia_state() & STATE_SYSTEM_FOCUSED) &&
3305 !(old_win_attributes_->ia_state & STATE_SYSTEM_FOCUSED)) {
3306 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, this);
3309 // Handle selection being added or removed.
3310 bool is_selected_now = (ia_state() & STATE_SYSTEM_SELECTED) != 0;
3311 bool was_selected_before =
3312 (old_win_attributes_->ia_state & STATE_SYSTEM_SELECTED) != 0;
3313 if (is_selected_now && !was_selected_before) {
3314 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD, this);
3315 } else if (!is_selected_now && was_selected_before) {
3316 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, this);
3319 // Fire an event if this container object has scrolled.
3320 int sx = 0;
3321 int sy = 0;
3322 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
3323 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
3324 if (sx != previous_scroll_x_ || sy != previous_scroll_y_)
3325 manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND, this);
3326 previous_scroll_x_ = sx;
3327 previous_scroll_y_ = sy;
3330 // Changing a static text node can affect the IAccessibleText hypertext
3331 // of the parent node, so force an update on the parent.
3332 BrowserAccessibilityWin* parent = GetParent()->ToBrowserAccessibilityWin();
3333 if (parent &&
3334 GetRole() == ui::AX_ROLE_STATIC_TEXT &&
3335 name() != old_win_attributes_->name) {
3336 parent->UpdateStep1ComputeWinAttributes();
3337 parent->UpdateStep2ComputeHypertext();
3338 parent->UpdateStep3FireEvents(false);
3341 // Fire hypertext-related events.
3342 int start, old_len, new_len;
3343 ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len);
3344 if (old_len > 0) {
3345 // In-process screen readers may call IAccessibleText::get_oldText
3346 // in reaction to this event to retrieve the text that was removed.
3347 manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_REMOVED, this);
3349 if (new_len > 0) {
3350 // In-process screen readers may call IAccessibleText::get_newText
3351 // in reaction to this event to retrieve the text that was inserted.
3352 manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_INSERTED, this);
3356 old_win_attributes_.reset(nullptr);
3359 void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() {
3360 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3361 EVENT_OBJECT_HIDE, this);
3364 void BrowserAccessibilityWin::NativeAddReference() {
3365 AddRef();
3368 void BrowserAccessibilityWin::NativeReleaseReference() {
3369 Release();
3372 bool BrowserAccessibilityWin::IsNative() const {
3373 return true;
3376 void BrowserAccessibilityWin::OnLocationChanged() {
3377 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3378 EVENT_OBJECT_LOCATIONCHANGE, this);
3381 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3382 AddRef();
3383 return this;
3386 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3387 const VARIANT& var_id) {
3388 if (var_id.vt != VT_I4)
3389 return NULL;
3391 LONG child_id = var_id.lVal;
3392 if (child_id == CHILDID_SELF)
3393 return this;
3395 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3396 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3398 return manager()->ToBrowserAccessibilityManagerWin()->
3399 GetFromUniqueIdWin(child_id);
3402 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3403 ui::AXStringAttribute attribute,
3404 BSTR* value_bstr) {
3405 base::string16 str;
3407 if (!GetString16Attribute(attribute, &str))
3408 return S_FALSE;
3410 if (str.empty())
3411 return S_FALSE;
3413 *value_bstr = SysAllocString(str.c_str());
3414 DCHECK(*value_bstr);
3416 return S_OK;
3419 void BrowserAccessibilityWin::StringAttributeToIA2(
3420 ui::AXStringAttribute attribute,
3421 const char* ia2_attr) {
3422 base::string16 value;
3423 if (GetString16Attribute(attribute, &value)) {
3424 win_attributes_->ia2_attributes.push_back(
3425 base::ASCIIToUTF16(ia2_attr) + L":" + value);
3429 void BrowserAccessibilityWin::BoolAttributeToIA2(
3430 ui::AXBoolAttribute attribute,
3431 const char* ia2_attr) {
3432 bool value;
3433 if (GetBoolAttribute(attribute, &value)) {
3434 win_attributes_->ia2_attributes.push_back(
3435 (base::ASCIIToUTF16(ia2_attr) + L":") +
3436 (value ? L"true" : L"false"));
3440 void BrowserAccessibilityWin::IntAttributeToIA2(
3441 ui::AXIntAttribute attribute,
3442 const char* ia2_attr) {
3443 int value;
3444 if (GetIntAttribute(attribute, &value)) {
3445 win_attributes_->ia2_attributes.push_back(
3446 base::ASCIIToUTF16(ia2_attr) + L":" +
3447 base::IntToString16(value));
3451 base::string16 BrowserAccessibilityWin::GetNameRecursive() const {
3452 if (!name().empty()) {
3453 return name();
3456 base::string16 result;
3457 for (uint32 i = 0; i < PlatformChildCount(); ++i) {
3458 result += PlatformGetChild(i)->ToBrowserAccessibilityWin()->
3459 GetNameRecursive();
3461 return result;
3464 base::string16 BrowserAccessibilityWin::GetValueText() {
3465 float fval;
3466 base::string16 value = this->value();
3468 if (value.empty() &&
3469 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
3470 value = base::UTF8ToUTF16(base::DoubleToString(fval));
3472 return value;
3475 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3476 if (IsEditableText())
3477 return value();
3478 return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ? name() : hypertext();
3481 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index,
3482 size_t new_char_index) {
3483 CHECK(old_win_attributes_);
3485 // For anything other than the "embedded character", we just compare the
3486 // characters directly.
3487 base::char16 old_ch = old_win_attributes_->hypertext[old_char_index];
3488 base::char16 new_ch = win_attributes_->hypertext[new_char_index];
3489 if (old_ch != new_ch)
3490 return false;
3491 if (old_ch == new_ch && new_ch != kEmbeddedCharacter)
3492 return true;
3494 // If it's an embedded character, they're only identical if the child id
3495 // the hyperlink points to is the same.
3496 std::map<int32, int32>& old_offset_to_index =
3497 old_win_attributes_->hyperlink_offset_to_index;
3498 std::vector<int32>& old_hyperlinks = old_win_attributes_->hyperlinks;
3499 int32 old_hyperlinks_count = static_cast<int32>(old_hyperlinks.size());
3500 std::map<int32, int32>::iterator iter;
3501 iter = old_offset_to_index.find(old_char_index);
3502 int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1;
3503 int old_child_id = (old_index >= 0 && old_index < old_hyperlinks_count) ?
3504 old_hyperlinks[old_index] : -1;
3506 std::map<int32, int32>& new_offset_to_index =
3507 win_attributes_->hyperlink_offset_to_index;
3508 std::vector<int32>& new_hyperlinks = win_attributes_->hyperlinks;
3509 int32 new_hyperlinks_count = static_cast<int32>(new_hyperlinks.size());
3510 iter = new_offset_to_index.find(new_char_index);
3511 int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1;
3512 int new_child_id = (new_index >= 0 && new_index < new_hyperlinks_count) ?
3513 new_hyperlinks[new_index] : -1;
3515 return old_child_id == new_child_id;
3518 void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted(
3519 int* start, int* old_len, int* new_len) {
3520 CHECK(old_win_attributes_);
3522 *start = 0;
3523 *old_len = 0;
3524 *new_len = 0;
3526 const base::string16& old_text = old_win_attributes_->hypertext;
3527 const base::string16& new_text = hypertext();
3529 size_t common_prefix = 0;
3530 while (common_prefix < old_text.size() &&
3531 common_prefix < new_text.size() &&
3532 IsSameHypertextCharacter(common_prefix, common_prefix)) {
3533 ++common_prefix;
3536 size_t common_suffix = 0;
3537 while (common_prefix + common_suffix < old_text.size() &&
3538 common_prefix + common_suffix < new_text.size() &&
3539 IsSameHypertextCharacter(
3540 old_text.size() - common_suffix - 1,
3541 new_text.size() - common_suffix - 1)) {
3542 ++common_suffix;
3545 *start = common_prefix;
3546 *old_len = old_text.size() - common_prefix - common_suffix;
3547 *new_len = new_text.size() - common_prefix - common_suffix;
3550 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3551 const base::string16& text,
3552 LONG* offset) {
3553 if (*offset == IA2_TEXT_OFFSET_LENGTH)
3554 *offset = static_cast<LONG>(text.size());
3555 else if (*offset == IA2_TEXT_OFFSET_CARET)
3556 get_caretOffset(offset);
3559 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3560 IA2TextBoundaryType ia2_boundary) {
3561 switch(ia2_boundary) {
3562 case IA2_TEXT_BOUNDARY_CHAR:
3563 return ui::CHAR_BOUNDARY;
3564 case IA2_TEXT_BOUNDARY_WORD:
3565 return ui::WORD_BOUNDARY;
3566 case IA2_TEXT_BOUNDARY_LINE:
3567 return ui::LINE_BOUNDARY;
3568 case IA2_TEXT_BOUNDARY_SENTENCE:
3569 return ui::SENTENCE_BOUNDARY;
3570 case IA2_TEXT_BOUNDARY_PARAGRAPH:
3571 return ui::PARAGRAPH_BOUNDARY;
3572 case IA2_TEXT_BOUNDARY_ALL:
3573 return ui::ALL_BOUNDARY;
3574 default:
3575 NOTREACHED();
3577 return ui::CHAR_BOUNDARY;
3580 LONG BrowserAccessibilityWin::FindBoundary(
3581 const base::string16& text,
3582 IA2TextBoundaryType ia2_boundary,
3583 LONG start_offset,
3584 ui::TextBoundaryDirection direction) {
3585 HandleSpecialTextOffset(text, &start_offset);
3586 if (ia2_boundary == IA2_TEXT_BOUNDARY_WORD &&
3587 GetRole() == ui::AX_ROLE_TEXT_FIELD) {
3588 return GetWordStartBoundary(static_cast<int>(start_offset), direction);
3591 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3592 const std::vector<int32>& line_breaks = GetIntListAttribute(
3593 ui::AX_ATTR_LINE_BREAKS);
3594 return ui::FindAccessibleTextBoundary(
3595 text, line_breaks, boundary, start_offset, direction);
3598 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
3599 return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
3602 void BrowserAccessibilityWin::InitRoleAndState() {
3603 int32 ia_role = 0;
3604 int32 ia_state = 0;
3605 base::string16 role_name;
3606 int32 ia2_role = 0;
3607 int32 ia2_state = IA2_STATE_OPAQUE;
3609 if (HasState(ui::AX_STATE_BUSY))
3610 ia_state |= STATE_SYSTEM_BUSY;
3611 if (HasState(ui::AX_STATE_CHECKED))
3612 ia_state |= STATE_SYSTEM_CHECKED;
3613 if (HasState(ui::AX_STATE_COLLAPSED))
3614 ia_state |= STATE_SYSTEM_COLLAPSED;
3615 if (HasState(ui::AX_STATE_EXPANDED))
3616 ia_state |= STATE_SYSTEM_EXPANDED;
3617 if (HasState(ui::AX_STATE_FOCUSABLE))
3618 ia_state |= STATE_SYSTEM_FOCUSABLE;
3619 if (HasState(ui::AX_STATE_HASPOPUP))
3620 ia_state |= STATE_SYSTEM_HASPOPUP;
3621 if (HasState(ui::AX_STATE_INDETERMINATE))
3622 ia_state |= STATE_SYSTEM_INDETERMINATE;
3623 if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) &&
3624 GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE)
3625 ia2_state |= IA2_STATE_INVALID_ENTRY;
3626 if (HasState(ui::AX_STATE_INVISIBLE))
3627 ia_state |= STATE_SYSTEM_INVISIBLE;
3628 if (HasState(ui::AX_STATE_LINKED))
3629 ia_state |= STATE_SYSTEM_LINKED;
3630 if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
3631 ia_state |= STATE_SYSTEM_EXTSELECTABLE;
3632 ia_state |= STATE_SYSTEM_MULTISELECTABLE;
3634 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3635 if (HasState(ui::AX_STATE_OFFSCREEN))
3636 ia_state |= STATE_SYSTEM_OFFSCREEN;
3637 if (HasState(ui::AX_STATE_PRESSED))
3638 ia_state |= STATE_SYSTEM_PRESSED;
3639 if (HasState(ui::AX_STATE_PROTECTED))
3640 ia_state |= STATE_SYSTEM_PROTECTED;
3641 if (HasState(ui::AX_STATE_REQUIRED))
3642 ia2_state |= IA2_STATE_REQUIRED;
3643 if (HasState(ui::AX_STATE_SELECTABLE))
3644 ia_state |= STATE_SYSTEM_SELECTABLE;
3645 if (HasState(ui::AX_STATE_SELECTED))
3646 ia_state |= STATE_SYSTEM_SELECTED;
3647 if (HasState(ui::AX_STATE_VISITED))
3648 ia_state |= STATE_SYSTEM_TRAVERSED;
3649 if (!HasState(ui::AX_STATE_ENABLED))
3650 ia_state |= STATE_SYSTEM_UNAVAILABLE;
3651 if (HasState(ui::AX_STATE_VERTICAL))
3652 ia2_state |= IA2_STATE_VERTICAL;
3653 if (HasState(ui::AX_STATE_HORIZONTAL))
3654 ia2_state |= IA2_STATE_HORIZONTAL;
3655 if (HasState(ui::AX_STATE_VISITED))
3656 ia_state |= STATE_SYSTEM_TRAVERSED;
3658 // Expose whether or not the mouse is over an element, but suppress
3659 // this for tests because it can make the test results flaky depending
3660 // on the position of the mouse.
3661 BrowserAccessibilityStateImpl* accessibility_state =
3662 BrowserAccessibilityStateImpl::GetInstance();
3663 if (!accessibility_state->disable_hot_tracking_for_testing()) {
3664 if (HasState(ui::AX_STATE_HOVERED))
3665 ia_state |= STATE_SYSTEM_HOTTRACKED;
3668 // WebKit marks everything as readonly unless it's editable text, so if it's
3669 // not readonly, mark it as editable now. The final computation of the
3670 // READONLY state for MSAA is below, after the switch.
3671 if (!HasState(ui::AX_STATE_READ_ONLY))
3672 ia2_state |= IA2_STATE_EDITABLE;
3674 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
3675 ia_state |= STATE_SYSTEM_MIXED;
3677 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
3678 ia2_state |= IA2_STATE_EDITABLE;
3680 if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
3681 ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
3683 base::string16 html_tag = GetString16Attribute(
3684 ui::AX_ATTR_HTML_TAG);
3685 switch (GetRole()) {
3686 case ui::AX_ROLE_ALERT:
3687 ia_role = ROLE_SYSTEM_ALERT;
3688 break;
3689 case ui::AX_ROLE_ALERT_DIALOG:
3690 ia_role = ROLE_SYSTEM_DIALOG;
3691 break;
3692 case ui::AX_ROLE_APPLICATION:
3693 ia_role = ROLE_SYSTEM_APPLICATION;
3694 break;
3695 case ui::AX_ROLE_ARTICLE:
3696 ia_role = ROLE_SYSTEM_DOCUMENT;
3697 ia_state |= STATE_SYSTEM_READONLY;
3698 break;
3699 case ui::AX_ROLE_BANNER:
3700 ia_role = ROLE_SYSTEM_GROUPING;
3701 ia2_role = IA2_ROLE_HEADER;
3702 break;
3703 case ui::AX_ROLE_BLOCKQUOTE:
3704 role_name = html_tag;
3705 ia2_role = IA2_ROLE_SECTION;
3706 break;
3707 case ui::AX_ROLE_BUSY_INDICATOR:
3708 ia_role = ROLE_SYSTEM_ANIMATION;
3709 ia_state |= STATE_SYSTEM_READONLY;
3710 break;
3711 case ui::AX_ROLE_BUTTON:
3712 ia_role = ROLE_SYSTEM_PUSHBUTTON;
3713 break;
3714 case ui::AX_ROLE_CANVAS:
3715 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
3716 role_name = L"canvas";
3717 ia2_role = IA2_ROLE_CANVAS;
3718 } else {
3719 ia_role = ROLE_SYSTEM_GRAPHIC;
3721 break;
3722 case ui::AX_ROLE_CAPTION:
3723 ia_role = ROLE_SYSTEM_TEXT;
3724 ia2_role = IA2_ROLE_CAPTION;
3725 break;
3726 case ui::AX_ROLE_CELL:
3727 ia_role = ROLE_SYSTEM_CELL;
3728 break;
3729 case ui::AX_ROLE_CHECK_BOX:
3730 ia_role = ROLE_SYSTEM_CHECKBUTTON;
3731 ia2_state |= IA2_STATE_CHECKABLE;
3732 break;
3733 case ui::AX_ROLE_COLOR_WELL:
3734 ia_role = ROLE_SYSTEM_TEXT;
3735 ia2_role = IA2_ROLE_COLOR_CHOOSER;
3736 break;
3737 case ui::AX_ROLE_COLUMN:
3738 ia_role = ROLE_SYSTEM_COLUMN;
3739 break;
3740 case ui::AX_ROLE_COLUMN_HEADER:
3741 ia_role = ROLE_SYSTEM_COLUMNHEADER;
3742 break;
3743 case ui::AX_ROLE_COMBO_BOX:
3744 ia_role = ROLE_SYSTEM_COMBOBOX;
3745 break;
3746 case ui::AX_ROLE_COMPLEMENTARY:
3747 ia_role = ROLE_SYSTEM_GROUPING;
3748 ia2_role = IA2_ROLE_NOTE;
3749 break;
3750 case ui::AX_ROLE_CONTENT_INFO:
3751 ia_role = ROLE_SYSTEM_TEXT;
3752 ia2_role = IA2_ROLE_PARAGRAPH;
3753 break;
3754 case ui::AX_ROLE_DATE:
3755 case ui::AX_ROLE_DATE_TIME:
3756 ia_role = ROLE_SYSTEM_DROPLIST;
3757 ia2_role = IA2_ROLE_DATE_EDITOR;
3758 break;
3759 case ui::AX_ROLE_DIV:
3760 role_name = L"div";
3761 ia_role = ROLE_SYSTEM_GROUPING;
3762 ia2_role = IA2_ROLE_SECTION;
3763 break;
3764 case ui::AX_ROLE_DEFINITION:
3765 role_name = html_tag;
3766 ia2_role = IA2_ROLE_PARAGRAPH;
3767 ia_state |= STATE_SYSTEM_READONLY;
3768 break;
3769 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
3770 role_name = html_tag;
3771 ia_role = ROLE_SYSTEM_TEXT;
3772 ia2_role = IA2_ROLE_PARAGRAPH;
3773 break;
3774 case ui::AX_ROLE_DESCRIPTION_LIST:
3775 role_name = html_tag;
3776 ia_role = ROLE_SYSTEM_LIST;
3777 ia_state |= STATE_SYSTEM_READONLY;
3778 break;
3779 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
3780 ia_role = ROLE_SYSTEM_LISTITEM;
3781 ia_state |= STATE_SYSTEM_READONLY;
3782 break;
3783 case ui::AX_ROLE_DETAILS:
3784 role_name = html_tag;
3785 ia_role = ROLE_SYSTEM_GROUPING;
3786 break;
3787 case ui::AX_ROLE_DIALOG:
3788 ia_role = ROLE_SYSTEM_DIALOG;
3789 break;
3790 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
3791 ia_role = ROLE_SYSTEM_PUSHBUTTON;
3792 break;
3793 case ui::AX_ROLE_DOCUMENT:
3794 case ui::AX_ROLE_ROOT_WEB_AREA:
3795 case ui::AX_ROLE_WEB_AREA:
3796 ia_role = ROLE_SYSTEM_DOCUMENT;
3797 ia_state |= STATE_SYSTEM_READONLY;
3798 ia_state |= STATE_SYSTEM_FOCUSABLE;
3799 break;
3800 case ui::AX_ROLE_EMBEDDED_OBJECT:
3801 ia_role = ROLE_SYSTEM_CLIENT;
3802 ia2_role = IA2_ROLE_EMBEDDED_OBJECT;
3803 break;
3804 case ui::AX_ROLE_FIGCAPTION:
3805 role_name = html_tag;
3806 ia2_role = IA2_ROLE_CAPTION;
3807 break;
3808 case ui::AX_ROLE_FIGURE:
3809 ia_role = ROLE_SYSTEM_GROUPING;
3810 break;
3811 case ui::AX_ROLE_FORM:
3812 role_name = L"form";
3813 ia2_role = IA2_ROLE_FORM;
3814 break;
3815 case ui::AX_ROLE_FOOTER:
3816 ia_role = ROLE_SYSTEM_GROUPING;
3817 ia2_role = IA2_ROLE_FOOTER;
3818 break;
3819 case ui::AX_ROLE_GRID:
3820 ia_role = ROLE_SYSTEM_TABLE;
3821 ia_state |= STATE_SYSTEM_READONLY;
3822 break;
3823 case ui::AX_ROLE_GROUP: {
3824 base::string16 aria_role = GetString16Attribute(
3825 ui::AX_ATTR_ROLE);
3826 if (aria_role == L"group" || html_tag == L"fieldset") {
3827 ia_role = ROLE_SYSTEM_GROUPING;
3828 } else if (html_tag == L"li") {
3829 ia_role = ROLE_SYSTEM_LISTITEM;
3830 ia_state |= STATE_SYSTEM_READONLY;
3831 } else {
3832 if (html_tag.empty())
3833 role_name = L"div";
3834 else
3835 role_name = html_tag;
3836 ia2_role = IA2_ROLE_SECTION;
3838 break;
3840 case ui::AX_ROLE_HEADING:
3841 role_name = html_tag;
3842 ia2_role = IA2_ROLE_HEADING;
3843 break;
3844 case ui::AX_ROLE_IFRAME:
3845 ia_role = ROLE_SYSTEM_DOCUMENT;
3846 ia2_role = IA2_ROLE_INTERNAL_FRAME;
3847 ia_state = STATE_SYSTEM_READONLY;
3848 break;
3849 case ui::AX_ROLE_IFRAME_PRESENTATIONAL:
3850 ia_role = ROLE_SYSTEM_GROUPING;
3851 break;
3852 case ui::AX_ROLE_IMAGE:
3853 ia_role = ROLE_SYSTEM_GRAPHIC;
3854 ia_state |= STATE_SYSTEM_READONLY;
3855 break;
3856 case ui::AX_ROLE_IMAGE_MAP:
3857 role_name = html_tag;
3858 ia2_role = IA2_ROLE_IMAGE_MAP;
3859 ia_state |= STATE_SYSTEM_READONLY;
3860 break;
3861 case ui::AX_ROLE_IMAGE_MAP_LINK:
3862 ia_role = ROLE_SYSTEM_LINK;
3863 ia_state |= STATE_SYSTEM_LINKED;
3864 ia_state |= STATE_SYSTEM_READONLY;
3865 break;
3866 case ui::AX_ROLE_LABEL_TEXT:
3867 case ui::AX_ROLE_LEGEND:
3868 ia_role = ROLE_SYSTEM_TEXT;
3869 ia2_role = IA2_ROLE_LABEL;
3870 break;
3871 case ui::AX_ROLE_LINK:
3872 ia_role = ROLE_SYSTEM_LINK;
3873 ia_state |= STATE_SYSTEM_LINKED;
3874 break;
3875 case ui::AX_ROLE_LIST:
3876 ia_role = ROLE_SYSTEM_LIST;
3877 ia_state |= STATE_SYSTEM_READONLY;
3878 break;
3879 case ui::AX_ROLE_LIST_BOX:
3880 ia_role = ROLE_SYSTEM_LIST;
3881 break;
3882 case ui::AX_ROLE_LIST_BOX_OPTION:
3883 ia_role = ROLE_SYSTEM_LISTITEM;
3884 if (ia_state & STATE_SYSTEM_SELECTABLE) {
3885 ia_state |= STATE_SYSTEM_FOCUSABLE;
3886 if (HasState(ui::AX_STATE_FOCUSED))
3887 ia_state |= STATE_SYSTEM_FOCUSED;
3889 break;
3890 case ui::AX_ROLE_LIST_ITEM:
3891 ia_role = ROLE_SYSTEM_LISTITEM;
3892 ia_state |= STATE_SYSTEM_READONLY;
3893 break;
3894 case ui::AX_ROLE_MAIN:
3895 ia_role = ROLE_SYSTEM_GROUPING;
3896 ia2_role = IA2_ROLE_PARAGRAPH;
3897 break;
3898 case ui::AX_ROLE_MARQUEE:
3899 ia_role = ROLE_SYSTEM_ANIMATION;
3900 break;
3901 case ui::AX_ROLE_MATH:
3902 ia_role = ROLE_SYSTEM_EQUATION;
3903 break;
3904 case ui::AX_ROLE_MENU:
3905 case ui::AX_ROLE_MENU_BUTTON:
3906 ia_role = ROLE_SYSTEM_MENUPOPUP;
3907 break;
3908 case ui::AX_ROLE_MENU_BAR:
3909 ia_role = ROLE_SYSTEM_MENUBAR;
3910 break;
3911 case ui::AX_ROLE_MENU_ITEM:
3912 ia_role = ROLE_SYSTEM_MENUITEM;
3913 break;
3914 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
3915 ia_role = ROLE_SYSTEM_MENUITEM;
3916 ia2_role = IA2_ROLE_CHECK_MENU_ITEM;
3917 ia2_state |= IA2_STATE_CHECKABLE;
3918 break;
3919 case ui::AX_ROLE_MENU_ITEM_RADIO:
3920 ia_role = ROLE_SYSTEM_MENUITEM;
3921 ia2_role = IA2_ROLE_RADIO_MENU_ITEM;
3922 break;
3923 case ui::AX_ROLE_MENU_LIST_POPUP:
3924 ia_role = ROLE_SYSTEM_CLIENT;
3925 break;
3926 case ui::AX_ROLE_MENU_LIST_OPTION:
3927 ia_role = ROLE_SYSTEM_LISTITEM;
3928 if (ia_state & STATE_SYSTEM_SELECTABLE) {
3929 ia_state |= STATE_SYSTEM_FOCUSABLE;
3930 if (HasState(ui::AX_STATE_FOCUSED))
3931 ia_state |= STATE_SYSTEM_FOCUSED;
3933 break;
3934 case ui::AX_ROLE_METER:
3935 role_name = html_tag;
3936 ia_role = ROLE_SYSTEM_PROGRESSBAR;
3937 break;
3938 case ui::AX_ROLE_NAVIGATION:
3939 ia_role = ROLE_SYSTEM_GROUPING;
3940 ia2_role = IA2_ROLE_SECTION;
3941 break;
3942 case ui::AX_ROLE_NOTE:
3943 ia_role = ROLE_SYSTEM_GROUPING;
3944 ia2_role = IA2_ROLE_NOTE;
3945 break;
3946 case ui::AX_ROLE_OUTLINE:
3947 ia_role = ROLE_SYSTEM_OUTLINE;
3948 break;
3949 case ui::AX_ROLE_PARAGRAPH:
3950 role_name = L"P";
3951 ia2_role = IA2_ROLE_PARAGRAPH;
3952 break;
3953 case ui::AX_ROLE_POP_UP_BUTTON:
3954 if (html_tag == L"select") {
3955 ia_role = ROLE_SYSTEM_COMBOBOX;
3956 } else {
3957 ia_role = ROLE_SYSTEM_BUTTONMENU;
3959 break;
3960 case ui::AX_ROLE_PRE:
3961 role_name = html_tag;
3962 ia_role = ROLE_SYSTEM_TEXT;
3963 ia2_role = IA2_ROLE_PARAGRAPH;
3964 break;
3965 case ui::AX_ROLE_PROGRESS_INDICATOR:
3966 ia_role = ROLE_SYSTEM_PROGRESSBAR;
3967 ia_state |= STATE_SYSTEM_READONLY;
3968 break;
3969 case ui::AX_ROLE_RADIO_BUTTON:
3970 ia_role = ROLE_SYSTEM_RADIOBUTTON;
3971 ia2_state = IA2_STATE_CHECKABLE;
3972 break;
3973 case ui::AX_ROLE_RADIO_GROUP:
3974 ia_role = ROLE_SYSTEM_GROUPING;
3975 break;
3976 case ui::AX_ROLE_REGION:
3977 if (html_tag == L"section") {
3978 ia_role = ROLE_SYSTEM_GROUPING;
3979 ia2_role = IA2_ROLE_SECTION;
3980 } else {
3981 ia_role = ROLE_SYSTEM_PANE;
3983 break;
3984 case ui::AX_ROLE_ROW:
3985 ia_role = ROLE_SYSTEM_ROW;
3986 break;
3987 case ui::AX_ROLE_ROW_HEADER:
3988 ia_role = ROLE_SYSTEM_ROWHEADER;
3989 break;
3990 case ui::AX_ROLE_RUBY:
3991 ia_role = ROLE_SYSTEM_TEXT;
3992 ia2_role = IA2_ROLE_TEXT_FRAME;
3993 break;
3994 case ui::AX_ROLE_RULER:
3995 ia_role = ROLE_SYSTEM_CLIENT;
3996 ia2_role = IA2_ROLE_RULER;
3997 ia_state |= STATE_SYSTEM_READONLY;
3998 break;
3999 case ui::AX_ROLE_SCROLL_AREA:
4000 ia_role = ROLE_SYSTEM_CLIENT;
4001 ia2_role = IA2_ROLE_SCROLL_PANE;
4002 ia_state |= STATE_SYSTEM_READONLY;
4003 ia2_state &= ~(IA2_STATE_EDITABLE);
4004 break;
4005 case ui::AX_ROLE_SCROLL_BAR:
4006 ia_role = ROLE_SYSTEM_SCROLLBAR;
4007 break;
4008 case ui::AX_ROLE_SEARCH:
4009 ia_role = ROLE_SYSTEM_GROUPING;
4010 ia2_role = IA2_ROLE_SECTION;
4011 break;
4012 case ui::AX_ROLE_SLIDER:
4013 ia_role = ROLE_SYSTEM_SLIDER;
4014 break;
4015 case ui::AX_ROLE_SPIN_BUTTON:
4016 ia_role = ROLE_SYSTEM_SPINBUTTON;
4017 break;
4018 case ui::AX_ROLE_SPIN_BUTTON_PART:
4019 ia_role = ROLE_SYSTEM_PUSHBUTTON;
4020 break;
4021 case ui::AX_ROLE_ANNOTATION:
4022 case ui::AX_ROLE_LIST_MARKER:
4023 case ui::AX_ROLE_STATIC_TEXT:
4024 ia_role = ROLE_SYSTEM_STATICTEXT;
4025 break;
4026 case ui::AX_ROLE_STATUS:
4027 ia_role = ROLE_SYSTEM_STATUSBAR;
4028 break;
4029 case ui::AX_ROLE_SPLITTER:
4030 ia_role = ROLE_SYSTEM_SEPARATOR;
4031 break;
4032 case ui::AX_ROLE_SVG_ROOT:
4033 ia_role = ROLE_SYSTEM_GRAPHIC;
4034 break;
4035 case ui::AX_ROLE_SWITCH:
4036 role_name = L"switch";
4037 ia2_role = IA2_ROLE_TOGGLE_BUTTON;
4038 break;
4039 case ui::AX_ROLE_TAB:
4040 ia_role = ROLE_SYSTEM_PAGETAB;
4041 break;
4042 case ui::AX_ROLE_TABLE: {
4043 base::string16 aria_role = GetString16Attribute(
4044 ui::AX_ATTR_ROLE);
4045 if (aria_role == L"treegrid") {
4046 ia_role = ROLE_SYSTEM_OUTLINE;
4047 } else {
4048 ia_role = ROLE_SYSTEM_TABLE;
4050 break;
4052 case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
4053 ia_role = ROLE_SYSTEM_GROUPING;
4054 ia2_role = IA2_ROLE_SECTION;
4055 ia_state |= STATE_SYSTEM_READONLY;
4056 break;
4057 case ui::AX_ROLE_TAB_LIST:
4058 ia_role = ROLE_SYSTEM_PAGETABLIST;
4059 break;
4060 case ui::AX_ROLE_TAB_PANEL:
4061 ia_role = ROLE_SYSTEM_PROPERTYPAGE;
4062 break;
4063 case ui::AX_ROLE_TOGGLE_BUTTON:
4064 ia_role = ROLE_SYSTEM_PUSHBUTTON;
4065 ia2_role = IA2_ROLE_TOGGLE_BUTTON;
4066 break;
4067 case ui::AX_ROLE_TEXT_AREA:
4068 ia_role = ROLE_SYSTEM_TEXT;
4069 ia2_state |= IA2_STATE_MULTI_LINE;
4070 ia2_state |= IA2_STATE_EDITABLE;
4071 ia2_state |= IA2_STATE_SELECTABLE_TEXT;
4072 break;
4073 case ui::AX_ROLE_TEXT_FIELD:
4074 case ui::AX_ROLE_SEARCH_BOX:
4075 ia_role = ROLE_SYSTEM_TEXT;
4076 ia2_state |= IA2_STATE_SINGLE_LINE;
4077 ia2_state |= IA2_STATE_EDITABLE;
4078 ia2_state |= IA2_STATE_SELECTABLE_TEXT;
4079 break;
4080 case ui::AX_ROLE_TIME:
4081 ia_role = ROLE_SYSTEM_SPINBUTTON;
4082 break;
4083 case ui::AX_ROLE_TIMER:
4084 ia_role = ROLE_SYSTEM_CLOCK;
4085 ia_state |= STATE_SYSTEM_READONLY;
4086 break;
4087 case ui::AX_ROLE_TOOLBAR:
4088 ia_role = ROLE_SYSTEM_TOOLBAR;
4089 ia_state |= STATE_SYSTEM_READONLY;
4090 break;
4091 case ui::AX_ROLE_TOOLTIP:
4092 ia_role = ROLE_SYSTEM_TOOLTIP;
4093 ia_state |= STATE_SYSTEM_READONLY;
4094 break;
4095 case ui::AX_ROLE_TREE:
4096 ia_role = ROLE_SYSTEM_OUTLINE;
4097 break;
4098 case ui::AX_ROLE_TREE_GRID:
4099 ia_role = ROLE_SYSTEM_OUTLINE;
4100 break;
4101 case ui::AX_ROLE_TREE_ITEM:
4102 ia_role = ROLE_SYSTEM_OUTLINEITEM;
4103 break;
4104 case ui::AX_ROLE_LINE_BREAK:
4105 ia_role = ROLE_SYSTEM_WHITESPACE;
4106 break;
4107 case ui::AX_ROLE_WINDOW:
4108 ia_role = ROLE_SYSTEM_WINDOW;
4109 break;
4111 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
4112 case ui::AX_ROLE_DIRECTORY:
4113 case ui::AX_ROLE_IGNORED:
4114 case ui::AX_ROLE_LOG:
4115 case ui::AX_ROLE_NONE:
4116 case ui::AX_ROLE_PRESENTATIONAL:
4117 case ui::AX_ROLE_SLIDER_THUMB:
4118 default:
4119 ia_role = ROLE_SYSTEM_CLIENT;
4120 break;
4123 // Compute the final value of READONLY for MSAA.
4125 // We always set the READONLY state for elements that have the
4126 // aria-readonly attribute and for a few roles (in the switch above).
4127 // We clear the READONLY state on focusable controls and on a document.
4128 // Everything else, the majority of objects, do not have this state set.
4129 if (HasState(ui::AX_STATE_FOCUSABLE) &&
4130 ia_role != ROLE_SYSTEM_DOCUMENT) {
4131 ia_state &= ~(STATE_SYSTEM_READONLY);
4133 if (!HasState(ui::AX_STATE_READ_ONLY))
4134 ia_state &= ~(STATE_SYSTEM_READONLY);
4135 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
4136 ia_state |= STATE_SYSTEM_READONLY;
4138 // The role should always be set.
4139 DCHECK(!role_name.empty() || ia_role);
4141 // If we didn't explicitly set the IAccessible2 role, make it the same
4142 // as the MSAA role.
4143 if (!ia2_role)
4144 ia2_role = ia_role;
4146 win_attributes_->ia_role = ia_role;
4147 win_attributes_->ia_state = ia_state;
4148 win_attributes_->role_name = role_name;
4149 win_attributes_->ia2_role = ia2_role;
4150 win_attributes_->ia2_state = ia2_state;
4153 } // namespace content