[Storage] Blob Storage Refactoring pt 1:
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_win.cc
blob752d1539ca43f4216c36025941d88ef5a081165d
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
181 // static
182 BrowserAccessibility* BrowserAccessibility::Create() {
183 ui::win::CreateATLModuleIfNeeded();
184 CComObject<BrowserAccessibilityWin>* instance;
185 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance);
186 DCHECK(SUCCEEDED(hr));
187 return instance->NewReference();
190 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() {
191 return static_cast<BrowserAccessibilityWin*>(this);
194 BrowserAccessibilityWin::BrowserAccessibilityWin()
195 : ia_role_(0),
196 ia_state_(0),
197 ia2_role_(0),
198 ia2_state_(0),
199 first_time_(true),
200 old_ia_state_(0),
201 previous_scroll_x_(0),
202 previous_scroll_y_(0) {
203 // Start unique IDs at -1 and decrement each time, because get_accChild
204 // uses positive IDs to enumerate children, so we use negative IDs to
205 // clearly distinguish between indices and unique IDs.
206 unique_id_win_ = next_unique_id_win_;
207 if (next_unique_id_win_ ==
208 base::win::kLastBrowserAccessibilityManagerAccessibilityId) {
209 next_unique_id_win_ =
210 base::win::kFirstBrowserAccessibilityManagerAccessibilityId;
212 next_unique_id_win_--;
215 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
216 for (size_t i = 0; i < relations_.size(); ++i)
217 relations_[i]->Release();
221 // IAccessible methods.
223 // Conventions:
224 // * Always test for instance_active() first and return E_FAIL if it's false.
225 // * Always check for invalid arguments first, even if they're unused.
226 // * Return S_FALSE if the only output is a string argument and it's empty.
229 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) {
230 if (!instance_active())
231 return E_FAIL;
233 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
234 if (!target)
235 return E_INVALIDARG;
237 manager()->DoDefaultAction(*target);
238 return S_OK;
241 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left,
242 LONG y_top,
243 VARIANT* child) {
244 if (!instance_active())
245 return E_FAIL;
247 if (!child)
248 return E_INVALIDARG;
250 gfx::Point point(x_left, y_top);
251 if (!GetGlobalBoundsRect().Contains(point)) {
252 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
253 child->vt = VT_EMPTY;
254 return S_FALSE;
257 BrowserAccessibility* result = BrowserAccessibilityForPoint(point);
258 if (result == this) {
259 // Point is within this object.
260 child->vt = VT_I4;
261 child->lVal = CHILDID_SELF;
262 } else {
263 child->vt = VT_DISPATCH;
264 child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
266 return S_OK;
269 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left,
270 LONG* y_top,
271 LONG* width,
272 LONG* height,
273 VARIANT var_id) {
274 if (!instance_active())
275 return E_FAIL;
277 if (!x_left || !y_top || !width || !height)
278 return E_INVALIDARG;
280 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
281 if (!target)
282 return E_INVALIDARG;
284 gfx::Rect bounds = target->GetGlobalBoundsRect();
285 *x_left = bounds.x();
286 *y_top = bounds.y();
287 *width = bounds.width();
288 *height = bounds.height();
290 return S_OK;
293 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
294 VARIANT start,
295 VARIANT* end) {
296 BrowserAccessibilityWin* target = GetTargetFromChildID(start);
297 if (!target)
298 return E_INVALIDARG;
300 if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) &&
301 start.lVal != CHILDID_SELF) {
302 // MSAA states that navigating to first/last child can only be from self.
303 return E_INVALIDARG;
306 uint32 child_count = target->PlatformChildCount();
308 BrowserAccessibility* result = NULL;
309 switch (nav_dir) {
310 case NAVDIR_DOWN:
311 case NAVDIR_UP:
312 case NAVDIR_LEFT:
313 case NAVDIR_RIGHT:
314 // These directions are not implemented, matching Mozilla and IE.
315 return E_NOTIMPL;
316 case NAVDIR_FIRSTCHILD:
317 if (child_count > 0)
318 result = target->PlatformGetChild(0);
319 break;
320 case NAVDIR_LASTCHILD:
321 if (child_count > 0)
322 result = target->PlatformGetChild(child_count - 1);
323 break;
324 case NAVDIR_NEXT:
325 result = target->GetNextSibling();
326 break;
327 case NAVDIR_PREVIOUS:
328 result = target->GetPreviousSibling();
329 break;
332 if (!result) {
333 end->vt = VT_EMPTY;
334 return S_FALSE;
337 end->vt = VT_DISPATCH;
338 end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference();
339 return S_OK;
342 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child,
343 IDispatch** disp_child) {
344 if (!instance_active())
345 return E_FAIL;
347 if (!disp_child)
348 return E_INVALIDARG;
350 *disp_child = NULL;
352 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child);
353 if (!target)
354 return E_INVALIDARG;
356 (*disp_child) = target->NewReference();
357 return S_OK;
360 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) {
361 if (!instance_active())
362 return E_FAIL;
364 if (!child_count)
365 return E_INVALIDARG;
367 *child_count = PlatformChildCount();
369 return S_OK;
372 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id,
373 BSTR* def_action) {
374 if (!instance_active())
375 return E_FAIL;
377 if (!def_action)
378 return E_INVALIDARG;
380 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
381 if (!target)
382 return E_INVALIDARG;
384 return target->GetStringAttributeAsBstr(
385 ui::AX_ATTR_SHORTCUT, def_action);
388 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id,
389 BSTR* desc) {
390 if (!instance_active())
391 return E_FAIL;
393 if (!desc)
394 return E_INVALIDARG;
396 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
397 if (!target)
398 return E_INVALIDARG;
400 return target->GetStringAttributeAsBstr(
401 ui::AX_ATTR_DESCRIPTION, desc);
404 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) {
405 if (!instance_active())
406 return E_FAIL;
408 if (!focus_child)
409 return E_INVALIDARG;
411 BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>(
412 manager()->GetFocus(this));
413 if (focus == this) {
414 focus_child->vt = VT_I4;
415 focus_child->lVal = CHILDID_SELF;
416 } else if (focus == NULL) {
417 focus_child->vt = VT_EMPTY;
418 } else {
419 focus_child->vt = VT_DISPATCH;
420 focus_child->pdispVal = focus->NewReference();
423 return S_OK;
426 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) {
427 if (!instance_active())
428 return E_FAIL;
430 if (!help)
431 return E_INVALIDARG;
433 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
434 if (!target)
435 return E_INVALIDARG;
437 return target->GetStringAttributeAsBstr(
438 ui::AX_ATTR_HELP, help);
441 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id,
442 BSTR* acc_key) {
443 if (!instance_active())
444 return E_FAIL;
446 if (!acc_key)
447 return E_INVALIDARG;
449 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
450 if (!target)
451 return E_INVALIDARG;
453 return target->GetStringAttributeAsBstr(
454 ui::AX_ATTR_SHORTCUT, acc_key);
457 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) {
458 if (!instance_active())
459 return E_FAIL;
461 if (!name)
462 return E_INVALIDARG;
464 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
465 if (!target)
466 return E_INVALIDARG;
468 std::string name_str = target->name();
470 // If the name is empty, see if it's labeled by another element.
471 if (name_str.empty()) {
472 int title_elem_id;
473 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
474 &title_elem_id)) {
475 BrowserAccessibility* title_elem =
476 manager()->GetFromID(title_elem_id);
477 if (title_elem)
478 name_str = title_elem->GetTextRecursive();
482 if (name_str.empty())
483 return S_FALSE;
485 *name = SysAllocString(base::UTF8ToUTF16(name_str).c_str());
487 DCHECK(*name);
488 return S_OK;
491 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) {
492 if (!instance_active())
493 return E_FAIL;
495 if (!disp_parent)
496 return E_INVALIDARG;
498 IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin();
499 if (parent_obj == NULL) {
500 // This happens if we're the root of the tree;
501 // return the IAccessible for the window.
502 parent_obj =
503 manager()->ToBrowserAccessibilityManagerWin()->GetParentIAccessible();
504 // |parent| can only be NULL if the manager was created before the parent
505 // IAccessible was known and it wasn't subsequently set before a client
506 // requested it. This has been fixed. |parent| may also be NULL during
507 // destruction. Possible cases where this could occur include tabs being
508 // dragged to a new window, etc.
509 if (!parent_obj) {
510 DVLOG(1) << "In Function: "
511 << __FUNCTION__
512 << ". Parent IAccessible interface is NULL. Returning failure";
513 return E_FAIL;
516 parent_obj->AddRef();
517 *disp_parent = parent_obj;
518 return S_OK;
521 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id,
522 VARIANT* role) {
523 if (!instance_active())
524 return E_FAIL;
526 if (!role)
527 return E_INVALIDARG;
529 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
530 if (!target)
531 return E_INVALIDARG;
533 if (!target->role_name_.empty()) {
534 role->vt = VT_BSTR;
535 role->bstrVal = SysAllocString(target->role_name_.c_str());
536 } else {
537 role->vt = VT_I4;
538 role->lVal = target->ia_role_;
540 return S_OK;
543 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id,
544 VARIANT* state) {
545 if (!instance_active())
546 return E_FAIL;
548 if (!state)
549 return E_INVALIDARG;
551 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
552 if (!target)
553 return E_INVALIDARG;
555 state->vt = VT_I4;
556 state->lVal = target->ia_state_;
557 if (manager()->GetFocus(NULL) == this)
558 state->lVal |= STATE_SYSTEM_FOCUSED;
560 return S_OK;
563 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id,
564 BSTR* value) {
565 if (!instance_active())
566 return E_FAIL;
568 if (!value)
569 return E_INVALIDARG;
571 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
572 if (!target)
573 return E_INVALIDARG;
575 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR ||
576 target->ia_role() == ROLE_SYSTEM_SCROLLBAR ||
577 target->ia_role() == ROLE_SYSTEM_SLIDER) {
578 base::string16 value_text = target->GetValueText();
579 *value = SysAllocString(value_text.c_str());
580 DCHECK(*value);
581 return S_OK;
584 // Expose color well value.
585 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) {
586 int r = target->GetIntAttribute(
587 ui::AX_ATTR_COLOR_VALUE_RED);
588 int g = target->GetIntAttribute(
589 ui::AX_ATTR_COLOR_VALUE_GREEN);
590 int b = target->GetIntAttribute(
591 ui::AX_ATTR_COLOR_VALUE_BLUE);
592 base::string16 value_text;
593 value_text = base::IntToString16((r * 100) / 255) + L"% red " +
594 base::IntToString16((g * 100) / 255) + L"% green " +
595 base::IntToString16((b * 100) / 255) + L"% blue";
596 *value = SysAllocString(value_text.c_str());
597 DCHECK(*value);
598 return S_OK;
601 *value = SysAllocString(base::UTF8ToUTF16(target->value()).c_str());
602 DCHECK(*value);
603 return S_OK;
606 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file,
607 VARIANT var_id,
608 LONG* topic_id) {
609 return E_NOTIMPL;
612 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
613 if (!instance_active())
614 return E_FAIL;
616 if (GetRole() != ui::AX_ROLE_LIST_BOX)
617 return E_NOTIMPL;
619 unsigned long selected_count = 0;
620 for (size_t i = 0; i < InternalChildCount(); ++i) {
621 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED))
622 ++selected_count;
625 if (selected_count == 0) {
626 selected->vt = VT_EMPTY;
627 return S_OK;
630 if (selected_count == 1) {
631 for (size_t i = 0; i < InternalChildCount(); ++i) {
632 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
633 selected->vt = VT_DISPATCH;
634 selected->pdispVal =
635 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
636 return S_OK;
641 // Multiple items are selected.
642 base::win::EnumVariant* enum_variant =
643 new base::win::EnumVariant(selected_count);
644 enum_variant->AddRef();
645 unsigned long index = 0;
646 for (size_t i = 0; i < InternalChildCount(); ++i) {
647 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) {
648 enum_variant->ItemAt(index)->vt = VT_DISPATCH;
649 enum_variant->ItemAt(index)->pdispVal =
650 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference();
651 ++index;
654 selected->vt = VT_UNKNOWN;
655 selected->punkVal = static_cast<IUnknown*>(
656 static_cast<base::win::IUnknownImpl*>(enum_variant));
657 return S_OK;
660 STDMETHODIMP BrowserAccessibilityWin::accSelect(
661 LONG flags_sel, VARIANT var_id) {
662 if (!instance_active())
663 return E_FAIL;
665 if (flags_sel & SELFLAG_TAKEFOCUS) {
666 manager()->SetFocus(this, true);
667 return S_OK;
670 return S_FALSE;
674 // IAccessible2 methods.
677 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) {
678 if (!instance_active())
679 return E_FAIL;
681 if (!role)
682 return E_INVALIDARG;
684 *role = ia2_role_;
686 return S_OK;
689 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) {
690 if (!instance_active())
691 return E_FAIL;
693 if (!attributes)
694 return E_INVALIDARG;
696 // The iaccessible2 attributes are a set of key-value pairs
697 // separated by semicolons, with a colon between the key and the value.
698 base::string16 str;
699 const std::vector<base::string16>& attributes_list = ia2_attributes();
700 for (unsigned int i = 0; i < attributes_list.size(); ++i) {
701 str += attributes_list[i] + L';';
704 if (str.empty())
705 return S_FALSE;
707 *attributes = SysAllocString(str.c_str());
708 DCHECK(*attributes);
709 return S_OK;
712 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) {
713 if (!instance_active())
714 return E_FAIL;
716 if (!states)
717 return E_INVALIDARG;
719 *states = ia2_state_;
721 return S_OK;
724 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) {
725 if (!instance_active())
726 return E_FAIL;
728 if (!unique_id)
729 return E_INVALIDARG;
731 *unique_id = unique_id_win_;
732 return S_OK;
735 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) {
736 if (!instance_active())
737 return E_FAIL;
739 if (!window_handle)
740 return E_INVALIDARG;
742 *window_handle =
743 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
744 if (!*window_handle)
745 return E_FAIL;
747 return S_OK;
750 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) {
751 if (!instance_active())
752 return E_FAIL;
754 if (!index_in_parent)
755 return E_INVALIDARG;
757 *index_in_parent = this->GetIndexInParent();
758 return S_OK;
761 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) {
762 if (!instance_active())
763 return E_FAIL;
765 if (!n_relations)
766 return E_INVALIDARG;
768 *n_relations = relations_.size();
769 return S_OK;
772 STDMETHODIMP BrowserAccessibilityWin::get_relation(
773 LONG relation_index,
774 IAccessibleRelation** relation) {
775 if (!instance_active())
776 return E_FAIL;
778 if (relation_index < 0 ||
779 relation_index >= static_cast<long>(relations_.size())) {
780 return E_INVALIDARG;
783 if (!relation)
784 return E_INVALIDARG;
786 relations_[relation_index]->AddRef();
787 *relation = relations_[relation_index];
788 return S_OK;
791 STDMETHODIMP BrowserAccessibilityWin::get_relations(
792 LONG max_relations,
793 IAccessibleRelation** relations,
794 LONG* n_relations) {
795 if (!instance_active())
796 return E_FAIL;
798 if (!relations || !n_relations)
799 return E_INVALIDARG;
801 long count = static_cast<long>(relations_.size());
802 *n_relations = count;
803 if (count == 0)
804 return S_FALSE;
806 for (long i = 0; i < count; ++i) {
807 relations_[i]->AddRef();
808 relations[i] = relations_[i];
811 return S_OK;
814 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) {
815 if (!instance_active())
816 return E_FAIL;
818 gfx::Rect r = GetLocation();
819 switch(scroll_type) {
820 case IA2_SCROLL_TYPE_TOP_LEFT:
821 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0));
822 break;
823 case IA2_SCROLL_TYPE_BOTTOM_RIGHT:
824 manager()->ScrollToMakeVisible(
825 *this, gfx::Rect(r.right(), r.bottom(), 0, 0));
826 break;
827 case IA2_SCROLL_TYPE_TOP_EDGE:
828 manager()->ScrollToMakeVisible(
829 *this, gfx::Rect(r.x(), r.y(), r.width(), 0));
830 break;
831 case IA2_SCROLL_TYPE_BOTTOM_EDGE:
832 manager()->ScrollToMakeVisible(
833 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0));
834 break;
835 case IA2_SCROLL_TYPE_LEFT_EDGE:
836 manager()->ScrollToMakeVisible(
837 *this, gfx::Rect(r.x(), r.y(), 0, r.height()));
838 break;
839 case IA2_SCROLL_TYPE_RIGHT_EDGE:
840 manager()->ScrollToMakeVisible(
841 *this, gfx::Rect(r.right(), r.y(), 0, r.height()));
842 break;
843 case IA2_SCROLL_TYPE_ANYWHERE:
844 default:
845 manager()->ScrollToMakeVisible(*this, r);
846 break;
849 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
851 return S_OK;
854 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint(
855 enum IA2CoordinateType coordinate_type,
856 LONG x,
857 LONG y) {
858 if (!instance_active())
859 return E_FAIL;
861 gfx::Point scroll_to(x, y);
863 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
864 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin();
865 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
866 if (GetParent())
867 scroll_to += GetParent()->GetLocation().OffsetFromOrigin();
868 } else {
869 return E_INVALIDARG;
872 manager()->ScrollToPoint(*this, scroll_to);
873 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
875 return S_OK;
878 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
879 LONG* group_level,
880 LONG* similar_items_in_group,
881 LONG* position_in_group) {
882 if (!instance_active())
883 return E_FAIL;
885 if (!group_level || !similar_items_in_group || !position_in_group)
886 return E_INVALIDARG;
888 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
889 GetParent() &&
890 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
891 *group_level = 0;
892 *similar_items_in_group = GetParent()->PlatformChildCount();
893 *position_in_group = GetIndexInParent() + 1;
894 return S_OK;
897 return E_NOTIMPL;
901 // IAccessibleApplication methods.
904 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) {
905 // No need to check |instance_active()| because this interface is
906 // global, and doesn't depend on any local state.
908 if (!app_name)
909 return E_INVALIDARG;
911 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
912 // the part before the "/".
913 std::vector<std::string> product_components;
914 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
915 DCHECK_EQ(2U, product_components.size());
916 if (product_components.size() != 2)
917 return E_FAIL;
918 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str());
919 DCHECK(*app_name);
920 return *app_name ? S_OK : E_FAIL;
923 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) {
924 // No need to check |instance_active()| because this interface is
925 // global, and doesn't depend on any local state.
927 if (!app_version)
928 return E_INVALIDARG;
930 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
931 // the part after the "/".
932 std::vector<std::string> product_components;
933 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components);
934 DCHECK_EQ(2U, product_components.size());
935 if (product_components.size() != 2)
936 return E_FAIL;
937 *app_version =
938 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str());
939 DCHECK(*app_version);
940 return *app_version ? S_OK : E_FAIL;
943 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) {
944 // No need to check |instance_active()| because this interface is
945 // global, and doesn't depend on any local state.
947 if (!toolkit_name)
948 return E_INVALIDARG;
950 // This is hard-coded; all products based on the Chromium engine
951 // will have the same toolkit name, so that assistive technology can
952 // detect any Chrome-based product.
953 *toolkit_name = SysAllocString(L"Chrome");
954 DCHECK(*toolkit_name);
955 return *toolkit_name ? S_OK : E_FAIL;
958 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion(
959 BSTR* toolkit_version) {
960 // No need to check |instance_active()| because this interface is
961 // global, and doesn't depend on any local state.
963 if (!toolkit_version)
964 return E_INVALIDARG;
966 std::string user_agent = GetContentClient()->GetUserAgent();
967 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str());
968 DCHECK(*toolkit_version);
969 return *toolkit_version ? S_OK : E_FAIL;
973 // IAccessibleImage methods.
976 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
977 if (!instance_active())
978 return E_FAIL;
980 if (!desc)
981 return E_INVALIDARG;
983 return GetStringAttributeAsBstr(
984 ui::AX_ATTR_DESCRIPTION, desc);
987 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition(
988 enum IA2CoordinateType coordinate_type,
989 LONG* x,
990 LONG* y) {
991 if (!instance_active())
992 return E_FAIL;
994 if (!x || !y)
995 return E_INVALIDARG;
997 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
998 HWND parent_hwnd =
999 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
1000 if (!parent_hwnd)
1001 return E_FAIL;
1002 POINT top_left = {0, 0};
1003 ::ClientToScreen(parent_hwnd, &top_left);
1004 *x = GetLocation().x() + top_left.x;
1005 *y = GetLocation().y() + top_left.y;
1006 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1007 *x = GetLocation().x();
1008 *y = GetLocation().y();
1009 if (GetParent()) {
1010 *x -= GetParent()->GetLocation().x();
1011 *y -= GetParent()->GetLocation().y();
1013 } else {
1014 return E_INVALIDARG;
1017 return S_OK;
1020 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) {
1021 if (!instance_active())
1022 return E_FAIL;
1024 if (!height || !width)
1025 return E_INVALIDARG;
1027 *height = GetLocation().height();
1028 *width = GetLocation().width();
1029 return S_OK;
1033 // IAccessibleTable methods.
1036 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt(
1037 long row,
1038 long column,
1039 IUnknown** accessible) {
1040 if (!instance_active())
1041 return E_FAIL;
1043 if (!accessible)
1044 return E_INVALIDARG;
1046 int columns;
1047 int rows;
1048 if (!GetIntAttribute(
1049 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1050 !GetIntAttribute(
1051 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1052 columns <= 0 ||
1053 rows <= 0) {
1054 return S_FALSE;
1057 if (row < 0 || row >= rows || column < 0 || column >= columns)
1058 return E_INVALIDARG;
1060 const std::vector<int32>& cell_ids = GetIntListAttribute(
1061 ui::AX_ATTR_CELL_IDS);
1062 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1064 int cell_id = cell_ids[row * columns + column];
1065 BrowserAccessibilityWin* cell = GetFromID(cell_id);
1066 if (cell) {
1067 *accessible = static_cast<IAccessible*>(cell->NewReference());
1068 return S_OK;
1071 *accessible = NULL;
1072 return E_INVALIDARG;
1075 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) {
1076 if (!instance_active())
1077 return E_FAIL;
1079 if (!accessible)
1080 return E_INVALIDARG;
1082 // TODO(dmazzoni): implement
1083 return S_FALSE;
1086 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row,
1087 long column,
1088 long* cell_index) {
1089 if (!instance_active())
1090 return E_FAIL;
1092 if (!cell_index)
1093 return E_INVALIDARG;
1095 int columns;
1096 int rows;
1097 if (!GetIntAttribute(
1098 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1099 !GetIntAttribute(
1100 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1101 columns <= 0 ||
1102 rows <= 0) {
1103 return S_FALSE;
1106 if (row < 0 || row >= rows || column < 0 || column >= columns)
1107 return E_INVALIDARG;
1109 const std::vector<int32>& cell_ids = GetIntListAttribute(
1110 ui::AX_ATTR_CELL_IDS);
1111 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1112 ui::AX_ATTR_UNIQUE_CELL_IDS);
1113 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size()));
1114 int cell_id = cell_ids[row * columns + column];
1115 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
1116 if (unique_cell_ids[i] == cell_id) {
1117 *cell_index = (long)i;
1118 return S_OK;
1122 return S_FALSE;
1125 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column,
1126 BSTR* description) {
1127 if (!instance_active())
1128 return E_FAIL;
1130 if (!description)
1131 return E_INVALIDARG;
1133 int columns;
1134 int rows;
1135 if (!GetIntAttribute(
1136 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1137 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1138 columns <= 0 ||
1139 rows <= 0) {
1140 return S_FALSE;
1143 if (column < 0 || column >= columns)
1144 return E_INVALIDARG;
1146 const std::vector<int32>& cell_ids = GetIntListAttribute(
1147 ui::AX_ATTR_CELL_IDS);
1148 for (int i = 0; i < rows; ++i) {
1149 int cell_id = cell_ids[i * columns + column];
1150 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1151 manager()->GetFromID(cell_id));
1152 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1153 base::string16 cell_name = cell->GetString16Attribute(
1154 ui::AX_ATTR_NAME);
1155 if (cell_name.size() > 0) {
1156 *description = SysAllocString(cell_name.c_str());
1157 return S_OK;
1160 return cell->GetStringAttributeAsBstr(
1161 ui::AX_ATTR_DESCRIPTION, description);
1165 return S_FALSE;
1168 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt(
1169 long row,
1170 long column,
1171 long* n_columns_spanned) {
1172 if (!instance_active())
1173 return E_FAIL;
1175 if (!n_columns_spanned)
1176 return E_INVALIDARG;
1178 int columns;
1179 int rows;
1180 if (!GetIntAttribute(
1181 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1182 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1183 columns <= 0 ||
1184 rows <= 0) {
1185 return S_FALSE;
1188 if (row < 0 || row >= rows || column < 0 || column >= columns)
1189 return E_INVALIDARG;
1191 const std::vector<int32>& cell_ids = GetIntListAttribute(
1192 ui::AX_ATTR_CELL_IDS);
1193 int cell_id = cell_ids[row * columns + column];
1194 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>(
1195 manager()->GetFromID(cell_id));
1196 int colspan;
1197 if (cell &&
1198 cell->GetIntAttribute(
1199 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1200 colspan >= 1) {
1201 *n_columns_spanned = colspan;
1202 return S_OK;
1205 return S_FALSE;
1208 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader(
1209 IAccessibleTable** accessible_table,
1210 long* starting_row_index) {
1211 // TODO(dmazzoni): implement
1212 return E_NOTIMPL;
1215 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index,
1216 long* column_index) {
1217 if (!instance_active())
1218 return E_FAIL;
1220 if (!column_index)
1221 return E_INVALIDARG;
1223 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1224 ui::AX_ATTR_UNIQUE_CELL_IDS);
1225 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1226 if (cell_index < 0)
1227 return E_INVALIDARG;
1228 if (cell_index >= cell_id_count)
1229 return S_FALSE;
1231 int cell_id = unique_cell_ids[cell_index];
1232 BrowserAccessibilityWin* cell =
1233 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1234 int col_index;
1235 if (cell &&
1236 cell->GetIntAttribute(
1237 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) {
1238 *column_index = col_index;
1239 return S_OK;
1242 return S_FALSE;
1245 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) {
1246 if (!instance_active())
1247 return E_FAIL;
1249 if (!column_count)
1250 return E_INVALIDARG;
1252 int columns;
1253 if (GetIntAttribute(
1254 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) {
1255 *column_count = columns;
1256 return S_OK;
1259 return S_FALSE;
1262 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) {
1263 if (!instance_active())
1264 return E_FAIL;
1266 if (!row_count)
1267 return E_INVALIDARG;
1269 int rows;
1270 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1271 *row_count = rows;
1272 return S_OK;
1275 return S_FALSE;
1278 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) {
1279 if (!instance_active())
1280 return E_FAIL;
1282 if (!cell_count)
1283 return E_INVALIDARG;
1285 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1286 *cell_count = 0;
1287 return S_OK;
1290 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) {
1291 if (!instance_active())
1292 return E_FAIL;
1294 if (!column_count)
1295 return E_INVALIDARG;
1297 *column_count = 0;
1298 return S_OK;
1301 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) {
1302 if (!instance_active())
1303 return E_FAIL;
1305 if (!row_count)
1306 return E_INVALIDARG;
1308 *row_count = 0;
1309 return S_OK;
1312 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row,
1313 BSTR* description) {
1314 if (!instance_active())
1315 return E_FAIL;
1317 if (!description)
1318 return E_INVALIDARG;
1320 int columns;
1321 int rows;
1322 if (!GetIntAttribute(
1323 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1324 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1325 columns <= 0 ||
1326 rows <= 0) {
1327 return S_FALSE;
1330 if (row < 0 || row >= rows)
1331 return E_INVALIDARG;
1333 const std::vector<int32>& cell_ids = GetIntListAttribute(
1334 ui::AX_ATTR_CELL_IDS);
1335 for (int i = 0; i < columns; ++i) {
1336 int cell_id = cell_ids[row * columns + i];
1337 BrowserAccessibilityWin* cell =
1338 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1339 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1340 base::string16 cell_name = cell->GetString16Attribute(
1341 ui::AX_ATTR_NAME);
1342 if (cell_name.size() > 0) {
1343 *description = SysAllocString(cell_name.c_str());
1344 return S_OK;
1347 return cell->GetStringAttributeAsBstr(
1348 ui::AX_ATTR_DESCRIPTION, description);
1352 return S_FALSE;
1355 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row,
1356 long column,
1357 long* n_rows_spanned) {
1358 if (!instance_active())
1359 return E_FAIL;
1361 if (!n_rows_spanned)
1362 return E_INVALIDARG;
1364 int columns;
1365 int rows;
1366 if (!GetIntAttribute(
1367 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1368 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) ||
1369 columns <= 0 ||
1370 rows <= 0) {
1371 return S_FALSE;
1374 if (row < 0 || row >= rows || column < 0 || column >= columns)
1375 return E_INVALIDARG;
1377 const std::vector<int32>& cell_ids = GetIntListAttribute(
1378 ui::AX_ATTR_CELL_IDS);
1379 int cell_id = cell_ids[row * columns + column];
1380 BrowserAccessibilityWin* cell =
1381 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1382 int rowspan;
1383 if (cell &&
1384 cell->GetIntAttribute(
1385 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1386 rowspan >= 1) {
1387 *n_rows_spanned = rowspan;
1388 return S_OK;
1391 return S_FALSE;
1394 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader(
1395 IAccessibleTable** accessible_table,
1396 long* starting_column_index) {
1397 // TODO(dmazzoni): implement
1398 return E_NOTIMPL;
1401 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index,
1402 long* row_index) {
1403 if (!instance_active())
1404 return E_FAIL;
1406 if (!row_index)
1407 return E_INVALIDARG;
1409 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1410 ui::AX_ATTR_UNIQUE_CELL_IDS);
1411 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1412 if (cell_index < 0)
1413 return E_INVALIDARG;
1414 if (cell_index >= cell_id_count)
1415 return S_FALSE;
1417 int cell_id = unique_cell_ids[cell_index];
1418 BrowserAccessibilityWin* cell =
1419 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1420 int cell_row_index;
1421 if (cell &&
1422 cell->GetIntAttribute(
1423 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) {
1424 *row_index = cell_row_index;
1425 return S_OK;
1428 return S_FALSE;
1431 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children,
1432 long** children,
1433 long* n_children) {
1434 if (!instance_active())
1435 return E_FAIL;
1437 if (!children || !n_children)
1438 return E_INVALIDARG;
1440 // TODO(dmazzoni): Implement this.
1441 *n_children = 0;
1442 return S_OK;
1445 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns,
1446 long** columns,
1447 long* n_columns) {
1448 if (!instance_active())
1449 return E_FAIL;
1451 if (!columns || !n_columns)
1452 return E_INVALIDARG;
1454 // TODO(dmazzoni): Implement this.
1455 *n_columns = 0;
1456 return S_OK;
1459 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows,
1460 long** rows,
1461 long* n_rows) {
1462 if (!instance_active())
1463 return E_FAIL;
1465 if (!rows || !n_rows)
1466 return E_INVALIDARG;
1468 // TODO(dmazzoni): Implement this.
1469 *n_rows = 0;
1470 return S_OK;
1473 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) {
1474 if (!instance_active())
1475 return E_FAIL;
1477 if (!accessible)
1478 return E_INVALIDARG;
1480 // TODO(dmazzoni): implement
1481 return S_FALSE;
1484 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected(
1485 long column,
1486 boolean* is_selected) {
1487 if (!instance_active())
1488 return E_FAIL;
1490 if (!is_selected)
1491 return E_INVALIDARG;
1493 // TODO(dmazzoni): Implement this.
1494 *is_selected = false;
1495 return S_OK;
1498 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row,
1499 boolean* is_selected) {
1500 if (!instance_active())
1501 return E_FAIL;
1503 if (!is_selected)
1504 return E_INVALIDARG;
1506 // TODO(dmazzoni): Implement this.
1507 *is_selected = false;
1508 return S_OK;
1511 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row,
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_rowColumnExtentsAtIndex(
1526 long index,
1527 long* row,
1528 long* column,
1529 long* row_extents,
1530 long* column_extents,
1531 boolean* is_selected) {
1532 if (!instance_active())
1533 return E_FAIL;
1535 if (!row || !column || !row_extents || !column_extents || !is_selected)
1536 return E_INVALIDARG;
1538 const std::vector<int32>& unique_cell_ids = GetIntListAttribute(
1539 ui::AX_ATTR_UNIQUE_CELL_IDS);
1540 int cell_id_count = static_cast<int>(unique_cell_ids.size());
1541 if (index < 0)
1542 return E_INVALIDARG;
1543 if (index >= cell_id_count)
1544 return S_FALSE;
1546 int cell_id = unique_cell_ids[index];
1547 BrowserAccessibilityWin* cell =
1548 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1549 int rowspan;
1550 int colspan;
1551 if (cell &&
1552 cell->GetIntAttribute(
1553 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1554 cell->GetIntAttribute(
1555 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1556 rowspan >= 1 &&
1557 colspan >= 1) {
1558 *row_extents = rowspan;
1559 *column_extents = colspan;
1560 return S_OK;
1563 return S_FALSE;
1567 // IAccessibleTable2 methods.
1570 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row,
1571 long column,
1572 IUnknown** cell) {
1573 return get_accessibleAt(row, column, cell);
1576 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) {
1577 return get_nSelectedChildren(cell_count);
1580 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells(
1581 IUnknown*** cells,
1582 long* n_selected_cells) {
1583 if (!instance_active())
1584 return E_FAIL;
1586 if (!cells || !n_selected_cells)
1587 return E_INVALIDARG;
1589 // TODO(dmazzoni): Implement this.
1590 *n_selected_cells = 0;
1591 return S_OK;
1594 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns,
1595 long* n_columns) {
1596 if (!instance_active())
1597 return E_FAIL;
1599 if (!columns || !n_columns)
1600 return E_INVALIDARG;
1602 // TODO(dmazzoni): Implement this.
1603 *n_columns = 0;
1604 return S_OK;
1607 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows,
1608 long* n_rows) {
1609 if (!instance_active())
1610 return E_FAIL;
1612 if (!rows || !n_rows)
1613 return E_INVALIDARG;
1615 // TODO(dmazzoni): Implement this.
1616 *n_rows = 0;
1617 return S_OK;
1622 // IAccessibleTableCell methods.
1625 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent(
1626 long* n_columns_spanned) {
1627 if (!instance_active())
1628 return E_FAIL;
1630 if (!n_columns_spanned)
1631 return E_INVALIDARG;
1633 int colspan;
1634 if (GetIntAttribute(
1635 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) &&
1636 colspan >= 1) {
1637 *n_columns_spanned = colspan;
1638 return S_OK;
1641 return S_FALSE;
1644 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells(
1645 IUnknown*** cell_accessibles,
1646 long* n_column_header_cells) {
1647 if (!instance_active())
1648 return E_FAIL;
1650 if (!cell_accessibles || !n_column_header_cells)
1651 return E_INVALIDARG;
1653 *n_column_header_cells = 0;
1655 int column;
1656 if (!GetIntAttribute(
1657 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1658 return S_FALSE;
1661 BrowserAccessibility* table = GetParent();
1662 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1663 table = table->GetParent();
1664 if (!table) {
1665 NOTREACHED();
1666 return S_FALSE;
1669 int columns;
1670 int rows;
1671 if (!table->GetIntAttribute(
1672 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1673 !table->GetIntAttribute(
1674 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1675 return S_FALSE;
1677 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns)
1678 return S_FALSE;
1680 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1681 ui::AX_ATTR_CELL_IDS);
1683 for (int i = 0; i < rows; ++i) {
1684 int cell_id = cell_ids[i * columns + column];
1685 BrowserAccessibilityWin* cell =
1686 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin();
1687 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER)
1688 (*n_column_header_cells)++;
1691 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1692 (*n_column_header_cells) * sizeof(cell_accessibles[0])));
1693 int index = 0;
1694 for (int i = 0; i < rows; ++i) {
1695 int cell_id = cell_ids[i * columns + column];
1696 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1697 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) {
1698 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1699 cell->ToBrowserAccessibilityWin()->NewReference());
1700 ++index;
1704 return S_OK;
1707 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) {
1708 if (!instance_active())
1709 return E_FAIL;
1711 if (!column_index)
1712 return E_INVALIDARG;
1714 int column;
1715 if (GetIntAttribute(
1716 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) {
1717 *column_index = column;
1718 return S_OK;
1721 return S_FALSE;
1724 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) {
1725 if (!instance_active())
1726 return E_FAIL;
1728 if (!n_rows_spanned)
1729 return E_INVALIDARG;
1731 int rowspan;
1732 if (GetIntAttribute(
1733 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1734 rowspan >= 1) {
1735 *n_rows_spanned = rowspan;
1736 return S_OK;
1739 return S_FALSE;
1742 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells(
1743 IUnknown*** cell_accessibles,
1744 long* n_row_header_cells) {
1745 if (!instance_active())
1746 return E_FAIL;
1748 if (!cell_accessibles || !n_row_header_cells)
1749 return E_INVALIDARG;
1751 *n_row_header_cells = 0;
1753 int row;
1754 if (!GetIntAttribute(
1755 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1756 return S_FALSE;
1759 BrowserAccessibility* table = GetParent();
1760 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
1761 table = table->GetParent();
1762 if (!table) {
1763 NOTREACHED();
1764 return S_FALSE;
1767 int columns;
1768 int rows;
1769 if (!table->GetIntAttribute(
1770 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) ||
1771 !table->GetIntAttribute(
1772 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) {
1773 return S_FALSE;
1775 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows)
1776 return S_FALSE;
1778 const std::vector<int32>& cell_ids = table->GetIntListAttribute(
1779 ui::AX_ATTR_CELL_IDS);
1781 for (int i = 0; i < columns; ++i) {
1782 int cell_id = cell_ids[row * columns + i];
1783 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1784 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER)
1785 (*n_row_header_cells)++;
1788 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc(
1789 (*n_row_header_cells) * sizeof(cell_accessibles[0])));
1790 int index = 0;
1791 for (int i = 0; i < columns; ++i) {
1792 int cell_id = cell_ids[row * columns + i];
1793 BrowserAccessibility* cell = manager()->GetFromID(cell_id);
1794 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) {
1795 (*cell_accessibles)[index] = static_cast<IAccessible*>(
1796 cell->ToBrowserAccessibilityWin()->NewReference());
1797 ++index;
1801 return S_OK;
1804 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) {
1805 if (!instance_active())
1806 return E_FAIL;
1808 if (!row_index)
1809 return E_INVALIDARG;
1811 int row;
1812 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) {
1813 *row_index = row;
1814 return S_OK;
1816 return S_FALSE;
1819 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) {
1820 if (!instance_active())
1821 return E_FAIL;
1823 if (!is_selected)
1824 return E_INVALIDARG;
1826 *is_selected = false;
1827 return S_OK;
1830 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents(
1831 long* row_index,
1832 long* column_index,
1833 long* row_extents,
1834 long* column_extents,
1835 boolean* is_selected) {
1836 if (!instance_active())
1837 return E_FAIL;
1839 if (!row_index ||
1840 !column_index ||
1841 !row_extents ||
1842 !column_extents ||
1843 !is_selected) {
1844 return E_INVALIDARG;
1847 int row;
1848 int column;
1849 int rowspan;
1850 int colspan;
1851 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) &&
1852 GetIntAttribute(
1853 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) &&
1854 GetIntAttribute(
1855 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) &&
1856 GetIntAttribute(
1857 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) {
1858 *row_index = row;
1859 *column_index = column;
1860 *row_extents = rowspan;
1861 *column_extents = colspan;
1862 *is_selected = false;
1863 return S_OK;
1866 return S_FALSE;
1869 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) {
1870 if (!instance_active())
1871 return E_FAIL;
1873 if (!table)
1874 return E_INVALIDARG;
1877 int row;
1878 int column;
1879 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row);
1880 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column);
1882 BrowserAccessibility* find_table = GetParent();
1883 while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE)
1884 find_table = find_table->GetParent();
1885 if (!find_table) {
1886 NOTREACHED();
1887 return S_FALSE;
1890 *table = static_cast<IAccessibleTable*>(
1891 find_table->ToBrowserAccessibilityWin()->NewReference());
1893 return S_OK;
1897 // IAccessibleText methods.
1900 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) {
1901 if (!instance_active())
1902 return E_FAIL;
1904 if (!n_characters)
1905 return E_INVALIDARG;
1907 *n_characters = TextForIAccessibleText().length();
1908 return S_OK;
1911 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) {
1912 if (!instance_active())
1913 return E_FAIL;
1915 if (!offset)
1916 return E_INVALIDARG;
1918 *offset = 0;
1919 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1920 GetRole() == ui::AX_ROLE_TEXT_AREA) {
1921 int sel_start = 0;
1922 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1923 &sel_start))
1924 *offset = sel_start;
1927 return S_OK;
1930 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents(
1931 LONG offset,
1932 enum IA2CoordinateType coordinate_type,
1933 LONG* out_x,
1934 LONG* out_y,
1935 LONG* out_width,
1936 LONG* out_height) {
1937 if (!instance_active())
1938 return E_FAIL;
1940 if (!out_x || !out_y || !out_width || !out_height)
1941 return E_INVALIDARG;
1943 const base::string16& text_str = TextForIAccessibleText();
1944 HandleSpecialTextOffset(text_str, &offset);
1946 if (offset < 0 || offset > static_cast<LONG>(text_str.size()))
1947 return E_INVALIDARG;
1949 gfx::Rect character_bounds;
1950 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) {
1951 character_bounds = GetGlobalBoundsForRange(offset, 1);
1952 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) {
1953 character_bounds = GetLocalBoundsForRange(offset, 1);
1954 character_bounds -= GetLocation().OffsetFromOrigin();
1955 } else {
1956 return E_INVALIDARG;
1959 *out_x = character_bounds.x();
1960 *out_y = character_bounds.y();
1961 *out_width = character_bounds.width();
1962 *out_height = character_bounds.height();
1964 return S_OK;
1967 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) {
1968 if (!instance_active())
1969 return E_FAIL;
1971 if (!n_selections)
1972 return E_INVALIDARG;
1974 *n_selections = 0;
1975 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
1976 GetRole() == ui::AX_ROLE_TEXT_AREA) {
1977 int sel_start = 0;
1978 int sel_end = 0;
1979 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START,
1980 &sel_start) &&
1981 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) &&
1982 sel_start != sel_end)
1983 *n_selections = 1;
1986 return S_OK;
1989 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index,
1990 LONG* start_offset,
1991 LONG* end_offset) {
1992 if (!instance_active())
1993 return E_FAIL;
1995 if (!start_offset || !end_offset || selection_index != 0)
1996 return E_INVALIDARG;
1998 *start_offset = 0;
1999 *end_offset = 0;
2000 if (GetRole() == ui::AX_ROLE_TEXT_FIELD ||
2001 GetRole() == ui::AX_ROLE_TEXT_AREA) {
2002 int sel_start = 0;
2003 int sel_end = 0;
2004 if (GetIntAttribute(
2005 ui::AX_ATTR_TEXT_SEL_START, &sel_start) &&
2006 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) {
2007 *start_offset = sel_start;
2008 *end_offset = sel_end;
2012 return S_OK;
2015 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset,
2016 LONG end_offset,
2017 BSTR* text) {
2018 if (!instance_active())
2019 return E_FAIL;
2021 if (!text)
2022 return E_INVALIDARG;
2024 const base::string16& text_str = TextForIAccessibleText();
2026 // Handle special text offsets.
2027 HandleSpecialTextOffset(text_str, &start_offset);
2028 HandleSpecialTextOffset(text_str, &end_offset);
2030 // The spec allows the arguments to be reversed.
2031 if (start_offset > end_offset) {
2032 LONG tmp = start_offset;
2033 start_offset = end_offset;
2034 end_offset = tmp;
2037 // The spec does not allow the start or end offsets to be out or range;
2038 // we must return an error if so.
2039 LONG len = text_str.length();
2040 if (start_offset < 0)
2041 return E_INVALIDARG;
2042 if (end_offset > len)
2043 return E_INVALIDARG;
2045 base::string16 substr = text_str.substr(start_offset,
2046 end_offset - start_offset);
2047 if (substr.empty())
2048 return S_FALSE;
2050 *text = SysAllocString(substr.c_str());
2051 DCHECK(*text);
2052 return S_OK;
2055 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset(
2056 LONG offset,
2057 enum IA2TextBoundaryType boundary_type,
2058 LONG* start_offset,
2059 LONG* end_offset,
2060 BSTR* text) {
2061 if (!instance_active())
2062 return E_FAIL;
2064 if (!start_offset || !end_offset || !text)
2065 return E_INVALIDARG;
2067 // The IAccessible2 spec says we don't have to implement the "sentence"
2068 // boundary type, we can just let the screenreader handle it.
2069 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2070 *start_offset = 0;
2071 *end_offset = 0;
2072 *text = NULL;
2073 return S_FALSE;
2076 const base::string16& text_str = TextForIAccessibleText();
2078 *start_offset = FindBoundary(
2079 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2080 *end_offset = FindBoundary(
2081 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2082 return get_text(*start_offset, *end_offset, text);
2085 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset(
2086 LONG offset,
2087 enum IA2TextBoundaryType boundary_type,
2088 LONG* start_offset,
2089 LONG* end_offset,
2090 BSTR* text) {
2091 if (!instance_active())
2092 return E_FAIL;
2094 if (!start_offset || !end_offset || !text)
2095 return E_INVALIDARG;
2097 // The IAccessible2 spec says we don't have to implement the "sentence"
2098 // boundary type, we can just let the screenreader handle it.
2099 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2100 *start_offset = 0;
2101 *end_offset = 0;
2102 *text = NULL;
2103 return S_FALSE;
2106 const base::string16& text_str = TextForIAccessibleText();
2108 *start_offset = FindBoundary(
2109 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION);
2110 *end_offset = offset;
2111 return get_text(*start_offset, *end_offset, text);
2114 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset(
2115 LONG offset,
2116 enum IA2TextBoundaryType boundary_type,
2117 LONG* start_offset,
2118 LONG* end_offset,
2119 BSTR* text) {
2120 if (!instance_active())
2121 return E_FAIL;
2123 if (!start_offset || !end_offset || !text)
2124 return E_INVALIDARG;
2126 // The IAccessible2 spec says we don't have to implement the "sentence"
2127 // boundary type, we can just let the screenreader handle it.
2128 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) {
2129 *start_offset = 0;
2130 *end_offset = 0;
2131 *text = NULL;
2132 return S_FALSE;
2135 const base::string16& text_str = TextForIAccessibleText();
2137 *start_offset = offset;
2138 *end_offset = FindBoundary(
2139 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION);
2140 return get_text(*start_offset, *end_offset, text);
2143 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) {
2144 if (!instance_active())
2145 return E_FAIL;
2147 if (!new_text)
2148 return E_INVALIDARG;
2150 base::string16 text = TextForIAccessibleText();
2152 new_text->text = SysAllocString(text.c_str());
2153 new_text->start = 0;
2154 new_text->end = static_cast<long>(text.size());
2155 return S_OK;
2158 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) {
2159 if (!instance_active())
2160 return E_FAIL;
2162 if (!old_text)
2163 return E_INVALIDARG;
2165 old_text->text = SysAllocString(old_text_.c_str());
2166 old_text->start = 0;
2167 old_text->end = static_cast<long>(old_text_.size());
2168 return S_OK;
2171 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint(
2172 LONG x,
2173 LONG y,
2174 enum IA2CoordinateType coord_type,
2175 LONG* offset) {
2176 if (!instance_active())
2177 return E_FAIL;
2179 if (!offset)
2180 return E_INVALIDARG;
2182 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2183 // screen readers still return partially accurate results rather than
2184 // completely failing.
2185 *offset = 0;
2186 return S_OK;
2189 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo(
2190 LONG start_index,
2191 LONG end_index,
2192 enum IA2ScrollType scroll_type) {
2193 // TODO(dmazzoni): adjust this for the start and end index, too.
2194 return scrollTo(scroll_type);
2197 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint(
2198 LONG start_index,
2199 LONG end_index,
2200 enum IA2CoordinateType coordinate_type,
2201 LONG x, LONG y) {
2202 // TODO(dmazzoni): adjust this for the start and end index, too.
2203 return scrollToPoint(coordinate_type, x, y);
2206 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset,
2207 LONG end_offset) {
2208 if (!instance_active())
2209 return E_FAIL;
2211 const base::string16& text_str = TextForIAccessibleText();
2212 HandleSpecialTextOffset(text_str, &start_offset);
2213 HandleSpecialTextOffset(text_str, &end_offset);
2215 manager()->SetTextSelection(*this, start_offset, end_offset);
2216 return S_OK;
2219 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) {
2220 if (!instance_active())
2221 return E_FAIL;
2223 if (selection_index != 0)
2224 return E_INVALIDARG;
2226 manager()->SetTextSelection(*this, 0, 0);
2227 return S_OK;
2230 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) {
2231 if (!instance_active())
2232 return E_FAIL;
2234 const base::string16& text_str = TextForIAccessibleText();
2235 HandleSpecialTextOffset(text_str, &offset);
2236 manager()->SetTextSelection(*this, offset, offset);
2237 return S_OK;
2240 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index,
2241 LONG start_offset,
2242 LONG end_offset) {
2243 if (!instance_active())
2244 return E_FAIL;
2246 if (selection_index != 0)
2247 return E_INVALIDARG;
2249 const base::string16& text_str = TextForIAccessibleText();
2250 HandleSpecialTextOffset(text_str, &start_offset);
2251 HandleSpecialTextOffset(text_str, &end_offset);
2253 manager()->SetTextSelection(*this, start_offset, end_offset);
2254 return S_OK;
2258 // IAccessibleHypertext methods.
2261 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) {
2262 if (!instance_active())
2263 return E_FAIL;
2265 if (!hyperlink_count)
2266 return E_INVALIDARG;
2268 *hyperlink_count = hyperlink_offset_to_index_.size();
2269 return S_OK;
2272 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink(
2273 long index,
2274 IAccessibleHyperlink** hyperlink) {
2275 if (!instance_active())
2276 return E_FAIL;
2278 if (!hyperlink ||
2279 index < 0 ||
2280 index >= static_cast<long>(hyperlinks_.size())) {
2281 return E_INVALIDARG;
2284 BrowserAccessibilityWin* child =
2285 InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin();
2286 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference());
2287 return S_OK;
2290 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex(
2291 long char_index,
2292 long* hyperlink_index) {
2293 if (!instance_active())
2294 return E_FAIL;
2296 if (!hyperlink_index)
2297 return E_INVALIDARG;
2299 *hyperlink_index = -1;
2301 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size()))
2302 return E_INVALIDARG;
2304 std::map<int32, int32>::iterator it =
2305 hyperlink_offset_to_index_.find(char_index);
2306 if (it == hyperlink_offset_to_index_.end())
2307 return E_FAIL;
2309 *hyperlink_index = it->second;
2310 return S_OK;
2314 // IAccessibleValue methods.
2317 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) {
2318 if (!instance_active())
2319 return E_FAIL;
2321 if (!value)
2322 return E_INVALIDARG;
2324 float float_val;
2325 if (GetFloatAttribute(
2326 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) {
2327 value->vt = VT_R8;
2328 value->dblVal = float_val;
2329 return S_OK;
2332 value->vt = VT_EMPTY;
2333 return S_FALSE;
2336 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) {
2337 if (!instance_active())
2338 return E_FAIL;
2340 if (!value)
2341 return E_INVALIDARG;
2343 float float_val;
2344 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
2345 &float_val)) {
2346 value->vt = VT_R8;
2347 value->dblVal = float_val;
2348 return S_OK;
2351 value->vt = VT_EMPTY;
2352 return S_FALSE;
2355 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) {
2356 if (!instance_active())
2357 return E_FAIL;
2359 if (!value)
2360 return E_INVALIDARG;
2362 float float_val;
2363 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
2364 &float_val)) {
2365 value->vt = VT_R8;
2366 value->dblVal = float_val;
2367 return S_OK;
2370 value->vt = VT_EMPTY;
2371 return S_FALSE;
2374 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) {
2375 // TODO(dmazzoni): Implement this.
2376 return E_NOTIMPL;
2380 // ISimpleDOMDocument methods.
2383 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) {
2384 if (!instance_active())
2385 return E_FAIL;
2387 if (!url)
2388 return E_INVALIDARG;
2390 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url);
2393 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) {
2394 if (!instance_active())
2395 return E_FAIL;
2397 if (!title)
2398 return E_INVALIDARG;
2400 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title);
2403 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) {
2404 if (!instance_active())
2405 return E_FAIL;
2407 if (!mime_type)
2408 return E_INVALIDARG;
2410 return GetStringAttributeAsBstr(
2411 ui::AX_ATTR_DOC_MIMETYPE, mime_type);
2414 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) {
2415 if (!instance_active())
2416 return E_FAIL;
2418 if (!doc_type)
2419 return E_INVALIDARG;
2421 return GetStringAttributeAsBstr(
2422 ui::AX_ATTR_DOC_DOCTYPE, doc_type);
2426 // ISimpleDOMNode methods.
2429 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo(
2430 BSTR* node_name,
2431 short* name_space_id,
2432 BSTR* node_value,
2433 unsigned int* num_children,
2434 unsigned int* unique_id,
2435 unsigned short* node_type) {
2436 if (!instance_active())
2437 return E_FAIL;
2439 if (!node_name || !name_space_id || !node_value || !num_children ||
2440 !unique_id || !node_type) {
2441 return E_INVALIDARG;
2444 base::string16 tag;
2445 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag))
2446 *node_name = SysAllocString(tag.c_str());
2447 else
2448 *node_name = NULL;
2450 *name_space_id = 0;
2451 *node_value = SysAllocString(base::UTF8ToUTF16(value()).c_str());
2452 *num_children = PlatformChildCount();
2453 *unique_id = unique_id_win_;
2455 if (ia_role_ == ROLE_SYSTEM_DOCUMENT) {
2456 *node_type = NODETYPE_DOCUMENT;
2457 } else if (ia_role_ == ROLE_SYSTEM_TEXT &&
2458 ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) {
2459 *node_type = NODETYPE_TEXT;
2460 } else {
2461 *node_type = NODETYPE_ELEMENT;
2464 return S_OK;
2467 STDMETHODIMP BrowserAccessibilityWin::get_attributes(
2468 unsigned short max_attribs,
2469 BSTR* attrib_names,
2470 short* name_space_id,
2471 BSTR* attrib_values,
2472 unsigned short* num_attribs) {
2473 if (!instance_active())
2474 return E_FAIL;
2476 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs)
2477 return E_INVALIDARG;
2479 *num_attribs = max_attribs;
2480 if (*num_attribs > GetHtmlAttributes().size())
2481 *num_attribs = GetHtmlAttributes().size();
2483 for (unsigned short i = 0; i < *num_attribs; ++i) {
2484 attrib_names[i] = SysAllocString(
2485 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str());
2486 name_space_id[i] = 0;
2487 attrib_values[i] = SysAllocString(
2488 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str());
2490 return S_OK;
2493 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames(
2494 unsigned short num_attribs,
2495 BSTR* attrib_names,
2496 short* name_space_id,
2497 BSTR* attrib_values) {
2498 if (!instance_active())
2499 return E_FAIL;
2501 if (!attrib_names || !name_space_id || !attrib_values)
2502 return E_INVALIDARG;
2504 for (unsigned short i = 0; i < num_attribs; ++i) {
2505 name_space_id[i] = 0;
2506 bool found = false;
2507 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]);
2508 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) {
2509 if (GetHtmlAttributes()[j].first == name) {
2510 attrib_values[i] = SysAllocString(
2511 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str());
2512 found = true;
2513 break;
2516 if (!found) {
2517 attrib_values[i] = NULL;
2520 return S_OK;
2523 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle(
2524 unsigned short max_style_properties,
2525 boolean use_alternate_view,
2526 BSTR* style_properties,
2527 BSTR* style_values,
2528 unsigned short *num_style_properties) {
2529 if (!instance_active())
2530 return E_FAIL;
2532 if (!style_properties || !style_values)
2533 return E_INVALIDARG;
2535 // We only cache a single style property for now: DISPLAY
2537 base::string16 display;
2538 if (max_style_properties == 0 ||
2539 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) {
2540 *num_style_properties = 0;
2541 return S_OK;
2544 *num_style_properties = 1;
2545 style_properties[0] = SysAllocString(L"display");
2546 style_values[0] = SysAllocString(display.c_str());
2548 return S_OK;
2551 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties(
2552 unsigned short num_style_properties,
2553 boolean use_alternate_view,
2554 BSTR* style_properties,
2555 BSTR* style_values) {
2556 if (!instance_active())
2557 return E_FAIL;
2559 if (!style_properties || !style_values)
2560 return E_INVALIDARG;
2562 // We only cache a single style property for now: DISPLAY
2564 for (unsigned short i = 0; i < num_style_properties; ++i) {
2565 base::string16 name = (LPCWSTR)style_properties[i];
2566 base::StringToLowerASCII(&name);
2567 if (name == L"display") {
2568 base::string16 display = GetString16Attribute(
2569 ui::AX_ATTR_DISPLAY);
2570 style_values[i] = SysAllocString(display.c_str());
2571 } else {
2572 style_values[i] = NULL;
2576 return S_OK;
2579 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) {
2580 return scrollTo(placeTopLeft ?
2581 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE);
2584 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) {
2585 if (!instance_active())
2586 return E_FAIL;
2588 if (!node)
2589 return E_INVALIDARG;
2591 *node = GetParent()->ToBrowserAccessibilityWin()->NewReference();
2592 return S_OK;
2595 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) {
2596 if (!instance_active())
2597 return E_FAIL;
2599 if (!node)
2600 return E_INVALIDARG;
2602 if (PlatformChildCount() == 0) {
2603 *node = NULL;
2604 return S_FALSE;
2607 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2608 return S_OK;
2611 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) {
2612 if (!instance_active())
2613 return E_FAIL;
2615 if (!node)
2616 return E_INVALIDARG;
2618 if (PlatformChildCount() == 0) {
2619 *node = NULL;
2620 return S_FALSE;
2623 *node = PlatformGetChild(PlatformChildCount() - 1)
2624 ->ToBrowserAccessibilityWin()->NewReference();
2625 return S_OK;
2628 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling(
2629 ISimpleDOMNode** node) {
2630 if (!instance_active())
2631 return E_FAIL;
2633 if (!node)
2634 return E_INVALIDARG;
2636 if (!GetParent() || GetIndexInParent() <= 0) {
2637 *node = NULL;
2638 return S_FALSE;
2641 *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2642 ToBrowserAccessibilityWin()->NewReference();
2643 return S_OK;
2646 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) {
2647 if (!instance_active())
2648 return E_FAIL;
2650 if (!node)
2651 return E_INVALIDARG;
2653 if (!GetParent() ||
2654 GetIndexInParent() < 0 ||
2655 GetIndexInParent() >= static_cast<int>(
2656 GetParent()->InternalChildCount()) - 1) {
2657 *node = NULL;
2658 return S_FALSE;
2661 *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2662 ToBrowserAccessibilityWin()->NewReference();
2663 return S_OK;
2666 STDMETHODIMP BrowserAccessibilityWin::get_childAt(
2667 unsigned int child_index,
2668 ISimpleDOMNode** node) {
2669 if (!instance_active())
2670 return E_FAIL;
2672 if (!node)
2673 return E_INVALIDARG;
2675 if (child_index >= PlatformChildCount())
2676 return E_INVALIDARG;
2678 BrowserAccessibility* child = PlatformGetChild(child_index);
2679 if (!child) {
2680 *node = NULL;
2681 return S_FALSE;
2684 *node = child->ToBrowserAccessibilityWin()->NewReference();
2685 return S_OK;
2689 // ISimpleDOMText methods.
2692 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) {
2693 if (!instance_active())
2694 return E_FAIL;
2696 if (!dom_text)
2697 return E_INVALIDARG;
2699 return GetStringAttributeAsBstr(
2700 ui::AX_ATTR_NAME, dom_text);
2703 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds(
2704 unsigned int start_index,
2705 unsigned int end_index,
2706 int* out_x,
2707 int* out_y,
2708 int* out_width,
2709 int* out_height) {
2710 // TODO(dmazzoni): fully support this API by intersecting the
2711 // rect with the container's rect.
2712 return get_unclippedSubstringBounds(
2713 start_index, end_index, out_x, out_y, out_width, out_height);
2716 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds(
2717 unsigned int start_index,
2718 unsigned int end_index,
2719 int* out_x,
2720 int* out_y,
2721 int* out_width,
2722 int* out_height) {
2723 if (!instance_active())
2724 return E_FAIL;
2726 if (!out_x || !out_y || !out_width || !out_height)
2727 return E_INVALIDARG;
2729 const base::string16& text_str = TextForIAccessibleText();
2730 if (start_index > text_str.size() ||
2731 end_index > text_str.size() ||
2732 start_index > end_index) {
2733 return E_INVALIDARG;
2736 gfx::Rect bounds = GetGlobalBoundsForRange(
2737 start_index, end_index - start_index);
2738 *out_x = bounds.x();
2739 *out_y = bounds.y();
2740 *out_width = bounds.width();
2741 *out_height = bounds.height();
2742 return S_OK;
2745 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring(
2746 unsigned int start_index,
2747 unsigned int end_index) {
2748 if (!instance_active())
2749 return E_FAIL;
2751 const base::string16& text_str = TextForIAccessibleText();
2752 if (start_index > text_str.size() ||
2753 end_index > text_str.size() ||
2754 start_index > end_index) {
2755 return E_INVALIDARG;
2758 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2759 start_index, end_index - start_index));
2760 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2762 return S_OK;
2766 // IServiceProvider methods.
2769 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService,
2770 REFIID riid,
2771 void** object) {
2772 if (!instance_active())
2773 return E_FAIL;
2775 // The system uses IAccessible APIs for many purposes, but only
2776 // assistive technology like screen readers uses IAccessible2.
2777 // Enable full accessibility support when IAccessible2 APIs are queried.
2778 if (riid == IID_IAccessible2)
2779 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2781 if (guidService == GUID_IAccessibleContentDocument) {
2782 // Special Mozilla extension: return the accessible for the root document.
2783 // Screen readers use this to distinguish between a document loaded event
2784 // on the root document vs on an iframe.
2785 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2786 IID_IAccessible2, object);
2789 if (guidService == IID_IAccessible ||
2790 guidService == IID_IAccessible2 ||
2791 guidService == IID_IAccessibleAction ||
2792 guidService == IID_IAccessibleApplication ||
2793 guidService == IID_IAccessibleHyperlink ||
2794 guidService == IID_IAccessibleHypertext ||
2795 guidService == IID_IAccessibleImage ||
2796 guidService == IID_IAccessibleTable ||
2797 guidService == IID_IAccessibleTable2 ||
2798 guidService == IID_IAccessibleTableCell ||
2799 guidService == IID_IAccessibleText ||
2800 guidService == IID_IAccessibleValue ||
2801 guidService == IID_ISimpleDOMDocument ||
2802 guidService == IID_ISimpleDOMNode ||
2803 guidService == IID_ISimpleDOMText ||
2804 guidService == GUID_ISimpleDOM) {
2805 return QueryInterface(riid, object);
2808 // We only support the IAccessibleEx interface on Windows 8 and above. This
2809 // is needed for the on-screen Keyboard to show up in metro mode, when the
2810 // user taps an editable portion on the page.
2811 // All methods in the IAccessibleEx interface are unimplemented.
2812 if (riid == IID_IAccessibleEx &&
2813 base::win::GetVersion() >= base::win::VERSION_WIN8) {
2814 return QueryInterface(riid, object);
2817 *object = NULL;
2818 return E_FAIL;
2821 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id,
2822 IUnknown** provider) {
2823 DVLOG(1) << "In Function: "
2824 << __FUNCTION__
2825 << " for pattern id: "
2826 << id;
2827 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) {
2828 if (IsEditableText()) {
2829 DVLOG(1) << "Returning UIA text provider";
2830 base::win::UIATextProvider::CreateTextProvider(
2831 GetValueText(), true, provider);
2832 return S_OK;
2835 return E_NOTIMPL;
2838 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id,
2839 VARIANT* ret) {
2840 DVLOG(1) << "In Function: "
2841 << __FUNCTION__
2842 << " for property id: "
2843 << id;
2844 V_VT(ret) = VT_EMPTY;
2845 if (id == UIA_ControlTypePropertyId) {
2846 if (IsEditableText()) {
2847 V_VT(ret) = VT_I4;
2848 ret->lVal = UIA_EditControlTypeId;
2849 DVLOG(1) << "Returning Edit control type";
2850 } else {
2851 DVLOG(1) << "Returning empty control type";
2854 return S_OK;
2858 // CComObjectRootEx methods.
2861 // static
2862 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface(
2863 void* this_ptr,
2864 const _ATL_INTMAP_ENTRY* entries,
2865 REFIID iid,
2866 void** object) {
2867 BrowserAccessibilityWin* accessibility =
2868 reinterpret_cast<BrowserAccessibilityWin*>(this_ptr);
2869 int32 ia_role = accessibility->ia_role_;
2870 if (iid == IID_IAccessibleImage) {
2871 if (ia_role != ROLE_SYSTEM_GRAPHIC) {
2872 *object = NULL;
2873 return E_NOINTERFACE;
2875 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) {
2876 if (ia_role != ROLE_SYSTEM_TABLE) {
2877 *object = NULL;
2878 return E_NOINTERFACE;
2880 } else if (iid == IID_IAccessibleTableCell) {
2881 if (!accessibility->IsCellOrTableHeaderRole()) {
2882 *object = NULL;
2883 return E_NOINTERFACE;
2885 } else if (iid == IID_IAccessibleValue) {
2886 if (ia_role != ROLE_SYSTEM_PROGRESSBAR &&
2887 ia_role != ROLE_SYSTEM_SCROLLBAR &&
2888 ia_role != ROLE_SYSTEM_SLIDER) {
2889 *object = NULL;
2890 return E_NOINTERFACE;
2892 } else if (iid == IID_ISimpleDOMDocument) {
2893 if (ia_role != ROLE_SYSTEM_DOCUMENT) {
2894 *object = NULL;
2895 return E_NOINTERFACE;
2899 return CComObjectRootBase::InternalQueryInterface(
2900 this_ptr, entries, iid, object);
2904 // Private methods.
2907 // Called every time this node's data changes.
2908 void BrowserAccessibilityWin::OnDataChanged() {
2909 BrowserAccessibility::OnDataChanged();
2911 InitRoleAndState();
2913 // Expose autocomplete attribute for combobox and textbox.
2914 StringAttributeToIA2(ui::AX_ATTR_AUTO_COMPLETE, "autocomplete");
2916 // Expose the "display" and "tag" attributes.
2917 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display");
2918 StringAttributeToIA2(ui::AX_ATTR_TEXT_INPUT_TYPE, "text-input-type");
2919 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag");
2920 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles");
2922 // Expose "level" attribute for headings, trees, etc.
2923 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level");
2925 // Expose the set size and position in set for listbox options.
2926 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
2927 GetParent() &&
2928 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) {
2929 ia2_attributes_.push_back(
2930 L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
2931 ia2_attributes_.push_back(
2932 L"setsize:" + base::IntToString16(GetIndexInParent() + 1));
2935 if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON ||
2936 ia_role_ == ROLE_SYSTEM_RADIOBUTTON ||
2937 ia2_role_ == IA2_ROLE_CHECK_MENU_ITEM ||
2938 ia2_role_ == IA2_ROLE_RADIO_MENU_ITEM ||
2939 ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) {
2940 ia2_attributes_.push_back(L"checkable:true");
2943 // Expose live region attributes.
2944 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live");
2945 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant");
2946 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic");
2947 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy");
2949 // Expose container live region attributes.
2950 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS,
2951 "container-live");
2952 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
2953 "container-relevant");
2954 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
2955 "container-atomic");
2956 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
2957 "container-busy");
2959 // Expose slider value.
2960 if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR ||
2961 ia_role_ == ROLE_SYSTEM_SCROLLBAR ||
2962 ia_role_ == ROLE_SYSTEM_SLIDER) {
2963 ia2_attributes_.push_back(L"valuetext:" + GetValueText());
2966 // Expose table cell index.
2967 if (IsCellOrTableHeaderRole()) {
2968 BrowserAccessibility* table = GetParent();
2969 while (table && table->GetRole() != ui::AX_ROLE_TABLE)
2970 table = table->GetParent();
2971 if (table) {
2972 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute(
2973 ui::AX_ATTR_UNIQUE_CELL_IDS);
2974 for (size_t i = 0; i < unique_cell_ids.size(); ++i) {
2975 if (unique_cell_ids[i] == GetId()) {
2976 ia2_attributes_.push_back(
2977 base::string16(L"table-cell-index:") + base::IntToString16(i));
2983 // Expose invalid state for form controls and elements with aria-invalid.
2984 int invalid_state;
2985 if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE, &invalid_state)) {
2986 // TODO(nektar): Handle the possibility of having multiple aria-invalid
2987 // attributes defined, e.g., "invalid:spelling,grammar".
2988 switch (invalid_state) {
2989 case ui::AX_INVALID_STATE_NONE:
2990 break;
2991 case ui::AX_INVALID_STATE_FALSE:
2992 ia2_attributes_.push_back(L"invalid:false");
2993 break;
2994 case ui::AX_INVALID_STATE_TRUE:
2995 ia2_attributes_.push_back(L"invalid:true");
2996 break;
2997 case ui::AX_INVALID_STATE_SPELLING:
2998 ia2_attributes_.push_back(L"invalid:spelling");
2999 break;
3000 case ui::AX_INVALID_STATE_GRAMMAR:
3001 ia2_attributes_.push_back(L"invalid:grammar");
3002 break;
3003 case ui::AX_INVALID_STATE_OTHER:
3005 base::string16 aria_invalid_value;
3006 if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE,
3007 &aria_invalid_value)) {
3008 ia2_attributes_.push_back(L"invalid:" + aria_invalid_value);
3009 } else {
3010 // Set the attribute to L"true", since we cannot be more specific.
3011 ia2_attributes_.push_back(L"invalid:true");
3014 break;
3015 default:
3016 NOTREACHED();
3020 // The calculation of the accessible name of an element has been
3021 // standardized in the HTML to Platform Accessibility APIs Implementation
3022 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
3023 // appropriate accessible name on Windows, we need to apply some logic
3024 // to the fields we get from WebKit.
3026 // TODO(dmazzoni): move most of this logic into WebKit.
3028 // WebKit gives us:
3030 // name: the default name, e.g. inner text
3031 // title ui element: a reference to a <label> element on the same
3032 // page that labels this node.
3033 // description: accessible labels that override the default name:
3034 // aria-label or aria-labelledby or aria-describedby
3035 // help: the value of the "title" attribute
3037 // On Windows, the logic we apply lets some fields take precedence and
3038 // always returns the primary name in "name" and the secondary name,
3039 // if any, in "description".
3041 int title_elem_id = GetIntAttribute(
3042 ui::AX_ATTR_TITLE_UI_ELEMENT);
3043 std::string help = GetStringAttribute(ui::AX_ATTR_HELP);
3044 std::string description = GetStringAttribute(
3045 ui::AX_ATTR_DESCRIPTION);
3047 // WebKit annoyingly puts the title in the description if there's no other
3048 // description, which just confuses the rest of the logic. Put it back.
3049 // Now "help" is always the value of the "title" attribute, if present.
3050 std::string title_attr;
3051 if (GetHtmlAttribute("title", &title_attr) &&
3052 description == title_attr &&
3053 help.empty()) {
3054 help = description;
3055 description.clear();
3058 // Now implement the main logic: the descripion should become the name if
3059 // it's nonempty, and the help should become the description if
3060 // there's no description - or the name if there's no name or description.
3061 if (!description.empty()) {
3062 set_name(description);
3063 description.clear();
3065 if (!help.empty() && description.empty()) {
3066 description = help;
3067 help.clear();
3069 if (!description.empty() && name().empty() && !title_elem_id) {
3070 set_name(description);
3071 description.clear();
3074 // If it's a text field, also consider the placeholder.
3075 std::string placeholder;
3076 if (GetRole() == ui::AX_ROLE_TEXT_FIELD &&
3077 HasState(ui::AX_STATE_FOCUSABLE) &&
3078 GetHtmlAttribute("placeholder", &placeholder)) {
3079 if (name().empty() && !title_elem_id) {
3080 set_name(placeholder);
3081 } else if (description.empty()) {
3082 description = placeholder;
3086 SetStringAttribute(ui::AX_ATTR_DESCRIPTION, description);
3087 SetStringAttribute(ui::AX_ATTR_HELP, help);
3089 // On Windows, the value of a document should be its url.
3090 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA ||
3091 GetRole() == ui::AX_ROLE_WEB_AREA) {
3092 set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL));
3095 // For certain roles (listbox option, static text, and list marker)
3096 // WebKit stores the main accessible text in the "value" - swap it so
3097 // that it's the "name".
3098 if (name().empty() &&
3099 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION ||
3100 GetRole() == ui::AX_ROLE_STATIC_TEXT ||
3101 GetRole() == ui::AX_ROLE_LIST_MARKER)) {
3102 std::string tmp = value();
3103 set_value(name());
3104 set_name(tmp);
3107 // If this doesn't have a value and is linked then set its value to the url
3108 // attribute. This allows screen readers to read an empty link's destination.
3109 if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED))
3110 set_value(GetStringAttribute(ui::AX_ATTR_URL));
3112 // Clear any old relationships between this node and other nodes.
3113 for (size_t i = 0; i < relations_.size(); ++i)
3114 relations_[i]->Release();
3115 relations_.clear();
3117 // Handle title UI element.
3118 if (title_elem_id) {
3119 // Add a labelled by relationship.
3120 CComObject<BrowserAccessibilityRelation>* relation;
3121 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance(
3122 &relation);
3123 DCHECK(SUCCEEDED(hr));
3124 relation->AddRef();
3125 relation->Initialize(this, IA2_RELATION_LABELLED_BY);
3126 relation->AddTarget(title_elem_id);
3127 relations_.push_back(relation);
3130 // If this is a web area for a presentational iframe, give it a role of
3131 // something other than DOCUMENT so that the fact that it's a separate doc
3132 // is not exposed to AT.
3133 if (IsWebAreaForPresentationalIframe()) {
3134 ia_role_ = ROLE_SYSTEM_GROUPING;
3135 ia2_role_ = ROLE_SYSTEM_GROUPING;
3139 void BrowserAccessibilityWin::OnUpdateFinished() {
3140 // Construct the hypertext for this node.
3141 hyperlink_offset_to_index_.clear();
3142 hyperlinks_.clear();
3143 hypertext_.clear();
3144 for (unsigned int i = 0; i < PlatformChildCount(); ++i) {
3145 BrowserAccessibility* child = PlatformGetChild(i);
3146 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) {
3147 hypertext_ += base::UTF8ToUTF16(child->name());
3148 } else {
3149 hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size();
3150 hypertext_ += kEmbeddedCharacter;
3151 hyperlinks_.push_back(i);
3154 DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size());
3156 // Fire an event when an alert first appears.
3157 if (GetRole() == ui::AX_ROLE_ALERT && first_time_)
3158 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this);
3160 // Fire events if text has changed.
3161 base::string16 text = TextForIAccessibleText();
3162 if (previous_text_ != text) {
3163 if (!previous_text_.empty() && !text.empty()) {
3164 manager()->NotifyAccessibilityEvent(
3165 ui::AX_EVENT_SHOW, this);
3168 // TODO(dmazzoni): Look into HIDE events, too.
3170 old_text_ = previous_text_;
3171 previous_text_ = text;
3174 BrowserAccessibilityManagerWin* manager =
3175 this->manager()->ToBrowserAccessibilityManagerWin();
3177 // Fire events if the state has changed.
3178 if (!first_time_ && ia_state_ != old_ia_state_) {
3179 // Normally focus events are handled elsewhere, however
3180 // focus for managed descendants is platform-specific.
3181 // Fire a focus event if the focused descendant in a multi-select
3182 // list box changes.
3183 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION &&
3184 (ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
3185 (ia_state_ & STATE_SYSTEM_SELECTABLE) &&
3186 (ia_state_ & STATE_SYSTEM_FOCUSED) &&
3187 !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) {
3188 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, unique_id_win());
3191 if ((ia_state_ & STATE_SYSTEM_SELECTED) &&
3192 !(old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3193 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD,
3194 unique_id_win());
3195 } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) &&
3196 (old_ia_state_ & STATE_SYSTEM_SELECTED)) {
3197 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE,
3198 unique_id_win());
3201 old_ia_state_ = ia_state_;
3204 // Fire an event if this container object has scrolled.
3205 int sx = 0;
3206 int sy = 0;
3207 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) &&
3208 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) {
3209 if (!first_time_ &&
3210 (sx != previous_scroll_x_ || sy != previous_scroll_y_)) {
3211 manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND,
3212 unique_id_win());
3214 previous_scroll_x_ = sx;
3215 previous_scroll_y_ = sy;
3218 first_time_ = false;
3221 void BrowserAccessibilityWin::NativeAddReference() {
3222 AddRef();
3225 void BrowserAccessibilityWin::NativeReleaseReference() {
3226 Release();
3229 bool BrowserAccessibilityWin::IsNative() const {
3230 return true;
3233 void BrowserAccessibilityWin::OnLocationChanged() {
3234 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3235 EVENT_OBJECT_LOCATIONCHANGE, unique_id_win());
3238 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() {
3239 AddRef();
3240 return this;
3243 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID(
3244 const VARIANT& var_id) {
3245 if (var_id.vt != VT_I4)
3246 return NULL;
3248 LONG child_id = var_id.lVal;
3249 if (child_id == CHILDID_SELF)
3250 return this;
3252 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount()))
3253 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin();
3255 return manager()->ToBrowserAccessibilityManagerWin()->
3256 GetFromUniqueIdWin(child_id);
3259 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr(
3260 ui::AXStringAttribute attribute,
3261 BSTR* value_bstr) {
3262 base::string16 str;
3264 if (!GetString16Attribute(attribute, &str))
3265 return S_FALSE;
3267 if (str.empty())
3268 return S_FALSE;
3270 *value_bstr = SysAllocString(str.c_str());
3271 DCHECK(*value_bstr);
3273 return S_OK;
3276 void BrowserAccessibilityWin::StringAttributeToIA2(
3277 ui::AXStringAttribute attribute,
3278 const char* ia2_attr) {
3279 base::string16 value;
3280 if (GetString16Attribute(attribute, &value))
3281 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + value);
3284 void BrowserAccessibilityWin::BoolAttributeToIA2(
3285 ui::AXBoolAttribute attribute,
3286 const char* ia2_attr) {
3287 bool value;
3288 if (GetBoolAttribute(attribute, &value)) {
3289 ia2_attributes_.push_back((base::ASCIIToUTF16(ia2_attr) + L":") +
3290 (value ? L"true" : L"false"));
3294 void BrowserAccessibilityWin::IntAttributeToIA2(
3295 ui::AXIntAttribute attribute,
3296 const char* ia2_attr) {
3297 int value;
3298 if (GetIntAttribute(attribute, &value)) {
3299 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" +
3300 base::IntToString16(value));
3304 base::string16 BrowserAccessibilityWin::GetValueText() {
3305 float fval;
3306 base::string16 value = base::UTF8ToUTF16(this->value());
3308 if (value.empty() &&
3309 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) {
3310 value = base::UTF8ToUTF16(base::DoubleToString(fval));
3312 return value;
3315 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() {
3316 if (IsEditableText())
3317 return base::UTF8ToUTF16(value());
3318 return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ?
3319 base::UTF8ToUTF16(name()) : hypertext_;
3322 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3323 const base::string16& text,
3324 LONG* offset) {
3325 if (*offset == IA2_TEXT_OFFSET_LENGTH)
3326 *offset = static_cast<LONG>(text.size());
3327 else if (*offset == IA2_TEXT_OFFSET_CARET)
3328 get_caretOffset(offset);
3331 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3332 IA2TextBoundaryType ia2_boundary) {
3333 switch(ia2_boundary) {
3334 case IA2_TEXT_BOUNDARY_CHAR:
3335 return ui::CHAR_BOUNDARY;
3336 case IA2_TEXT_BOUNDARY_WORD:
3337 return ui::WORD_BOUNDARY;
3338 case IA2_TEXT_BOUNDARY_LINE:
3339 return ui::LINE_BOUNDARY;
3340 case IA2_TEXT_BOUNDARY_SENTENCE:
3341 return ui::SENTENCE_BOUNDARY;
3342 case IA2_TEXT_BOUNDARY_PARAGRAPH:
3343 return ui::PARAGRAPH_BOUNDARY;
3344 case IA2_TEXT_BOUNDARY_ALL:
3345 return ui::ALL_BOUNDARY;
3346 default:
3347 NOTREACHED();
3349 return ui::CHAR_BOUNDARY;
3352 LONG BrowserAccessibilityWin::FindBoundary(
3353 const base::string16& text,
3354 IA2TextBoundaryType ia2_boundary,
3355 LONG start_offset,
3356 ui::TextBoundaryDirection direction) {
3357 HandleSpecialTextOffset(text, &start_offset);
3358 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary);
3359 const std::vector<int32>& line_breaks = GetIntListAttribute(
3360 ui::AX_ATTR_LINE_BREAKS);
3361 return ui::FindAccessibleTextBoundary(
3362 text, line_breaks, boundary, start_offset, direction);
3365 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) {
3366 return manager()->GetFromID(id)->ToBrowserAccessibilityWin();
3369 void BrowserAccessibilityWin::InitRoleAndState() {
3370 ia_state_ = 0;
3371 ia2_state_ = IA2_STATE_OPAQUE;
3372 ia2_attributes_.clear();
3374 if (HasState(ui::AX_STATE_BUSY))
3375 ia_state_ |= STATE_SYSTEM_BUSY;
3376 if (HasState(ui::AX_STATE_CHECKED))
3377 ia_state_ |= STATE_SYSTEM_CHECKED;
3378 if (HasState(ui::AX_STATE_COLLAPSED))
3379 ia_state_ |= STATE_SYSTEM_COLLAPSED;
3380 if (HasState(ui::AX_STATE_EXPANDED))
3381 ia_state_ |= STATE_SYSTEM_EXPANDED;
3382 if (HasState(ui::AX_STATE_FOCUSABLE))
3383 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3384 if (HasState(ui::AX_STATE_HASPOPUP))
3385 ia_state_ |= STATE_SYSTEM_HASPOPUP;
3386 if (HasState(ui::AX_STATE_HOVERED))
3387 ia_state_ |= STATE_SYSTEM_HOTTRACKED;
3388 if (HasState(ui::AX_STATE_INDETERMINATE))
3389 ia_state_ |= STATE_SYSTEM_INDETERMINATE;
3390 if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) &&
3391 GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE)
3392 ia2_state_ |= IA2_STATE_INVALID_ENTRY;
3393 if (HasState(ui::AX_STATE_INVISIBLE))
3394 ia_state_ |= STATE_SYSTEM_INVISIBLE;
3395 if (HasState(ui::AX_STATE_LINKED))
3396 ia_state_ |= STATE_SYSTEM_LINKED;
3397 if (HasState(ui::AX_STATE_MULTISELECTABLE)) {
3398 ia_state_ |= STATE_SYSTEM_EXTSELECTABLE;
3399 ia_state_ |= STATE_SYSTEM_MULTISELECTABLE;
3401 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3402 if (HasState(ui::AX_STATE_OFFSCREEN))
3403 ia_state_ |= STATE_SYSTEM_OFFSCREEN;
3404 if (HasState(ui::AX_STATE_PRESSED))
3405 ia_state_ |= STATE_SYSTEM_PRESSED;
3406 if (HasState(ui::AX_STATE_PROTECTED))
3407 ia_state_ |= STATE_SYSTEM_PROTECTED;
3408 if (HasState(ui::AX_STATE_REQUIRED))
3409 ia2_state_ |= IA2_STATE_REQUIRED;
3410 if (HasState(ui::AX_STATE_SELECTABLE))
3411 ia_state_ |= STATE_SYSTEM_SELECTABLE;
3412 if (HasState(ui::AX_STATE_SELECTED))
3413 ia_state_ |= STATE_SYSTEM_SELECTED;
3414 if (HasState(ui::AX_STATE_VISITED))
3415 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3416 if (!HasState(ui::AX_STATE_ENABLED))
3417 ia_state_ |= STATE_SYSTEM_UNAVAILABLE;
3418 if (HasState(ui::AX_STATE_VERTICAL))
3419 ia2_state_ |= IA2_STATE_VERTICAL;
3420 if (HasState(ui::AX_STATE_HORIZONTAL))
3421 ia2_state_ |= IA2_STATE_HORIZONTAL;
3422 if (HasState(ui::AX_STATE_VISITED))
3423 ia_state_ |= STATE_SYSTEM_TRAVERSED;
3425 // WebKit marks everything as readonly unless it's editable text, so if it's
3426 // not readonly, mark it as editable now. The final computation of the
3427 // READONLY state for MSAA is below, after the switch.
3428 if (!HasState(ui::AX_STATE_READ_ONLY))
3429 ia2_state_ |= IA2_STATE_EDITABLE;
3431 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED))
3432 ia_state_ |= STATE_SYSTEM_MIXED;
3434 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE))
3435 ia2_state_ |= IA2_STATE_EDITABLE;
3437 if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty())
3438 ia2_state_ |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
3440 base::string16 html_tag = GetString16Attribute(
3441 ui::AX_ATTR_HTML_TAG);
3442 ia_role_ = 0;
3443 ia2_role_ = 0;
3444 switch (GetRole()) {
3445 case ui::AX_ROLE_ALERT:
3446 ia_role_ = ROLE_SYSTEM_ALERT;
3447 break;
3448 case ui::AX_ROLE_ALERT_DIALOG:
3449 ia_role_ = ROLE_SYSTEM_DIALOG;
3450 break;
3451 case ui::AX_ROLE_APPLICATION:
3452 ia_role_ = ROLE_SYSTEM_APPLICATION;
3453 break;
3454 case ui::AX_ROLE_ARTICLE:
3455 ia_role_ = ROLE_SYSTEM_DOCUMENT;
3456 ia_state_ |= STATE_SYSTEM_READONLY;
3457 break;
3458 case ui::AX_ROLE_BANNER:
3459 ia_role_ = ROLE_SYSTEM_GROUPING;
3460 ia2_role_ = IA2_ROLE_HEADER;
3461 break;
3462 case ui::AX_ROLE_BLOCKQUOTE:
3463 role_name_ = html_tag;
3464 ia2_role_ = IA2_ROLE_SECTION;
3465 break;
3466 case ui::AX_ROLE_BUSY_INDICATOR:
3467 ia_role_ = ROLE_SYSTEM_ANIMATION;
3468 ia_state_ |= STATE_SYSTEM_READONLY;
3469 break;
3470 case ui::AX_ROLE_BUTTON:
3471 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3472 break;
3473 case ui::AX_ROLE_CANVAS:
3474 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) {
3475 role_name_ = L"canvas";
3476 ia2_role_ = IA2_ROLE_CANVAS;
3477 } else {
3478 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3480 break;
3481 case ui::AX_ROLE_CAPTION:
3482 ia_role_ = ROLE_SYSTEM_TEXT;
3483 ia2_role_ = IA2_ROLE_CAPTION;
3484 break;
3485 case ui::AX_ROLE_CELL:
3486 ia_role_ = ROLE_SYSTEM_CELL;
3487 break;
3488 case ui::AX_ROLE_CHECK_BOX:
3489 ia_role_ = ROLE_SYSTEM_CHECKBUTTON;
3490 ia2_state_ |= IA2_STATE_CHECKABLE;
3491 break;
3492 case ui::AX_ROLE_COLOR_WELL:
3493 ia_role_ = ROLE_SYSTEM_TEXT;
3494 ia2_role_ = IA2_ROLE_COLOR_CHOOSER;
3495 break;
3496 case ui::AX_ROLE_COLUMN:
3497 ia_role_ = ROLE_SYSTEM_COLUMN;
3498 break;
3499 case ui::AX_ROLE_COLUMN_HEADER:
3500 ia_role_ = ROLE_SYSTEM_COLUMNHEADER;
3501 break;
3502 case ui::AX_ROLE_COMBO_BOX:
3503 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3504 break;
3505 case ui::AX_ROLE_COMPLEMENTARY:
3506 ia_role_ = ROLE_SYSTEM_GROUPING;
3507 ia2_role_ = IA2_ROLE_NOTE;
3508 break;
3509 case ui::AX_ROLE_CONTENT_INFO:
3510 ia_role_ = ROLE_SYSTEM_TEXT;
3511 ia2_role_ = IA2_ROLE_PARAGRAPH;
3512 break;
3513 case ui::AX_ROLE_DATE:
3514 case ui::AX_ROLE_DATE_TIME:
3515 ia_role_ = ROLE_SYSTEM_DROPLIST;
3516 ia2_role_ = IA2_ROLE_DATE_EDITOR;
3517 break;
3518 case ui::AX_ROLE_DIV:
3519 role_name_ = L"div";
3520 ia_role_ = ROLE_SYSTEM_GROUPING;
3521 ia2_role_ = IA2_ROLE_SECTION;
3522 break;
3523 case ui::AX_ROLE_DEFINITION:
3524 role_name_ = html_tag;
3525 ia2_role_ = IA2_ROLE_PARAGRAPH;
3526 ia_state_ |= STATE_SYSTEM_READONLY;
3527 break;
3528 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL:
3529 role_name_ = html_tag;
3530 ia_role_ = ROLE_SYSTEM_TEXT;
3531 ia2_role_ = IA2_ROLE_PARAGRAPH;
3532 break;
3533 case ui::AX_ROLE_DESCRIPTION_LIST:
3534 role_name_ = html_tag;
3535 ia_role_ = ROLE_SYSTEM_LIST;
3536 ia_state_ |= STATE_SYSTEM_READONLY;
3537 break;
3538 case ui::AX_ROLE_DESCRIPTION_LIST_TERM:
3539 ia_role_ = ROLE_SYSTEM_LISTITEM;
3540 ia_state_ |= STATE_SYSTEM_READONLY;
3541 break;
3542 case ui::AX_ROLE_DETAILS:
3543 role_name_ = html_tag;
3544 ia_role_ = ROLE_SYSTEM_GROUPING;
3545 break;
3546 case ui::AX_ROLE_DIALOG:
3547 ia_role_ = ROLE_SYSTEM_DIALOG;
3548 break;
3549 case ui::AX_ROLE_DISCLOSURE_TRIANGLE:
3550 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3551 break;
3552 case ui::AX_ROLE_DOCUMENT:
3553 case ui::AX_ROLE_ROOT_WEB_AREA:
3554 case ui::AX_ROLE_WEB_AREA:
3555 ia_role_ = ROLE_SYSTEM_DOCUMENT;
3556 ia_state_ |= STATE_SYSTEM_READONLY;
3557 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3558 break;
3559 case ui::AX_ROLE_EMBEDDED_OBJECT:
3560 ia_role_ = ROLE_SYSTEM_CLIENT;
3561 ia2_role_ = IA2_ROLE_EMBEDDED_OBJECT;
3562 break;
3563 case ui::AX_ROLE_FIGCAPTION:
3564 role_name_ = html_tag;
3565 ia2_role_ = IA2_ROLE_CAPTION;
3566 break;
3567 case ui::AX_ROLE_FIGURE:
3568 ia_role_ = ROLE_SYSTEM_GROUPING;
3569 break;
3570 case ui::AX_ROLE_FORM:
3571 role_name_ = L"form";
3572 ia2_role_ = IA2_ROLE_FORM;
3573 break;
3574 case ui::AX_ROLE_FOOTER:
3575 ia_role_ = ROLE_SYSTEM_GROUPING;
3576 ia2_role_ = IA2_ROLE_FOOTER;
3577 break;
3578 case ui::AX_ROLE_GRID:
3579 ia_role_ = ROLE_SYSTEM_TABLE;
3580 ia_state_ |= STATE_SYSTEM_READONLY;
3581 break;
3582 case ui::AX_ROLE_GROUP: {
3583 base::string16 aria_role = GetString16Attribute(
3584 ui::AX_ATTR_ROLE);
3585 if (aria_role == L"group" || html_tag == L"fieldset") {
3586 ia_role_ = ROLE_SYSTEM_GROUPING;
3587 } else if (html_tag == L"li") {
3588 ia_role_ = ROLE_SYSTEM_LISTITEM;
3589 ia_state_ |= STATE_SYSTEM_READONLY;
3590 } else {
3591 if (html_tag.empty())
3592 role_name_ = L"div";
3593 else
3594 role_name_ = html_tag;
3595 ia2_role_ = IA2_ROLE_SECTION;
3597 break;
3599 case ui::AX_ROLE_HEADING:
3600 role_name_ = html_tag;
3601 ia2_role_ = IA2_ROLE_HEADING;
3602 break;
3603 case ui::AX_ROLE_IFRAME:
3604 ia_role_ = ROLE_SYSTEM_DOCUMENT;
3605 ia2_role_ = IA2_ROLE_INTERNAL_FRAME;
3606 ia_state_ = STATE_SYSTEM_READONLY;
3607 break;
3608 case ui::AX_ROLE_IFRAME_PRESENTATIONAL:
3609 ia_role_ = ROLE_SYSTEM_GROUPING;
3610 break;
3611 case ui::AX_ROLE_IMAGE:
3612 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3613 ia_state_ |= STATE_SYSTEM_READONLY;
3614 break;
3615 case ui::AX_ROLE_IMAGE_MAP:
3616 role_name_ = html_tag;
3617 ia2_role_ = IA2_ROLE_IMAGE_MAP;
3618 ia_state_ |= STATE_SYSTEM_READONLY;
3619 break;
3620 case ui::AX_ROLE_IMAGE_MAP_LINK:
3621 ia_role_ = ROLE_SYSTEM_LINK;
3622 ia_state_ |= STATE_SYSTEM_LINKED;
3623 ia_state_ |= STATE_SYSTEM_READONLY;
3624 break;
3625 case ui::AX_ROLE_LABEL_TEXT:
3626 case ui::AX_ROLE_LEGEND:
3627 ia_role_ = ROLE_SYSTEM_TEXT;
3628 ia2_role_ = IA2_ROLE_LABEL;
3629 break;
3630 case ui::AX_ROLE_SEARCH:
3631 ia_role_ = ROLE_SYSTEM_GROUPING;
3632 ia2_role_ = IA2_ROLE_SECTION;
3633 break;
3634 case ui::AX_ROLE_LINK:
3635 ia_role_ = ROLE_SYSTEM_LINK;
3636 ia_state_ |= STATE_SYSTEM_LINKED;
3637 break;
3638 case ui::AX_ROLE_LIST:
3639 ia_role_ = ROLE_SYSTEM_LIST;
3640 ia_state_ |= STATE_SYSTEM_READONLY;
3641 break;
3642 case ui::AX_ROLE_LIST_BOX:
3643 ia_role_ = ROLE_SYSTEM_LIST;
3644 break;
3645 case ui::AX_ROLE_LIST_BOX_OPTION:
3646 ia_role_ = ROLE_SYSTEM_LISTITEM;
3647 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3648 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3649 if (HasState(ui::AX_STATE_FOCUSED))
3650 ia_state_ |= STATE_SYSTEM_FOCUSED;
3652 break;
3653 case ui::AX_ROLE_LIST_ITEM:
3654 ia_role_ = ROLE_SYSTEM_LISTITEM;
3655 ia_state_ |= STATE_SYSTEM_READONLY;
3656 break;
3657 case ui::AX_ROLE_MAIN:
3658 ia_role_ = ROLE_SYSTEM_GROUPING;
3659 ia2_role_ = IA2_ROLE_PARAGRAPH;
3660 break;
3661 case ui::AX_ROLE_MARQUEE:
3662 ia_role_ = ROLE_SYSTEM_ANIMATION;
3663 break;
3664 case ui::AX_ROLE_MATH:
3665 ia_role_ = ROLE_SYSTEM_EQUATION;
3666 break;
3667 case ui::AX_ROLE_MENU:
3668 case ui::AX_ROLE_MENU_BUTTON:
3669 ia_role_ = ROLE_SYSTEM_MENUPOPUP;
3670 break;
3671 case ui::AX_ROLE_MENU_BAR:
3672 ia_role_ = ROLE_SYSTEM_MENUBAR;
3673 break;
3674 case ui::AX_ROLE_MENU_ITEM:
3675 ia_role_ = ROLE_SYSTEM_MENUITEM;
3676 break;
3677 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
3678 ia_role_ = ROLE_SYSTEM_MENUITEM;
3679 ia2_role_ = IA2_ROLE_CHECK_MENU_ITEM;
3680 ia2_state_ |= IA2_STATE_CHECKABLE;
3681 break;
3682 case ui::AX_ROLE_MENU_ITEM_RADIO:
3683 ia_role_ = ROLE_SYSTEM_MENUITEM;
3684 ia2_role_ = IA2_ROLE_RADIO_MENU_ITEM;
3685 break;
3686 case ui::AX_ROLE_MENU_LIST_POPUP:
3687 ia_role_ = ROLE_SYSTEM_CLIENT;
3688 break;
3689 case ui::AX_ROLE_MENU_LIST_OPTION:
3690 ia_role_ = ROLE_SYSTEM_LISTITEM;
3691 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
3692 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
3693 if (HasState(ui::AX_STATE_FOCUSED))
3694 ia_state_ |= STATE_SYSTEM_FOCUSED;
3696 break;
3697 case ui::AX_ROLE_METER:
3698 role_name_ = html_tag;
3699 ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3700 break;
3701 case ui::AX_ROLE_NAVIGATION:
3702 ia_role_ = ROLE_SYSTEM_GROUPING;
3703 ia2_role_ = IA2_ROLE_SECTION;
3704 break;
3705 case ui::AX_ROLE_NOTE:
3706 ia_role_ = ROLE_SYSTEM_GROUPING;
3707 ia2_role_ = IA2_ROLE_NOTE;
3708 break;
3709 case ui::AX_ROLE_OUTLINE:
3710 ia_role_ = ROLE_SYSTEM_OUTLINE;
3711 break;
3712 case ui::AX_ROLE_PARAGRAPH:
3713 role_name_ = L"P";
3714 ia2_role_ = IA2_ROLE_PARAGRAPH;
3715 break;
3716 case ui::AX_ROLE_POP_UP_BUTTON:
3717 if (html_tag == L"select") {
3718 ia_role_ = ROLE_SYSTEM_COMBOBOX;
3719 } else {
3720 ia_role_ = ROLE_SYSTEM_BUTTONMENU;
3722 break;
3723 case ui::AX_ROLE_PRE:
3724 role_name_ = html_tag;
3725 ia_role_ = ROLE_SYSTEM_TEXT;
3726 ia2_role_ = IA2_ROLE_PARAGRAPH;
3727 break;
3728 case ui::AX_ROLE_PROGRESS_INDICATOR:
3729 ia_role_ = ROLE_SYSTEM_PROGRESSBAR;
3730 ia_state_ |= STATE_SYSTEM_READONLY;
3731 break;
3732 case ui::AX_ROLE_RADIO_BUTTON:
3733 ia_role_ = ROLE_SYSTEM_RADIOBUTTON;
3734 ia2_state_ = IA2_STATE_CHECKABLE;
3735 break;
3736 case ui::AX_ROLE_RADIO_GROUP:
3737 ia_role_ = ROLE_SYSTEM_GROUPING;
3738 break;
3739 case ui::AX_ROLE_REGION:
3740 if (html_tag == L"section") {
3741 ia_role_ = ROLE_SYSTEM_GROUPING;
3742 ia2_role_ = IA2_ROLE_SECTION;
3743 } else {
3744 ia_role_ = ROLE_SYSTEM_PANE;
3746 break;
3747 case ui::AX_ROLE_ROW:
3748 ia_role_ = ROLE_SYSTEM_ROW;
3749 break;
3750 case ui::AX_ROLE_ROW_HEADER:
3751 ia_role_ = ROLE_SYSTEM_ROWHEADER;
3752 break;
3753 case ui::AX_ROLE_RUBY:
3754 ia_role_ = ROLE_SYSTEM_TEXT;
3755 ia2_role_ = IA2_ROLE_TEXT_FRAME;
3756 break;
3757 case ui::AX_ROLE_RULER:
3758 ia_role_ = ROLE_SYSTEM_CLIENT;
3759 ia2_role_ = IA2_ROLE_RULER;
3760 ia_state_ |= STATE_SYSTEM_READONLY;
3761 break;
3762 case ui::AX_ROLE_SCROLL_AREA:
3763 ia_role_ = ROLE_SYSTEM_CLIENT;
3764 ia2_role_ = IA2_ROLE_SCROLL_PANE;
3765 ia_state_ |= STATE_SYSTEM_READONLY;
3766 ia2_state_ &= ~(IA2_STATE_EDITABLE);
3767 break;
3768 case ui::AX_ROLE_SCROLL_BAR:
3769 ia_role_ = ROLE_SYSTEM_SCROLLBAR;
3770 break;
3771 case ui::AX_ROLE_SLIDER:
3772 ia_role_ = ROLE_SYSTEM_SLIDER;
3773 break;
3774 case ui::AX_ROLE_SPIN_BUTTON:
3775 ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3776 break;
3777 case ui::AX_ROLE_SPIN_BUTTON_PART:
3778 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3779 break;
3780 case ui::AX_ROLE_ANNOTATION:
3781 case ui::AX_ROLE_LIST_MARKER:
3782 case ui::AX_ROLE_STATIC_TEXT:
3783 ia_role_ = ROLE_SYSTEM_STATICTEXT;
3784 break;
3785 case ui::AX_ROLE_STATUS:
3786 ia_role_ = ROLE_SYSTEM_STATUSBAR;
3787 break;
3788 case ui::AX_ROLE_SPLITTER:
3789 ia_role_ = ROLE_SYSTEM_SEPARATOR;
3790 break;
3791 case ui::AX_ROLE_SVG_ROOT:
3792 ia_role_ = ROLE_SYSTEM_GRAPHIC;
3793 break;
3794 case ui::AX_ROLE_TAB:
3795 ia_role_ = ROLE_SYSTEM_PAGETAB;
3796 break;
3797 case ui::AX_ROLE_TABLE: {
3798 base::string16 aria_role = GetString16Attribute(
3799 ui::AX_ATTR_ROLE);
3800 if (aria_role == L"treegrid") {
3801 ia_role_ = ROLE_SYSTEM_OUTLINE;
3802 } else {
3803 ia_role_ = ROLE_SYSTEM_TABLE;
3805 break;
3807 case ui::AX_ROLE_TABLE_HEADER_CONTAINER:
3808 ia_role_ = ROLE_SYSTEM_GROUPING;
3809 ia2_role_ = IA2_ROLE_SECTION;
3810 ia_state_ |= STATE_SYSTEM_READONLY;
3811 break;
3812 case ui::AX_ROLE_TAB_LIST:
3813 ia_role_ = ROLE_SYSTEM_PAGETABLIST;
3814 break;
3815 case ui::AX_ROLE_TAB_PANEL:
3816 ia_role_ = ROLE_SYSTEM_PROPERTYPAGE;
3817 break;
3818 case ui::AX_ROLE_TOGGLE_BUTTON:
3819 ia_role_ = ROLE_SYSTEM_PUSHBUTTON;
3820 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON;
3821 break;
3822 case ui::AX_ROLE_TEXT_AREA:
3823 ia_role_ = ROLE_SYSTEM_TEXT;
3824 ia2_state_ |= IA2_STATE_MULTI_LINE;
3825 ia2_state_ |= IA2_STATE_EDITABLE;
3826 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3827 break;
3828 case ui::AX_ROLE_TEXT_FIELD:
3829 ia_role_ = ROLE_SYSTEM_TEXT;
3830 ia2_state_ |= IA2_STATE_SINGLE_LINE;
3831 ia2_state_ |= IA2_STATE_EDITABLE;
3832 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT;
3833 break;
3834 case ui::AX_ROLE_TIME:
3835 ia_role_ = ROLE_SYSTEM_SPINBUTTON;
3836 break;
3837 case ui::AX_ROLE_TIMER:
3838 ia_role_ = ROLE_SYSTEM_CLOCK;
3839 ia_state_ |= STATE_SYSTEM_READONLY;
3840 break;
3841 case ui::AX_ROLE_TOOLBAR:
3842 ia_role_ = ROLE_SYSTEM_TOOLBAR;
3843 ia_state_ |= STATE_SYSTEM_READONLY;
3844 break;
3845 case ui::AX_ROLE_TOOLTIP:
3846 ia_role_ = ROLE_SYSTEM_TOOLTIP;
3847 ia_state_ |= STATE_SYSTEM_READONLY;
3848 break;
3849 case ui::AX_ROLE_TREE:
3850 ia_role_ = ROLE_SYSTEM_OUTLINE;
3851 break;
3852 case ui::AX_ROLE_TREE_GRID:
3853 ia_role_ = ROLE_SYSTEM_OUTLINE;
3854 break;
3855 case ui::AX_ROLE_TREE_ITEM:
3856 ia_role_ = ROLE_SYSTEM_OUTLINEITEM;
3857 break;
3858 case ui::AX_ROLE_LINE_BREAK:
3859 ia_role_ = ROLE_SYSTEM_WHITESPACE;
3860 break;
3861 case ui::AX_ROLE_WINDOW:
3862 ia_role_ = ROLE_SYSTEM_WINDOW;
3863 break;
3865 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3866 case ui::AX_ROLE_DIRECTORY:
3867 case ui::AX_ROLE_IGNORED:
3868 case ui::AX_ROLE_LOG:
3869 case ui::AX_ROLE_NONE:
3870 case ui::AX_ROLE_PRESENTATIONAL:
3871 case ui::AX_ROLE_SLIDER_THUMB:
3872 default:
3873 ia_role_ = ROLE_SYSTEM_CLIENT;
3874 break;
3877 // Compute the final value of READONLY for MSAA.
3879 // We always set the READONLY state for elements that have the
3880 // aria-readonly attribute and for a few roles (in the switch above).
3881 // We clear the READONLY state on focusable controls and on a document.
3882 // Everything else, the majority of objects, do not have this state set.
3883 if (HasState(ui::AX_STATE_FOCUSABLE) &&
3884 ia_role_ != ROLE_SYSTEM_DOCUMENT) {
3885 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3887 if (!HasState(ui::AX_STATE_READ_ONLY))
3888 ia_state_ &= ~(STATE_SYSTEM_READONLY);
3889 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY))
3890 ia_state_ |= STATE_SYSTEM_READONLY;
3892 // The role should always be set.
3893 DCHECK(!role_name_.empty() || ia_role_);
3895 // If we didn't explicitly set the IAccessible2 role, make it the same
3896 // as the MSAA role.
3897 if (!ia2_role_)
3898 ia2_role_ = ia_role_;
3901 } // namespace content