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"
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";
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
)
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
,
73 // IAccessibleRelation methods not implemented.
74 CONTENT_EXPORT STDMETHODIMP
get_localizedRelationType(BSTR
* relation_type
) {
80 base::win::ScopedComPtr
<BrowserAccessibilityWin
> owner_
;
81 std::vector
<int> target_ids_
;
84 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin
* owner
,
85 const base::string16
& type
) {
90 void BrowserAccessibilityRelation::AddTarget(int target_id
) {
91 target_ids_
.push_back(target_id
);
94 STDMETHODIMP
BrowserAccessibilityRelation::get_relationType(
95 BSTR
* relation_type
) {
99 if (!owner_
->instance_active())
102 *relation_type
= SysAllocString(type_
.c_str());
103 DCHECK(*relation_type
);
107 STDMETHODIMP
BrowserAccessibilityRelation::get_nTargets(long* n_targets
) {
111 if (!owner_
->instance_active())
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()) {
127 STDMETHODIMP
BrowserAccessibilityRelation::get_target(long target_index
,
132 if (!owner_
->instance_active())
135 if (target_index
< 0 ||
136 target_index
>= static_cast<long>(target_ids_
.size())) {
140 BrowserAccessibilityManager
* manager
= owner_
->manager();
141 BrowserAccessibility
* result
=
142 manager
->GetFromID(target_ids_
[target_index
]);
143 if (!result
|| !result
->instance_active())
146 *target
= static_cast<IAccessible
*>(
147 result
->ToBrowserAccessibilityWin()->NewReference());
151 STDMETHODIMP
BrowserAccessibilityRelation::get_targets(long max_targets
,
154 if (!targets
|| !n_targets
)
157 if (!owner_
->instance_active())
160 long count
= static_cast<long>(target_ids_
.size());
161 if (count
> max_targets
)
168 for (long i
= 0; i
< count
; ++i
) {
169 HRESULT result
= get_target(i
, &targets
[i
]);
178 // BrowserAccessibilityWin
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()
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.
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())
233 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
237 manager()->DoDefaultAction(*target
);
241 STDMETHODIMP
BrowserAccessibilityWin::accHitTest(LONG x_left
,
244 if (!instance_active())
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
;
257 BrowserAccessibility
* result
= BrowserAccessibilityForPoint(point
);
258 if (result
== this) {
259 // Point is within this object.
261 child
->lVal
= CHILDID_SELF
;
263 child
->vt
= VT_DISPATCH
;
264 child
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
269 STDMETHODIMP
BrowserAccessibilityWin::accLocation(LONG
* x_left
,
274 if (!instance_active())
277 if (!x_left
|| !y_top
|| !width
|| !height
)
280 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
284 gfx::Rect bounds
= target
->GetGlobalBoundsRect();
285 *x_left
= bounds
.x();
287 *width
= bounds
.width();
288 *height
= bounds
.height();
293 STDMETHODIMP
BrowserAccessibilityWin::accNavigate(LONG nav_dir
,
296 BrowserAccessibilityWin
* target
= GetTargetFromChildID(start
);
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.
306 uint32 child_count
= target
->PlatformChildCount();
308 BrowserAccessibility
* result
= NULL
;
314 // These directions are not implemented, matching Mozilla and IE.
316 case NAVDIR_FIRSTCHILD
:
318 result
= target
->PlatformGetChild(0);
320 case NAVDIR_LASTCHILD
:
322 result
= target
->PlatformGetChild(child_count
- 1);
325 result
= target
->GetNextSibling();
327 case NAVDIR_PREVIOUS
:
328 result
= target
->GetPreviousSibling();
337 end
->vt
= VT_DISPATCH
;
338 end
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
342 STDMETHODIMP
BrowserAccessibilityWin::get_accChild(VARIANT var_child
,
343 IDispatch
** disp_child
) {
344 if (!instance_active())
352 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_child
);
356 (*disp_child
) = target
->NewReference();
360 STDMETHODIMP
BrowserAccessibilityWin::get_accChildCount(LONG
* child_count
) {
361 if (!instance_active())
367 *child_count
= PlatformChildCount();
372 STDMETHODIMP
BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id
,
374 if (!instance_active())
380 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
384 return target
->GetStringAttributeAsBstr(
385 ui::AX_ATTR_SHORTCUT
, def_action
);
388 STDMETHODIMP
BrowserAccessibilityWin::get_accDescription(VARIANT var_id
,
390 if (!instance_active())
396 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
400 return target
->GetStringAttributeAsBstr(
401 ui::AX_ATTR_DESCRIPTION
, desc
);
404 STDMETHODIMP
BrowserAccessibilityWin::get_accFocus(VARIANT
* focus_child
) {
405 if (!instance_active())
411 BrowserAccessibilityWin
* focus
= static_cast<BrowserAccessibilityWin
*>(
412 manager()->GetFocus(this));
414 focus_child
->vt
= VT_I4
;
415 focus_child
->lVal
= CHILDID_SELF
;
416 } else if (focus
== NULL
) {
417 focus_child
->vt
= VT_EMPTY
;
419 focus_child
->vt
= VT_DISPATCH
;
420 focus_child
->pdispVal
= focus
->NewReference();
426 STDMETHODIMP
BrowserAccessibilityWin::get_accHelp(VARIANT var_id
, BSTR
* help
) {
427 if (!instance_active())
433 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
437 return target
->GetStringAttributeAsBstr(
438 ui::AX_ATTR_HELP
, help
);
441 STDMETHODIMP
BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id
,
443 if (!instance_active())
449 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
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())
464 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
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()) {
473 if (target
->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT
,
475 BrowserAccessibility
* title_elem
=
476 manager()->GetFromID(title_elem_id
);
478 name_str
= title_elem
->GetTextRecursive();
482 if (name_str
.empty())
485 *name
= SysAllocString(base::UTF8ToUTF16(name_str
).c_str());
491 STDMETHODIMP
BrowserAccessibilityWin::get_accParent(IDispatch
** disp_parent
) {
492 if (!instance_active())
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.
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.
510 DVLOG(1) << "In Function: "
512 << ". Parent IAccessible interface is NULL. Returning failure";
516 parent_obj
->AddRef();
517 *disp_parent
= parent_obj
;
521 STDMETHODIMP
BrowserAccessibilityWin::get_accRole(VARIANT var_id
,
523 if (!instance_active())
529 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
533 if (!target
->role_name_
.empty()) {
535 role
->bstrVal
= SysAllocString(target
->role_name_
.c_str());
538 role
->lVal
= target
->ia_role_
;
543 STDMETHODIMP
BrowserAccessibilityWin::get_accState(VARIANT var_id
,
545 if (!instance_active())
551 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
556 state
->lVal
= target
->ia_state_
;
557 if (manager()->GetFocus(NULL
) == this)
558 state
->lVal
|= STATE_SYSTEM_FOCUSED
;
563 STDMETHODIMP
BrowserAccessibilityWin::get_accValue(VARIANT var_id
,
565 if (!instance_active())
571 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
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());
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());
601 *value
= SysAllocString(base::UTF8ToUTF16(target
->value()).c_str());
606 STDMETHODIMP
BrowserAccessibilityWin::get_accHelpTopic(BSTR
* help_file
,
612 STDMETHODIMP
BrowserAccessibilityWin::get_accSelection(VARIANT
* selected
) {
613 if (!instance_active())
616 if (GetRole() != ui::AX_ROLE_LIST_BOX
)
619 unsigned long selected_count
= 0;
620 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
621 if (InternalGetChild(i
)->HasState(ui::AX_STATE_SELECTED
))
625 if (selected_count
== 0) {
626 selected
->vt
= VT_EMPTY
;
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
;
635 InternalGetChild(i
)->ToBrowserAccessibilityWin()->NewReference();
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();
654 selected
->vt
= VT_UNKNOWN
;
655 selected
->punkVal
= static_cast<IUnknown
*>(
656 static_cast<base::win::IUnknownImpl
*>(enum_variant
));
660 STDMETHODIMP
BrowserAccessibilityWin::accSelect(
661 LONG flags_sel
, VARIANT var_id
) {
662 if (!instance_active())
665 if (flags_sel
& SELFLAG_TAKEFOCUS
) {
666 manager()->SetFocus(this, true);
674 // IAccessible2 methods.
677 STDMETHODIMP
BrowserAccessibilityWin::role(LONG
* role
) {
678 if (!instance_active())
689 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(BSTR
* attributes
) {
690 if (!instance_active())
696 // The iaccessible2 attributes are a set of key-value pairs
697 // separated by semicolons, with a colon between the key and the value.
699 for (unsigned int i
= 0; i
< ia2_attributes_
.size(); ++i
) {
702 str
+= ia2_attributes_
[i
];
708 *attributes
= SysAllocString(str
.c_str());
713 STDMETHODIMP
BrowserAccessibilityWin::get_states(AccessibleStates
* states
) {
714 if (!instance_active())
720 *states
= ia2_state_
;
725 STDMETHODIMP
BrowserAccessibilityWin::get_uniqueID(LONG
* unique_id
) {
726 if (!instance_active())
732 *unique_id
= unique_id_win_
;
736 STDMETHODIMP
BrowserAccessibilityWin::get_windowHandle(HWND
* window_handle
) {
737 if (!instance_active())
744 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
751 STDMETHODIMP
BrowserAccessibilityWin::get_indexInParent(LONG
* index_in_parent
) {
752 if (!instance_active())
755 if (!index_in_parent
)
758 *index_in_parent
= this->GetIndexInParent();
762 STDMETHODIMP
BrowserAccessibilityWin::get_nRelations(LONG
* n_relations
) {
763 if (!instance_active())
769 *n_relations
= relations_
.size();
773 STDMETHODIMP
BrowserAccessibilityWin::get_relation(
775 IAccessibleRelation
** relation
) {
776 if (!instance_active())
779 if (relation_index
< 0 ||
780 relation_index
>= static_cast<long>(relations_
.size())) {
787 relations_
[relation_index
]->AddRef();
788 *relation
= relations_
[relation_index
];
792 STDMETHODIMP
BrowserAccessibilityWin::get_relations(
794 IAccessibleRelation
** relations
,
796 if (!instance_active())
799 if (!relations
|| !n_relations
)
802 long count
= static_cast<long>(relations_
.size());
803 *n_relations
= count
;
807 for (long i
= 0; i
< count
; ++i
) {
808 relations_
[i
]->AddRef();
809 relations
[i
] = relations_
[i
];
815 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type
) {
816 if (!instance_active())
819 gfx::Rect r
= GetLocation();
820 switch(scroll_type
) {
821 case IA2_SCROLL_TYPE_TOP_LEFT
:
822 manager()->ScrollToMakeVisible(*this, gfx::Rect(r
.x(), r
.y(), 0, 0));
824 case IA2_SCROLL_TYPE_BOTTOM_RIGHT
:
825 manager()->ScrollToMakeVisible(
826 *this, gfx::Rect(r
.right(), r
.bottom(), 0, 0));
828 case IA2_SCROLL_TYPE_TOP_EDGE
:
829 manager()->ScrollToMakeVisible(
830 *this, gfx::Rect(r
.x(), r
.y(), r
.width(), 0));
832 case IA2_SCROLL_TYPE_BOTTOM_EDGE
:
833 manager()->ScrollToMakeVisible(
834 *this, gfx::Rect(r
.x(), r
.bottom(), r
.width(), 0));
836 case IA2_SCROLL_TYPE_LEFT_EDGE
:
837 manager()->ScrollToMakeVisible(
838 *this, gfx::Rect(r
.x(), r
.y(), 0, r
.height()));
840 case IA2_SCROLL_TYPE_RIGHT_EDGE
:
841 manager()->ScrollToMakeVisible(
842 *this, gfx::Rect(r
.right(), r
.y(), 0, r
.height()));
844 case IA2_SCROLL_TYPE_ANYWHERE
:
846 manager()->ScrollToMakeVisible(*this, r
);
850 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
855 STDMETHODIMP
BrowserAccessibilityWin::scrollToPoint(
856 enum IA2CoordinateType coordinate_type
,
859 if (!instance_active())
862 gfx::Point
scroll_to(x
, y
);
864 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
865 scroll_to
-= manager()->GetViewBounds().OffsetFromOrigin();
866 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
868 scroll_to
+= GetParent()->GetLocation().OffsetFromOrigin();
873 manager()->ScrollToPoint(*this, scroll_to
);
874 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
879 STDMETHODIMP
BrowserAccessibilityWin::get_groupPosition(
881 LONG
* similar_items_in_group
,
882 LONG
* position_in_group
) {
883 if (!instance_active())
886 if (!group_level
|| !similar_items_in_group
|| !position_in_group
)
889 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
891 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX
) {
893 *similar_items_in_group
= GetParent()->PlatformChildCount();
894 *position_in_group
= GetIndexInParent() + 1;
902 // IAccessibleApplication methods.
905 STDMETHODIMP
BrowserAccessibilityWin::get_appName(BSTR
* app_name
) {
906 // No need to check |instance_active()| because this interface is
907 // global, and doesn't depend on any local state.
912 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
913 // the part before the "/".
914 std::vector
<std::string
> product_components
;
915 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
916 DCHECK_EQ(2U, product_components
.size());
917 if (product_components
.size() != 2)
919 *app_name
= SysAllocString(base::UTF8ToUTF16(product_components
[0]).c_str());
921 return *app_name
? S_OK
: E_FAIL
;
924 STDMETHODIMP
BrowserAccessibilityWin::get_appVersion(BSTR
* app_version
) {
925 // No need to check |instance_active()| because this interface is
926 // global, and doesn't depend on any local state.
931 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
932 // the part after the "/".
933 std::vector
<std::string
> product_components
;
934 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
935 DCHECK_EQ(2U, product_components
.size());
936 if (product_components
.size() != 2)
939 SysAllocString(base::UTF8ToUTF16(product_components
[1]).c_str());
940 DCHECK(*app_version
);
941 return *app_version
? S_OK
: E_FAIL
;
944 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitName(BSTR
* toolkit_name
) {
945 // No need to check |instance_active()| because this interface is
946 // global, and doesn't depend on any local state.
951 // This is hard-coded; all products based on the Chromium engine
952 // will have the same toolkit name, so that assistive technology can
953 // detect any Chrome-based product.
954 *toolkit_name
= SysAllocString(L
"Chrome");
955 DCHECK(*toolkit_name
);
956 return *toolkit_name
? S_OK
: E_FAIL
;
959 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitVersion(
960 BSTR
* toolkit_version
) {
961 // No need to check |instance_active()| because this interface is
962 // global, and doesn't depend on any local state.
964 if (!toolkit_version
)
967 std::string user_agent
= GetContentClient()->GetUserAgent();
968 *toolkit_version
= SysAllocString(base::UTF8ToUTF16(user_agent
).c_str());
969 DCHECK(*toolkit_version
);
970 return *toolkit_version
? S_OK
: E_FAIL
;
974 // IAccessibleImage methods.
977 STDMETHODIMP
BrowserAccessibilityWin::get_description(BSTR
* desc
) {
978 if (!instance_active())
984 return GetStringAttributeAsBstr(
985 ui::AX_ATTR_DESCRIPTION
, desc
);
988 STDMETHODIMP
BrowserAccessibilityWin::get_imagePosition(
989 enum IA2CoordinateType coordinate_type
,
992 if (!instance_active())
998 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
1000 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
1003 POINT top_left
= {0, 0};
1004 ::ClientToScreen(parent_hwnd
, &top_left
);
1005 *x
= GetLocation().x() + top_left
.x
;
1006 *y
= GetLocation().y() + top_left
.y
;
1007 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
1008 *x
= GetLocation().x();
1009 *y
= GetLocation().y();
1011 *x
-= GetParent()->GetLocation().x();
1012 *y
-= GetParent()->GetLocation().y();
1015 return E_INVALIDARG
;
1021 STDMETHODIMP
BrowserAccessibilityWin::get_imageSize(LONG
* height
, LONG
* width
) {
1022 if (!instance_active())
1025 if (!height
|| !width
)
1026 return E_INVALIDARG
;
1028 *height
= GetLocation().height();
1029 *width
= GetLocation().width();
1034 // IAccessibleTable methods.
1037 STDMETHODIMP
BrowserAccessibilityWin::get_accessibleAt(
1040 IUnknown
** accessible
) {
1041 if (!instance_active())
1045 return E_INVALIDARG
;
1049 if (!GetIntAttribute(
1050 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1052 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1058 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1059 return E_INVALIDARG
;
1061 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1062 ui::AX_ATTR_CELL_IDS
);
1063 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1065 int cell_id
= cell_ids
[row
* columns
+ column
];
1066 BrowserAccessibilityWin
* cell
= GetFromID(cell_id
);
1068 *accessible
= static_cast<IAccessible
*>(cell
->NewReference());
1073 return E_INVALIDARG
;
1076 STDMETHODIMP
BrowserAccessibilityWin::get_caption(IUnknown
** accessible
) {
1077 if (!instance_active())
1081 return E_INVALIDARG
;
1083 // TODO(dmazzoni): implement
1087 STDMETHODIMP
BrowserAccessibilityWin::get_childIndex(long row
,
1090 if (!instance_active())
1094 return E_INVALIDARG
;
1098 if (!GetIntAttribute(
1099 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1101 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1107 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1108 return E_INVALIDARG
;
1110 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1111 ui::AX_ATTR_CELL_IDS
);
1112 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1113 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1114 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1115 int cell_id
= cell_ids
[row
* columns
+ column
];
1116 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
1117 if (unique_cell_ids
[i
] == cell_id
) {
1118 *cell_index
= (long)i
;
1126 STDMETHODIMP
BrowserAccessibilityWin::get_columnDescription(long column
,
1127 BSTR
* description
) {
1128 if (!instance_active())
1132 return E_INVALIDARG
;
1136 if (!GetIntAttribute(
1137 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1138 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1144 if (column
< 0 || column
>= columns
)
1145 return E_INVALIDARG
;
1147 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1148 ui::AX_ATTR_CELL_IDS
);
1149 for (int i
= 0; i
< rows
; ++i
) {
1150 int cell_id
= cell_ids
[i
* columns
+ column
];
1151 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1152 manager()->GetFromID(cell_id
));
1153 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
) {
1154 base::string16 cell_name
= cell
->GetString16Attribute(
1156 if (cell_name
.size() > 0) {
1157 *description
= SysAllocString(cell_name
.c_str());
1161 return cell
->GetStringAttributeAsBstr(
1162 ui::AX_ATTR_DESCRIPTION
, description
);
1169 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtentAt(
1172 long* n_columns_spanned
) {
1173 if (!instance_active())
1176 if (!n_columns_spanned
)
1177 return E_INVALIDARG
;
1181 if (!GetIntAttribute(
1182 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1183 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1189 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1190 return E_INVALIDARG
;
1192 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1193 ui::AX_ATTR_CELL_IDS
);
1194 int cell_id
= cell_ids
[row
* columns
+ column
];
1195 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1196 manager()->GetFromID(cell_id
));
1199 cell
->GetIntAttribute(
1200 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1202 *n_columns_spanned
= colspan
;
1209 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeader(
1210 IAccessibleTable
** accessible_table
,
1211 long* starting_row_index
) {
1212 // TODO(dmazzoni): implement
1216 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long cell_index
,
1217 long* column_index
) {
1218 if (!instance_active())
1222 return E_INVALIDARG
;
1224 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1225 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1226 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1228 return E_INVALIDARG
;
1229 if (cell_index
>= cell_id_count
)
1232 int cell_id
= unique_cell_ids
[cell_index
];
1233 BrowserAccessibilityWin
* cell
=
1234 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1237 cell
->GetIntAttribute(
1238 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &col_index
)) {
1239 *column_index
= col_index
;
1246 STDMETHODIMP
BrowserAccessibilityWin::get_nColumns(long* column_count
) {
1247 if (!instance_active())
1251 return E_INVALIDARG
;
1254 if (GetIntAttribute(
1255 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
)) {
1256 *column_count
= columns
;
1263 STDMETHODIMP
BrowserAccessibilityWin::get_nRows(long* row_count
) {
1264 if (!instance_active())
1268 return E_INVALIDARG
;
1271 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1279 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count
) {
1280 if (!instance_active())
1284 return E_INVALIDARG
;
1286 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1291 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedColumns(long* column_count
) {
1292 if (!instance_active())
1296 return E_INVALIDARG
;
1302 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedRows(long* row_count
) {
1303 if (!instance_active())
1307 return E_INVALIDARG
;
1313 STDMETHODIMP
BrowserAccessibilityWin::get_rowDescription(long row
,
1314 BSTR
* description
) {
1315 if (!instance_active())
1319 return E_INVALIDARG
;
1323 if (!GetIntAttribute(
1324 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1325 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1331 if (row
< 0 || row
>= rows
)
1332 return E_INVALIDARG
;
1334 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1335 ui::AX_ATTR_CELL_IDS
);
1336 for (int i
= 0; i
< columns
; ++i
) {
1337 int cell_id
= cell_ids
[row
* columns
+ i
];
1338 BrowserAccessibilityWin
* cell
=
1339 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1340 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
) {
1341 base::string16 cell_name
= cell
->GetString16Attribute(
1343 if (cell_name
.size() > 0) {
1344 *description
= SysAllocString(cell_name
.c_str());
1348 return cell
->GetStringAttributeAsBstr(
1349 ui::AX_ATTR_DESCRIPTION
, description
);
1356 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtentAt(long row
,
1358 long* n_rows_spanned
) {
1359 if (!instance_active())
1362 if (!n_rows_spanned
)
1363 return E_INVALIDARG
;
1367 if (!GetIntAttribute(
1368 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1369 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1375 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1376 return E_INVALIDARG
;
1378 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1379 ui::AX_ATTR_CELL_IDS
);
1380 int cell_id
= cell_ids
[row
* columns
+ column
];
1381 BrowserAccessibilityWin
* cell
=
1382 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1385 cell
->GetIntAttribute(
1386 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1388 *n_rows_spanned
= rowspan
;
1395 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeader(
1396 IAccessibleTable
** accessible_table
,
1397 long* starting_column_index
) {
1398 // TODO(dmazzoni): implement
1402 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long cell_index
,
1404 if (!instance_active())
1408 return E_INVALIDARG
;
1410 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1411 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1412 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1414 return E_INVALIDARG
;
1415 if (cell_index
>= cell_id_count
)
1418 int cell_id
= unique_cell_ids
[cell_index
];
1419 BrowserAccessibilityWin
* cell
=
1420 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1423 cell
->GetIntAttribute(
1424 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &cell_row_index
)) {
1425 *row_index
= cell_row_index
;
1432 STDMETHODIMP
BrowserAccessibilityWin::get_selectedChildren(long max_children
,
1435 if (!instance_active())
1438 if (!children
|| !n_children
)
1439 return E_INVALIDARG
;
1441 // TODO(dmazzoni): Implement this.
1446 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long max_columns
,
1449 if (!instance_active())
1452 if (!columns
|| !n_columns
)
1453 return E_INVALIDARG
;
1455 // TODO(dmazzoni): Implement this.
1460 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long max_rows
,
1463 if (!instance_active())
1466 if (!rows
|| !n_rows
)
1467 return E_INVALIDARG
;
1469 // TODO(dmazzoni): Implement this.
1474 STDMETHODIMP
BrowserAccessibilityWin::get_summary(IUnknown
** accessible
) {
1475 if (!instance_active())
1479 return E_INVALIDARG
;
1481 // TODO(dmazzoni): implement
1485 STDMETHODIMP
BrowserAccessibilityWin::get_isColumnSelected(
1487 boolean
* is_selected
) {
1488 if (!instance_active())
1492 return E_INVALIDARG
;
1494 // TODO(dmazzoni): Implement this.
1495 *is_selected
= false;
1499 STDMETHODIMP
BrowserAccessibilityWin::get_isRowSelected(long row
,
1500 boolean
* is_selected
) {
1501 if (!instance_active())
1505 return E_INVALIDARG
;
1507 // TODO(dmazzoni): Implement this.
1508 *is_selected
= false;
1512 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(long row
,
1514 boolean
* is_selected
) {
1515 if (!instance_active())
1519 return E_INVALIDARG
;
1521 // TODO(dmazzoni): Implement this.
1522 *is_selected
= false;
1526 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1531 long* column_extents
,
1532 boolean
* is_selected
) {
1533 if (!instance_active())
1536 if (!row
|| !column
|| !row_extents
|| !column_extents
|| !is_selected
)
1537 return E_INVALIDARG
;
1539 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1540 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1541 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1543 return E_INVALIDARG
;
1544 if (index
>= cell_id_count
)
1547 int cell_id
= unique_cell_ids
[index
];
1548 BrowserAccessibilityWin
* cell
=
1549 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1553 cell
->GetIntAttribute(
1554 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1555 cell
->GetIntAttribute(
1556 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1559 *row_extents
= rowspan
;
1560 *column_extents
= colspan
;
1568 // IAccessibleTable2 methods.
1571 STDMETHODIMP
BrowserAccessibilityWin::get_cellAt(long row
,
1574 return get_accessibleAt(row
, column
, cell
);
1577 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedCells(long* cell_count
) {
1578 return get_nSelectedChildren(cell_count
);
1581 STDMETHODIMP
BrowserAccessibilityWin::get_selectedCells(
1583 long* n_selected_cells
) {
1584 if (!instance_active())
1587 if (!cells
|| !n_selected_cells
)
1588 return E_INVALIDARG
;
1590 // TODO(dmazzoni): Implement this.
1591 *n_selected_cells
= 0;
1595 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long** columns
,
1597 if (!instance_active())
1600 if (!columns
|| !n_columns
)
1601 return E_INVALIDARG
;
1603 // TODO(dmazzoni): Implement this.
1608 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long** rows
,
1610 if (!instance_active())
1613 if (!rows
|| !n_rows
)
1614 return E_INVALIDARG
;
1616 // TODO(dmazzoni): Implement this.
1623 // IAccessibleTableCell methods.
1626 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtent(
1627 long* n_columns_spanned
) {
1628 if (!instance_active())
1631 if (!n_columns_spanned
)
1632 return E_INVALIDARG
;
1635 if (GetIntAttribute(
1636 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1638 *n_columns_spanned
= colspan
;
1645 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeaderCells(
1646 IUnknown
*** cell_accessibles
,
1647 long* n_column_header_cells
) {
1648 if (!instance_active())
1651 if (!cell_accessibles
|| !n_column_header_cells
)
1652 return E_INVALIDARG
;
1654 *n_column_header_cells
= 0;
1657 if (!GetIntAttribute(
1658 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1662 BrowserAccessibility
* table
= GetParent();
1663 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
1664 table
= table
->GetParent();
1672 if (!table
->GetIntAttribute(
1673 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1674 !table
->GetIntAttribute(
1675 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1678 if (columns
<= 0 || rows
<= 0 || column
< 0 || column
>= columns
)
1681 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1682 ui::AX_ATTR_CELL_IDS
);
1684 for (int i
= 0; i
< rows
; ++i
) {
1685 int cell_id
= cell_ids
[i
* columns
+ column
];
1686 BrowserAccessibilityWin
* cell
=
1687 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1688 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
)
1689 (*n_column_header_cells
)++;
1692 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1693 (*n_column_header_cells
) * sizeof(cell_accessibles
[0])));
1695 for (int i
= 0; i
< rows
; ++i
) {
1696 int cell_id
= cell_ids
[i
* columns
+ column
];
1697 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1698 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
) {
1699 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1700 cell
->ToBrowserAccessibilityWin()->NewReference());
1708 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long* column_index
) {
1709 if (!instance_active())
1713 return E_INVALIDARG
;
1716 if (GetIntAttribute(
1717 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1718 *column_index
= column
;
1725 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned
) {
1726 if (!instance_active())
1729 if (!n_rows_spanned
)
1730 return E_INVALIDARG
;
1733 if (GetIntAttribute(
1734 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1736 *n_rows_spanned
= rowspan
;
1743 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeaderCells(
1744 IUnknown
*** cell_accessibles
,
1745 long* n_row_header_cells
) {
1746 if (!instance_active())
1749 if (!cell_accessibles
|| !n_row_header_cells
)
1750 return E_INVALIDARG
;
1752 *n_row_header_cells
= 0;
1755 if (!GetIntAttribute(
1756 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1760 BrowserAccessibility
* table
= GetParent();
1761 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
1762 table
= table
->GetParent();
1770 if (!table
->GetIntAttribute(
1771 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1772 !table
->GetIntAttribute(
1773 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1776 if (columns
<= 0 || rows
<= 0 || row
< 0 || row
>= rows
)
1779 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1780 ui::AX_ATTR_CELL_IDS
);
1782 for (int i
= 0; i
< columns
; ++i
) {
1783 int cell_id
= cell_ids
[row
* columns
+ i
];
1784 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1785 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
)
1786 (*n_row_header_cells
)++;
1789 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1790 (*n_row_header_cells
) * sizeof(cell_accessibles
[0])));
1792 for (int i
= 0; i
< columns
; ++i
) {
1793 int cell_id
= cell_ids
[row
* columns
+ i
];
1794 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1795 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
) {
1796 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1797 cell
->ToBrowserAccessibilityWin()->NewReference());
1805 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long* row_index
) {
1806 if (!instance_active())
1810 return E_INVALIDARG
;
1813 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1820 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(boolean
* is_selected
) {
1821 if (!instance_active())
1825 return E_INVALIDARG
;
1827 *is_selected
= false;
1831 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtents(
1835 long* column_extents
,
1836 boolean
* is_selected
) {
1837 if (!instance_active())
1845 return E_INVALIDARG
;
1852 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
) &&
1854 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
) &&
1856 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1858 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
)) {
1860 *column_index
= column
;
1861 *row_extents
= rowspan
;
1862 *column_extents
= colspan
;
1863 *is_selected
= false;
1870 STDMETHODIMP
BrowserAccessibilityWin::get_table(IUnknown
** table
) {
1871 if (!instance_active())
1875 return E_INVALIDARG
;
1880 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
);
1881 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
);
1883 BrowserAccessibility
* find_table
= GetParent();
1884 while (find_table
&& find_table
->GetRole() != ui::AX_ROLE_TABLE
)
1885 find_table
= find_table
->GetParent();
1891 *table
= static_cast<IAccessibleTable
*>(
1892 find_table
->ToBrowserAccessibilityWin()->NewReference());
1898 // IAccessibleText methods.
1901 STDMETHODIMP
BrowserAccessibilityWin::get_nCharacters(LONG
* n_characters
) {
1902 if (!instance_active())
1906 return E_INVALIDARG
;
1908 *n_characters
= TextForIAccessibleText().length();
1912 STDMETHODIMP
BrowserAccessibilityWin::get_caretOffset(LONG
* offset
) {
1913 if (!instance_active())
1917 return E_INVALIDARG
;
1920 if (GetRole() == ui::AX_ROLE_TEXT_FIELD
||
1921 GetRole() == ui::AX_ROLE_TEXT_AREA
) {
1923 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
1925 *offset
= sel_start
;
1931 STDMETHODIMP
BrowserAccessibilityWin::get_characterExtents(
1933 enum IA2CoordinateType coordinate_type
,
1938 if (!instance_active())
1941 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
1942 return E_INVALIDARG
;
1944 const base::string16
& text_str
= TextForIAccessibleText();
1945 HandleSpecialTextOffset(text_str
, &offset
);
1947 if (offset
< 0 || offset
> static_cast<LONG
>(text_str
.size()))
1948 return E_INVALIDARG
;
1950 gfx::Rect character_bounds
;
1951 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
1952 character_bounds
= GetGlobalBoundsForRange(offset
, 1);
1953 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
1954 character_bounds
= GetLocalBoundsForRange(offset
, 1);
1955 character_bounds
-= GetLocation().OffsetFromOrigin();
1957 return E_INVALIDARG
;
1960 *out_x
= character_bounds
.x();
1961 *out_y
= character_bounds
.y();
1962 *out_width
= character_bounds
.width();
1963 *out_height
= character_bounds
.height();
1968 STDMETHODIMP
BrowserAccessibilityWin::get_nSelections(LONG
* n_selections
) {
1969 if (!instance_active())
1973 return E_INVALIDARG
;
1976 if (GetRole() == ui::AX_ROLE_TEXT_FIELD
||
1977 GetRole() == ui::AX_ROLE_TEXT_AREA
) {
1980 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
1982 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
) &&
1983 sel_start
!= sel_end
)
1990 STDMETHODIMP
BrowserAccessibilityWin::get_selection(LONG selection_index
,
1993 if (!instance_active())
1996 if (!start_offset
|| !end_offset
|| selection_index
!= 0)
1997 return E_INVALIDARG
;
2001 if (GetRole() == ui::AX_ROLE_TEXT_FIELD
||
2002 GetRole() == ui::AX_ROLE_TEXT_AREA
) {
2005 if (GetIntAttribute(
2006 ui::AX_ATTR_TEXT_SEL_START
, &sel_start
) &&
2007 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
)) {
2008 *start_offset
= sel_start
;
2009 *end_offset
= sel_end
;
2016 STDMETHODIMP
BrowserAccessibilityWin::get_text(LONG start_offset
,
2019 if (!instance_active())
2023 return E_INVALIDARG
;
2025 const base::string16
& text_str
= TextForIAccessibleText();
2027 // Handle special text offsets.
2028 HandleSpecialTextOffset(text_str
, &start_offset
);
2029 HandleSpecialTextOffset(text_str
, &end_offset
);
2031 // The spec allows the arguments to be reversed.
2032 if (start_offset
> end_offset
) {
2033 LONG tmp
= start_offset
;
2034 start_offset
= end_offset
;
2038 // The spec does not allow the start or end offsets to be out or range;
2039 // we must return an error if so.
2040 LONG len
= text_str
.length();
2041 if (start_offset
< 0)
2042 return E_INVALIDARG
;
2043 if (end_offset
> len
)
2044 return E_INVALIDARG
;
2046 base::string16 substr
= text_str
.substr(start_offset
,
2047 end_offset
- start_offset
);
2051 *text
= SysAllocString(substr
.c_str());
2056 STDMETHODIMP
BrowserAccessibilityWin::get_textAtOffset(
2058 enum IA2TextBoundaryType boundary_type
,
2062 if (!instance_active())
2065 if (!start_offset
|| !end_offset
|| !text
)
2066 return E_INVALIDARG
;
2068 // The IAccessible2 spec says we don't have to implement the "sentence"
2069 // boundary type, we can just let the screenreader handle it.
2070 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2077 const base::string16
& text_str
= TextForIAccessibleText();
2079 *start_offset
= FindBoundary(
2080 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2081 *end_offset
= FindBoundary(
2082 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2083 return get_text(*start_offset
, *end_offset
, text
);
2086 STDMETHODIMP
BrowserAccessibilityWin::get_textBeforeOffset(
2088 enum IA2TextBoundaryType boundary_type
,
2092 if (!instance_active())
2095 if (!start_offset
|| !end_offset
|| !text
)
2096 return E_INVALIDARG
;
2098 // The IAccessible2 spec says we don't have to implement the "sentence"
2099 // boundary type, we can just let the screenreader handle it.
2100 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2107 const base::string16
& text_str
= TextForIAccessibleText();
2109 *start_offset
= FindBoundary(
2110 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2111 *end_offset
= offset
;
2112 return get_text(*start_offset
, *end_offset
, text
);
2115 STDMETHODIMP
BrowserAccessibilityWin::get_textAfterOffset(
2117 enum IA2TextBoundaryType boundary_type
,
2121 if (!instance_active())
2124 if (!start_offset
|| !end_offset
|| !text
)
2125 return E_INVALIDARG
;
2127 // The IAccessible2 spec says we don't have to implement the "sentence"
2128 // boundary type, we can just let the screenreader handle it.
2129 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2136 const base::string16
& text_str
= TextForIAccessibleText();
2138 *start_offset
= offset
;
2139 *end_offset
= FindBoundary(
2140 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2141 return get_text(*start_offset
, *end_offset
, text
);
2144 STDMETHODIMP
BrowserAccessibilityWin::get_newText(IA2TextSegment
* new_text
) {
2145 if (!instance_active())
2149 return E_INVALIDARG
;
2151 base::string16 text
= TextForIAccessibleText();
2153 new_text
->text
= SysAllocString(text
.c_str());
2154 new_text
->start
= 0;
2155 new_text
->end
= static_cast<long>(text
.size());
2159 STDMETHODIMP
BrowserAccessibilityWin::get_oldText(IA2TextSegment
* old_text
) {
2160 if (!instance_active())
2164 return E_INVALIDARG
;
2166 old_text
->text
= SysAllocString(old_text_
.c_str());
2167 old_text
->start
= 0;
2168 old_text
->end
= static_cast<long>(old_text_
.size());
2172 STDMETHODIMP
BrowserAccessibilityWin::get_offsetAtPoint(
2175 enum IA2CoordinateType coord_type
,
2177 if (!instance_active())
2181 return E_INVALIDARG
;
2183 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2184 // screen readers still return partially accurate results rather than
2185 // completely failing.
2190 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringTo(
2193 enum IA2ScrollType scroll_type
) {
2194 // TODO(dmazzoni): adjust this for the start and end index, too.
2195 return scrollTo(scroll_type
);
2198 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringToPoint(
2201 enum IA2CoordinateType coordinate_type
,
2203 // TODO(dmazzoni): adjust this for the start and end index, too.
2204 return scrollToPoint(coordinate_type
, x
, y
);
2207 STDMETHODIMP
BrowserAccessibilityWin::addSelection(LONG start_offset
,
2209 if (!instance_active())
2212 const base::string16
& text_str
= TextForIAccessibleText();
2213 HandleSpecialTextOffset(text_str
, &start_offset
);
2214 HandleSpecialTextOffset(text_str
, &end_offset
);
2216 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2220 STDMETHODIMP
BrowserAccessibilityWin::removeSelection(LONG selection_index
) {
2221 if (!instance_active())
2224 if (selection_index
!= 0)
2225 return E_INVALIDARG
;
2227 manager()->SetTextSelection(*this, 0, 0);
2231 STDMETHODIMP
BrowserAccessibilityWin::setCaretOffset(LONG offset
) {
2232 if (!instance_active())
2235 const base::string16
& text_str
= TextForIAccessibleText();
2236 HandleSpecialTextOffset(text_str
, &offset
);
2237 manager()->SetTextSelection(*this, offset
, offset
);
2241 STDMETHODIMP
BrowserAccessibilityWin::setSelection(LONG selection_index
,
2244 if (!instance_active())
2247 if (selection_index
!= 0)
2248 return E_INVALIDARG
;
2250 const base::string16
& text_str
= TextForIAccessibleText();
2251 HandleSpecialTextOffset(text_str
, &start_offset
);
2252 HandleSpecialTextOffset(text_str
, &end_offset
);
2254 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2259 // IAccessibleHypertext methods.
2262 STDMETHODIMP
BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count
) {
2263 if (!instance_active())
2266 if (!hyperlink_count
)
2267 return E_INVALIDARG
;
2269 *hyperlink_count
= hyperlink_offset_to_index_
.size();
2273 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlink(
2275 IAccessibleHyperlink
** hyperlink
) {
2276 if (!instance_active())
2281 index
>= static_cast<long>(hyperlinks_
.size())) {
2282 return E_INVALIDARG
;
2285 BrowserAccessibilityWin
* child
=
2286 InternalGetChild(hyperlinks_
[index
])->ToBrowserAccessibilityWin();
2287 *hyperlink
= static_cast<IAccessibleHyperlink
*>(child
->NewReference());
2291 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlinkIndex(
2293 long* hyperlink_index
) {
2294 if (!instance_active())
2297 if (!hyperlink_index
)
2298 return E_INVALIDARG
;
2300 *hyperlink_index
= -1;
2302 if (char_index
< 0 || char_index
>= static_cast<long>(hypertext_
.size()))
2303 return E_INVALIDARG
;
2305 std::map
<int32
, int32
>::iterator it
=
2306 hyperlink_offset_to_index_
.find(char_index
);
2307 if (it
== hyperlink_offset_to_index_
.end())
2310 *hyperlink_index
= it
->second
;
2315 // IAccessibleValue methods.
2318 STDMETHODIMP
BrowserAccessibilityWin::get_currentValue(VARIANT
* value
) {
2319 if (!instance_active())
2323 return E_INVALIDARG
;
2326 if (GetFloatAttribute(
2327 ui::AX_ATTR_VALUE_FOR_RANGE
, &float_val
)) {
2329 value
->dblVal
= float_val
;
2333 value
->vt
= VT_EMPTY
;
2337 STDMETHODIMP
BrowserAccessibilityWin::get_minimumValue(VARIANT
* value
) {
2338 if (!instance_active())
2342 return E_INVALIDARG
;
2345 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE
,
2348 value
->dblVal
= float_val
;
2352 value
->vt
= VT_EMPTY
;
2356 STDMETHODIMP
BrowserAccessibilityWin::get_maximumValue(VARIANT
* value
) {
2357 if (!instance_active())
2361 return E_INVALIDARG
;
2364 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE
,
2367 value
->dblVal
= float_val
;
2371 value
->vt
= VT_EMPTY
;
2375 STDMETHODIMP
BrowserAccessibilityWin::setCurrentValue(VARIANT new_value
) {
2376 // TODO(dmazzoni): Implement this.
2381 // ISimpleDOMDocument methods.
2384 STDMETHODIMP
BrowserAccessibilityWin::get_URL(BSTR
* url
) {
2385 if (!instance_active())
2389 return E_INVALIDARG
;
2391 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL
, url
);
2394 STDMETHODIMP
BrowserAccessibilityWin::get_title(BSTR
* title
) {
2395 if (!instance_active())
2399 return E_INVALIDARG
;
2401 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE
, title
);
2404 STDMETHODIMP
BrowserAccessibilityWin::get_mimeType(BSTR
* mime_type
) {
2405 if (!instance_active())
2409 return E_INVALIDARG
;
2411 return GetStringAttributeAsBstr(
2412 ui::AX_ATTR_DOC_MIMETYPE
, mime_type
);
2415 STDMETHODIMP
BrowserAccessibilityWin::get_docType(BSTR
* doc_type
) {
2416 if (!instance_active())
2420 return E_INVALIDARG
;
2422 return GetStringAttributeAsBstr(
2423 ui::AX_ATTR_DOC_DOCTYPE
, doc_type
);
2427 // ISimpleDOMNode methods.
2430 STDMETHODIMP
BrowserAccessibilityWin::get_nodeInfo(
2432 short* name_space_id
,
2434 unsigned int* num_children
,
2435 unsigned int* unique_id
,
2436 unsigned short* node_type
) {
2437 if (!instance_active())
2440 if (!node_name
|| !name_space_id
|| !node_value
|| !num_children
||
2441 !unique_id
|| !node_type
) {
2442 return E_INVALIDARG
;
2446 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG
, &tag
))
2447 *node_name
= SysAllocString(tag
.c_str());
2452 *node_value
= SysAllocString(base::UTF8ToUTF16(value()).c_str());
2453 *num_children
= PlatformChildCount();
2454 *unique_id
= unique_id_win_
;
2456 if (ia_role_
== ROLE_SYSTEM_DOCUMENT
) {
2457 *node_type
= NODETYPE_DOCUMENT
;
2458 } else if (ia_role_
== ROLE_SYSTEM_TEXT
&&
2459 ((ia2_state_
& IA2_STATE_EDITABLE
) == 0)) {
2460 *node_type
= NODETYPE_TEXT
;
2462 *node_type
= NODETYPE_ELEMENT
;
2468 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(
2469 unsigned short max_attribs
,
2471 short* name_space_id
,
2472 BSTR
* attrib_values
,
2473 unsigned short* num_attribs
) {
2474 if (!instance_active())
2477 if (!attrib_names
|| !name_space_id
|| !attrib_values
|| !num_attribs
)
2478 return E_INVALIDARG
;
2480 *num_attribs
= max_attribs
;
2481 if (*num_attribs
> GetHtmlAttributes().size())
2482 *num_attribs
= GetHtmlAttributes().size();
2484 for (unsigned short i
= 0; i
< *num_attribs
; ++i
) {
2485 attrib_names
[i
] = SysAllocString(
2486 base::UTF8ToUTF16(GetHtmlAttributes()[i
].first
).c_str());
2487 name_space_id
[i
] = 0;
2488 attrib_values
[i
] = SysAllocString(
2489 base::UTF8ToUTF16(GetHtmlAttributes()[i
].second
).c_str());
2494 STDMETHODIMP
BrowserAccessibilityWin::get_attributesForNames(
2495 unsigned short num_attribs
,
2497 short* name_space_id
,
2498 BSTR
* attrib_values
) {
2499 if (!instance_active())
2502 if (!attrib_names
|| !name_space_id
|| !attrib_values
)
2503 return E_INVALIDARG
;
2505 for (unsigned short i
= 0; i
< num_attribs
; ++i
) {
2506 name_space_id
[i
] = 0;
2508 std::string name
= base::UTF16ToUTF8((LPCWSTR
)attrib_names
[i
]);
2509 for (unsigned int j
= 0; j
< GetHtmlAttributes().size(); ++j
) {
2510 if (GetHtmlAttributes()[j
].first
== name
) {
2511 attrib_values
[i
] = SysAllocString(
2512 base::UTF8ToUTF16(GetHtmlAttributes()[j
].second
).c_str());
2518 attrib_values
[i
] = NULL
;
2524 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyle(
2525 unsigned short max_style_properties
,
2526 boolean use_alternate_view
,
2527 BSTR
* style_properties
,
2529 unsigned short *num_style_properties
) {
2530 if (!instance_active())
2533 if (!style_properties
|| !style_values
)
2534 return E_INVALIDARG
;
2536 // We only cache a single style property for now: DISPLAY
2538 base::string16 display
;
2539 if (max_style_properties
== 0 ||
2540 !GetString16Attribute(ui::AX_ATTR_DISPLAY
, &display
)) {
2541 *num_style_properties
= 0;
2545 *num_style_properties
= 1;
2546 style_properties
[0] = SysAllocString(L
"display");
2547 style_values
[0] = SysAllocString(display
.c_str());
2552 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyleForProperties(
2553 unsigned short num_style_properties
,
2554 boolean use_alternate_view
,
2555 BSTR
* style_properties
,
2556 BSTR
* style_values
) {
2557 if (!instance_active())
2560 if (!style_properties
|| !style_values
)
2561 return E_INVALIDARG
;
2563 // We only cache a single style property for now: DISPLAY
2565 for (unsigned short i
= 0; i
< num_style_properties
; ++i
) {
2566 base::string16 name
= (LPCWSTR
)style_properties
[i
];
2567 base::StringToLowerASCII(&name
);
2568 if (name
== L
"display") {
2569 base::string16 display
= GetString16Attribute(
2570 ui::AX_ATTR_DISPLAY
);
2571 style_values
[i
] = SysAllocString(display
.c_str());
2573 style_values
[i
] = NULL
;
2580 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(boolean placeTopLeft
) {
2581 return scrollTo(placeTopLeft
?
2582 IA2_SCROLL_TYPE_TOP_LEFT
: IA2_SCROLL_TYPE_ANYWHERE
);
2585 STDMETHODIMP
BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode
** node
) {
2586 if (!instance_active())
2590 return E_INVALIDARG
;
2592 *node
= GetParent()->ToBrowserAccessibilityWin()->NewReference();
2596 STDMETHODIMP
BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode
** node
) {
2597 if (!instance_active())
2601 return E_INVALIDARG
;
2603 if (PlatformChildCount() == 0) {
2608 *node
= PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2612 STDMETHODIMP
BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode
** node
) {
2613 if (!instance_active())
2617 return E_INVALIDARG
;
2619 if (PlatformChildCount() == 0) {
2624 *node
= PlatformGetChild(PlatformChildCount() - 1)
2625 ->ToBrowserAccessibilityWin()->NewReference();
2629 STDMETHODIMP
BrowserAccessibilityWin::get_previousSibling(
2630 ISimpleDOMNode
** node
) {
2631 if (!instance_active())
2635 return E_INVALIDARG
;
2637 if (!GetParent() || GetIndexInParent() <= 0) {
2642 *node
= GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2643 ToBrowserAccessibilityWin()->NewReference();
2647 STDMETHODIMP
BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode
** node
) {
2648 if (!instance_active())
2652 return E_INVALIDARG
;
2655 GetIndexInParent() < 0 ||
2656 GetIndexInParent() >= static_cast<int>(
2657 GetParent()->InternalChildCount()) - 1) {
2662 *node
= GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2663 ToBrowserAccessibilityWin()->NewReference();
2667 STDMETHODIMP
BrowserAccessibilityWin::get_childAt(
2668 unsigned int child_index
,
2669 ISimpleDOMNode
** node
) {
2670 if (!instance_active())
2674 return E_INVALIDARG
;
2676 if (child_index
>= PlatformChildCount())
2677 return E_INVALIDARG
;
2679 BrowserAccessibility
* child
= PlatformGetChild(child_index
);
2685 *node
= child
->ToBrowserAccessibilityWin()->NewReference();
2690 // ISimpleDOMText methods.
2693 STDMETHODIMP
BrowserAccessibilityWin::get_domText(BSTR
* dom_text
) {
2694 if (!instance_active())
2698 return E_INVALIDARG
;
2700 return GetStringAttributeAsBstr(
2701 ui::AX_ATTR_NAME
, dom_text
);
2704 STDMETHODIMP
BrowserAccessibilityWin::get_clippedSubstringBounds(
2705 unsigned int start_index
,
2706 unsigned int end_index
,
2711 // TODO(dmazzoni): fully support this API by intersecting the
2712 // rect with the container's rect.
2713 return get_unclippedSubstringBounds(
2714 start_index
, end_index
, out_x
, out_y
, out_width
, out_height
);
2717 STDMETHODIMP
BrowserAccessibilityWin::get_unclippedSubstringBounds(
2718 unsigned int start_index
,
2719 unsigned int end_index
,
2724 if (!instance_active())
2727 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
2728 return E_INVALIDARG
;
2730 const base::string16
& text_str
= TextForIAccessibleText();
2731 if (start_index
> text_str
.size() ||
2732 end_index
> text_str
.size() ||
2733 start_index
> end_index
) {
2734 return E_INVALIDARG
;
2737 gfx::Rect bounds
= GetGlobalBoundsForRange(
2738 start_index
, end_index
- start_index
);
2739 *out_x
= bounds
.x();
2740 *out_y
= bounds
.y();
2741 *out_width
= bounds
.width();
2742 *out_height
= bounds
.height();
2746 STDMETHODIMP
BrowserAccessibilityWin::scrollToSubstring(
2747 unsigned int start_index
,
2748 unsigned int end_index
) {
2749 if (!instance_active())
2752 const base::string16
& text_str
= TextForIAccessibleText();
2753 if (start_index
> text_str
.size() ||
2754 end_index
> text_str
.size() ||
2755 start_index
> end_index
) {
2756 return E_INVALIDARG
;
2759 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2760 start_index
, end_index
- start_index
));
2761 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2767 // IServiceProvider methods.
2770 STDMETHODIMP
BrowserAccessibilityWin::QueryService(REFGUID guidService
,
2773 if (!instance_active())
2776 // The system uses IAccessible APIs for many purposes, but only
2777 // assistive technology like screen readers uses IAccessible2.
2778 // Enable full accessibility support when IAccessible2 APIs are queried.
2779 if (riid
== IID_IAccessible2
)
2780 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2782 if (guidService
== GUID_IAccessibleContentDocument
) {
2783 // Special Mozilla extension: return the accessible for the root document.
2784 // Screen readers use this to distinguish between a document loaded event
2785 // on the root document vs on an iframe.
2786 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2787 IID_IAccessible2
, object
);
2790 if (guidService
== IID_IAccessible
||
2791 guidService
== IID_IAccessible2
||
2792 guidService
== IID_IAccessibleAction
||
2793 guidService
== IID_IAccessibleApplication
||
2794 guidService
== IID_IAccessibleHyperlink
||
2795 guidService
== IID_IAccessibleHypertext
||
2796 guidService
== IID_IAccessibleImage
||
2797 guidService
== IID_IAccessibleTable
||
2798 guidService
== IID_IAccessibleTable2
||
2799 guidService
== IID_IAccessibleTableCell
||
2800 guidService
== IID_IAccessibleText
||
2801 guidService
== IID_IAccessibleValue
||
2802 guidService
== IID_ISimpleDOMDocument
||
2803 guidService
== IID_ISimpleDOMNode
||
2804 guidService
== IID_ISimpleDOMText
||
2805 guidService
== GUID_ISimpleDOM
) {
2806 return QueryInterface(riid
, object
);
2809 // We only support the IAccessibleEx interface on Windows 8 and above. This
2810 // is needed for the on-screen Keyboard to show up in metro mode, when the
2811 // user taps an editable portion on the page.
2812 // All methods in the IAccessibleEx interface are unimplemented.
2813 if (riid
== IID_IAccessibleEx
&&
2814 base::win::GetVersion() >= base::win::VERSION_WIN8
) {
2815 return QueryInterface(riid
, object
);
2822 STDMETHODIMP
BrowserAccessibilityWin::GetPatternProvider(PATTERNID id
,
2823 IUnknown
** provider
) {
2824 DVLOG(1) << "In Function: "
2826 << " for pattern id: "
2828 if (id
== UIA_ValuePatternId
|| id
== UIA_TextPatternId
) {
2829 if (IsEditableText()) {
2830 DVLOG(1) << "Returning UIA text provider";
2831 base::win::UIATextProvider::CreateTextProvider(
2832 GetValueText(), true, provider
);
2839 STDMETHODIMP
BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id
,
2841 DVLOG(1) << "In Function: "
2843 << " for property id: "
2845 V_VT(ret
) = VT_EMPTY
;
2846 if (id
== UIA_ControlTypePropertyId
) {
2847 if (IsEditableText()) {
2849 ret
->lVal
= UIA_EditControlTypeId
;
2850 DVLOG(1) << "Returning Edit control type";
2852 DVLOG(1) << "Returning empty control type";
2859 // CComObjectRootEx methods.
2862 HRESULT WINAPI
BrowserAccessibilityWin::InternalQueryInterface(
2864 const _ATL_INTMAP_ENTRY
* entries
,
2867 if (iid
== IID_IAccessibleImage
) {
2868 if (ia_role_
!= ROLE_SYSTEM_GRAPHIC
) {
2870 return E_NOINTERFACE
;
2872 } else if (iid
== IID_IAccessibleTable
|| iid
== IID_IAccessibleTable2
) {
2873 if (ia_role_
!= ROLE_SYSTEM_TABLE
) {
2875 return E_NOINTERFACE
;
2877 } else if (iid
== IID_IAccessibleTableCell
) {
2878 if (ia_role_
!= ROLE_SYSTEM_CELL
) {
2880 return E_NOINTERFACE
;
2882 } else if (iid
== IID_IAccessibleValue
) {
2883 if (ia_role_
!= ROLE_SYSTEM_PROGRESSBAR
&&
2884 ia_role_
!= ROLE_SYSTEM_SCROLLBAR
&&
2885 ia_role_
!= ROLE_SYSTEM_SLIDER
) {
2887 return E_NOINTERFACE
;
2889 } else if (iid
== IID_ISimpleDOMDocument
) {
2890 if (ia_role_
!= ROLE_SYSTEM_DOCUMENT
) {
2892 return E_NOINTERFACE
;
2896 return CComObjectRootBase::InternalQueryInterface(
2897 this_ptr
, entries
, iid
, object
);
2904 // Called every time this node's data changes.
2905 void BrowserAccessibilityWin::OnDataChanged() {
2906 BrowserAccessibility::OnDataChanged();
2910 // Expose the "display" and "tag" attributes.
2911 StringAttributeToIA2(ui::AX_ATTR_DISPLAY
, "display");
2912 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG
, "tag");
2913 StringAttributeToIA2(ui::AX_ATTR_ROLE
, "xml-roles");
2915 // Expose "level" attribute for headings, trees, etc.
2916 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL
, "level");
2918 // Expose the set size and position in set for listbox options.
2919 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
2921 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX
) {
2922 ia2_attributes_
.push_back(
2923 L
"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
2924 ia2_attributes_
.push_back(
2925 L
"setsize:" + base::IntToString16(GetIndexInParent() + 1));
2928 if (ia_role_
== ROLE_SYSTEM_CHECKBUTTON
||
2929 ia_role_
== ROLE_SYSTEM_RADIOBUTTON
||
2930 ia2_role_
== IA2_ROLE_TOGGLE_BUTTON
) {
2931 ia2_attributes_
.push_back(L
"checkable:true");
2934 // Expose live region attributes.
2935 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS
, "live");
2936 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT
, "relevant");
2937 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC
, "atomic");
2938 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY
, "busy");
2940 // Expose container live region attributes.
2941 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS
,
2943 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT
,
2944 "container-relevant");
2945 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC
,
2946 "container-atomic");
2947 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY
,
2950 // Expose slider value.
2951 if (ia_role_
== ROLE_SYSTEM_PROGRESSBAR
||
2952 ia_role_
== ROLE_SYSTEM_SCROLLBAR
||
2953 ia_role_
== ROLE_SYSTEM_SLIDER
) {
2954 ia2_attributes_
.push_back(L
"valuetext:" + GetValueText());
2957 // Expose table cell index.
2958 if (ia_role_
== ROLE_SYSTEM_CELL
) {
2959 BrowserAccessibility
* table
= GetParent();
2960 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
2961 table
= table
->GetParent();
2963 const std::vector
<int32
>& unique_cell_ids
= table
->GetIntListAttribute(
2964 ui::AX_ATTR_UNIQUE_CELL_IDS
);
2965 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
2966 if (unique_cell_ids
[i
] == GetId()) {
2967 ia2_attributes_
.push_back(
2968 base::string16(L
"table-cell-index:") + base::IntToString16(i
));
2974 // The calculation of the accessible name of an element has been
2975 // standardized in the HTML to Platform Accessibility APIs Implementation
2976 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2977 // appropriate accessible name on Windows, we need to apply some logic
2978 // to the fields we get from WebKit.
2980 // TODO(dmazzoni): move most of this logic into WebKit.
2984 // name: the default name, e.g. inner text
2985 // title ui element: a reference to a <label> element on the same
2986 // page that labels this node.
2987 // description: accessible labels that override the default name:
2988 // aria-label or aria-labelledby or aria-describedby
2989 // help: the value of the "title" attribute
2991 // On Windows, the logic we apply lets some fields take precedence and
2992 // always returns the primary name in "name" and the secondary name,
2993 // if any, in "description".
2995 int title_elem_id
= GetIntAttribute(
2996 ui::AX_ATTR_TITLE_UI_ELEMENT
);
2997 std::string help
= GetStringAttribute(ui::AX_ATTR_HELP
);
2998 std::string description
= GetStringAttribute(
2999 ui::AX_ATTR_DESCRIPTION
);
3001 // WebKit annoyingly puts the title in the description if there's no other
3002 // description, which just confuses the rest of the logic. Put it back.
3003 // Now "help" is always the value of the "title" attribute, if present.
3004 std::string title_attr
;
3005 if (GetHtmlAttribute("title", &title_attr
) &&
3006 description
== title_attr
&&
3009 description
.clear();
3012 // Now implement the main logic: the descripion should become the name if
3013 // it's nonempty, and the help should become the description if
3014 // there's no description - or the name if there's no name or description.
3015 if (!description
.empty()) {
3016 set_name(description
);
3017 description
.clear();
3019 if (!help
.empty() && description
.empty()) {
3023 if (!description
.empty() && name().empty() && !title_elem_id
) {
3024 set_name(description
);
3025 description
.clear();
3028 // If it's a text field, also consider the placeholder.
3029 std::string placeholder
;
3030 if (GetRole() == ui::AX_ROLE_TEXT_FIELD
&&
3031 HasState(ui::AX_STATE_FOCUSABLE
) &&
3032 GetHtmlAttribute("placeholder", &placeholder
)) {
3033 if (name().empty() && !title_elem_id
) {
3034 set_name(placeholder
);
3035 } else if (description
.empty()) {
3036 description
= placeholder
;
3040 SetStringAttribute(ui::AX_ATTR_DESCRIPTION
, description
);
3041 SetStringAttribute(ui::AX_ATTR_HELP
, help
);
3043 // On Windows, the value of a document should be its url.
3044 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
||
3045 GetRole() == ui::AX_ROLE_WEB_AREA
) {
3046 set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL
));
3049 // For certain roles (listbox option, static text, and list marker)
3050 // WebKit stores the main accessible text in the "value" - swap it so
3051 // that it's the "name".
3052 if (name().empty() &&
3053 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
||
3054 GetRole() == ui::AX_ROLE_STATIC_TEXT
||
3055 GetRole() == ui::AX_ROLE_LIST_MARKER
)) {
3056 std::string tmp
= value();
3061 // If this doesn't have a value and is linked then set its value to the url
3062 // attribute. This allows screen readers to read an empty link's destination.
3063 if (value().empty() && (ia_state_
& STATE_SYSTEM_LINKED
))
3064 set_value(GetStringAttribute(ui::AX_ATTR_URL
));
3066 // Clear any old relationships between this node and other nodes.
3067 for (size_t i
= 0; i
< relations_
.size(); ++i
)
3068 relations_
[i
]->Release();
3071 // Handle title UI element.
3072 if (title_elem_id
) {
3073 // Add a labelled by relationship.
3074 CComObject
<BrowserAccessibilityRelation
>* relation
;
3075 HRESULT hr
= CComObject
<BrowserAccessibilityRelation
>::CreateInstance(
3077 DCHECK(SUCCEEDED(hr
));
3079 relation
->Initialize(this, IA2_RELATION_LABELLED_BY
);
3080 relation
->AddTarget(title_elem_id
);
3081 relations_
.push_back(relation
);
3085 void BrowserAccessibilityWin::OnUpdateFinished() {
3086 // Construct the hypertext for this node.
3087 hyperlink_offset_to_index_
.clear();
3088 hyperlinks_
.clear();
3090 for (unsigned int i
= 0; i
< PlatformChildCount(); ++i
) {
3091 BrowserAccessibility
* child
= PlatformGetChild(i
);
3092 if (child
->GetRole() == ui::AX_ROLE_STATIC_TEXT
) {
3093 hypertext_
+= base::UTF8ToUTF16(child
->name());
3095 hyperlink_offset_to_index_
[hypertext_
.size()] = hyperlinks_
.size();
3096 hypertext_
+= kEmbeddedCharacter
;
3097 hyperlinks_
.push_back(i
);
3100 DCHECK_EQ(hyperlink_offset_to_index_
.size(), hyperlinks_
.size());
3102 // Fire an event when an alert first appears.
3103 if (GetRole() == ui::AX_ROLE_ALERT
&& first_time_
)
3104 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, this);
3106 // Fire events if text has changed.
3107 base::string16 text
= TextForIAccessibleText();
3108 if (previous_text_
!= text
) {
3109 if (!previous_text_
.empty() && !text
.empty()) {
3110 manager()->NotifyAccessibilityEvent(
3111 ui::AX_EVENT_SHOW
, this);
3114 // TODO(dmazzoni): Look into HIDE events, too.
3116 old_text_
= previous_text_
;
3117 previous_text_
= text
;
3120 BrowserAccessibilityManagerWin
* manager
=
3121 this->manager()->ToBrowserAccessibilityManagerWin();
3123 // Fire events if the state has changed.
3124 if (!first_time_
&& ia_state_
!= old_ia_state_
) {
3125 // Normally focus events are handled elsewhere, however
3126 // focus for managed descendants is platform-specific.
3127 // Fire a focus event if the focused descendant in a multi-select
3128 // list box changes.
3129 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
3130 (ia_state_
& STATE_SYSTEM_FOCUSABLE
) &&
3131 (ia_state_
& STATE_SYSTEM_SELECTABLE
) &&
3132 (ia_state_
& STATE_SYSTEM_FOCUSED
) &&
3133 !(old_ia_state_
& STATE_SYSTEM_FOCUSED
)) {
3134 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS
, unique_id_win());
3137 if ((ia_state_
& STATE_SYSTEM_SELECTED
) &&
3138 !(old_ia_state_
& STATE_SYSTEM_SELECTED
)) {
3139 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD
,
3141 } else if (!(ia_state_
& STATE_SYSTEM_SELECTED
) &&
3142 (old_ia_state_
& STATE_SYSTEM_SELECTED
)) {
3143 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE
,
3147 old_ia_state_
= ia_state_
;
3150 // Fire an event if this container object has scrolled.
3153 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
3154 GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
3156 (sx
!= previous_scroll_x_
|| sy
!= previous_scroll_y_
)) {
3157 manager
->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND
,
3160 previous_scroll_x_
= sx
;
3161 previous_scroll_y_
= sy
;
3164 first_time_
= false;
3167 void BrowserAccessibilityWin::NativeAddReference() {
3171 void BrowserAccessibilityWin::NativeReleaseReference() {
3175 bool BrowserAccessibilityWin::IsNative() const {
3179 void BrowserAccessibilityWin::OnLocationChanged() {
3180 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3181 EVENT_OBJECT_LOCATIONCHANGE
, unique_id_win());
3184 BrowserAccessibilityWin
* BrowserAccessibilityWin::NewReference() {
3189 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetTargetFromChildID(
3190 const VARIANT
& var_id
) {
3191 if (var_id
.vt
!= VT_I4
)
3194 LONG child_id
= var_id
.lVal
;
3195 if (child_id
== CHILDID_SELF
)
3198 if (child_id
>= 1 && child_id
<= static_cast<LONG
>(PlatformChildCount()))
3199 return PlatformGetChild(child_id
- 1)->ToBrowserAccessibilityWin();
3201 return manager()->ToBrowserAccessibilityManagerWin()->
3202 GetFromUniqueIdWin(child_id
);
3205 HRESULT
BrowserAccessibilityWin::GetStringAttributeAsBstr(
3206 ui::AXStringAttribute attribute
,
3210 if (!GetString16Attribute(attribute
, &str
))
3216 *value_bstr
= SysAllocString(str
.c_str());
3217 DCHECK(*value_bstr
);
3222 void BrowserAccessibilityWin::StringAttributeToIA2(
3223 ui::AXStringAttribute attribute
,
3224 const char* ia2_attr
) {
3225 base::string16 value
;
3226 if (GetString16Attribute(attribute
, &value
))
3227 ia2_attributes_
.push_back(base::ASCIIToUTF16(ia2_attr
) + L
":" + value
);
3230 void BrowserAccessibilityWin::BoolAttributeToIA2(
3231 ui::AXBoolAttribute attribute
,
3232 const char* ia2_attr
) {
3234 if (GetBoolAttribute(attribute
, &value
)) {
3235 ia2_attributes_
.push_back((base::ASCIIToUTF16(ia2_attr
) + L
":") +
3236 (value
? L
"true" : L
"false"));
3240 void BrowserAccessibilityWin::IntAttributeToIA2(
3241 ui::AXIntAttribute attribute
,
3242 const char* ia2_attr
) {
3244 if (GetIntAttribute(attribute
, &value
)) {
3245 ia2_attributes_
.push_back(base::ASCIIToUTF16(ia2_attr
) + L
":" +
3246 base::IntToString16(value
));
3250 base::string16
BrowserAccessibilityWin::GetValueText() {
3252 base::string16 value
= base::UTF8ToUTF16(this->value());
3254 if (value
.empty() &&
3255 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE
, &fval
)) {
3256 value
= base::UTF8ToUTF16(base::DoubleToString(fval
));
3261 base::string16
BrowserAccessibilityWin::TextForIAccessibleText() {
3262 if (IsEditableText())
3263 return base::UTF8ToUTF16(value());
3264 return (GetRole() == ui::AX_ROLE_STATIC_TEXT
) ?
3265 base::UTF8ToUTF16(name()) : hypertext_
;
3268 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3269 const base::string16
& text
,
3271 if (*offset
== IA2_TEXT_OFFSET_LENGTH
)
3272 *offset
= static_cast<LONG
>(text
.size());
3273 else if (*offset
== IA2_TEXT_OFFSET_CARET
)
3274 get_caretOffset(offset
);
3277 ui::TextBoundaryType
BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3278 IA2TextBoundaryType ia2_boundary
) {
3279 switch(ia2_boundary
) {
3280 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
3281 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
3282 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
3283 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
3284 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
3285 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
3288 return ui::CHAR_BOUNDARY
;
3292 LONG
BrowserAccessibilityWin::FindBoundary(
3293 const base::string16
& text
,
3294 IA2TextBoundaryType ia2_boundary
,
3296 ui::TextBoundaryDirection direction
) {
3297 HandleSpecialTextOffset(text
, &start_offset
);
3298 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
3299 const std::vector
<int32
>& line_breaks
= GetIntListAttribute(
3300 ui::AX_ATTR_LINE_BREAKS
);
3301 return ui::FindAccessibleTextBoundary(
3302 text
, line_breaks
, boundary
, start_offset
, direction
);
3305 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetFromID(int32 id
) {
3306 return manager()->GetFromID(id
)->ToBrowserAccessibilityWin();
3309 void BrowserAccessibilityWin::InitRoleAndState() {
3311 ia2_state_
= IA2_STATE_OPAQUE
;
3312 ia2_attributes_
.clear();
3314 if (HasState(ui::AX_STATE_BUSY
))
3315 ia_state_
|= STATE_SYSTEM_BUSY
;
3316 if (HasState(ui::AX_STATE_CHECKED
))
3317 ia_state_
|= STATE_SYSTEM_CHECKED
;
3318 if (HasState(ui::AX_STATE_COLLAPSED
))
3319 ia_state_
|= STATE_SYSTEM_COLLAPSED
;
3320 if (HasState(ui::AX_STATE_EXPANDED
))
3321 ia_state_
|= STATE_SYSTEM_EXPANDED
;
3322 if (HasState(ui::AX_STATE_FOCUSABLE
))
3323 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3324 if (HasState(ui::AX_STATE_HASPOPUP
))
3325 ia_state_
|= STATE_SYSTEM_HASPOPUP
;
3326 if (HasState(ui::AX_STATE_HOVERED
))
3327 ia_state_
|= STATE_SYSTEM_HOTTRACKED
;
3328 if (HasState(ui::AX_STATE_INDETERMINATE
))
3329 ia_state_
|= STATE_SYSTEM_INDETERMINATE
;
3330 if (HasState(ui::AX_STATE_INVISIBLE
))
3331 ia_state_
|= STATE_SYSTEM_INVISIBLE
;
3332 if (HasState(ui::AX_STATE_LINKED
))
3333 ia_state_
|= STATE_SYSTEM_LINKED
;
3334 if (HasState(ui::AX_STATE_MULTISELECTABLE
)) {
3335 ia_state_
|= STATE_SYSTEM_EXTSELECTABLE
;
3336 ia_state_
|= STATE_SYSTEM_MULTISELECTABLE
;
3338 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3339 if (HasState(ui::AX_STATE_OFFSCREEN
))
3340 ia_state_
|= STATE_SYSTEM_OFFSCREEN
;
3341 if (HasState(ui::AX_STATE_PRESSED
))
3342 ia_state_
|= STATE_SYSTEM_PRESSED
;
3343 if (HasState(ui::AX_STATE_PROTECTED
))
3344 ia_state_
|= STATE_SYSTEM_PROTECTED
;
3345 if (HasState(ui::AX_STATE_REQUIRED
))
3346 ia2_state_
|= IA2_STATE_REQUIRED
;
3347 if (HasState(ui::AX_STATE_SELECTABLE
))
3348 ia_state_
|= STATE_SYSTEM_SELECTABLE
;
3349 if (HasState(ui::AX_STATE_SELECTED
))
3350 ia_state_
|= STATE_SYSTEM_SELECTED
;
3351 if (HasState(ui::AX_STATE_VISITED
))
3352 ia_state_
|= STATE_SYSTEM_TRAVERSED
;
3353 if (!HasState(ui::AX_STATE_ENABLED
))
3354 ia_state_
|= STATE_SYSTEM_UNAVAILABLE
;
3355 if (HasState(ui::AX_STATE_VERTICAL
)) {
3356 ia2_state_
|= IA2_STATE_VERTICAL
;
3358 ia2_state_
|= IA2_STATE_HORIZONTAL
;
3360 if (HasState(ui::AX_STATE_VISITED
))
3361 ia_state_
|= STATE_SYSTEM_TRAVERSED
;
3363 // WebKit marks everything as readonly unless it's editable text, so if it's
3364 // not readonly, mark it as editable now. The final computation of the
3365 // READONLY state for MSAA is below, after the switch.
3366 if (!HasState(ui::AX_STATE_READ_ONLY
))
3367 ia2_state_
|= IA2_STATE_EDITABLE
;
3369 base::string16 invalid
;
3370 if (GetHtmlAttribute("aria-invalid", &invalid
))
3371 ia2_state_
|= IA2_STATE_INVALID_ENTRY
;
3373 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED
))
3374 ia_state_
|= STATE_SYSTEM_MIXED
;
3376 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE
))
3377 ia2_state_
|= IA2_STATE_EDITABLE
;
3379 base::string16 html_tag
= GetString16Attribute(
3380 ui::AX_ATTR_HTML_TAG
);
3383 switch (GetRole()) {
3384 case ui::AX_ROLE_ALERT
:
3385 ia_role_
= ROLE_SYSTEM_ALERT
;
3387 case ui::AX_ROLE_ALERT_DIALOG
:
3388 ia_role_
= ROLE_SYSTEM_DIALOG
;
3390 case ui::AX_ROLE_APPLICATION
:
3391 ia_role_
= ROLE_SYSTEM_APPLICATION
;
3393 case ui::AX_ROLE_ARTICLE
:
3394 ia_role_
= ROLE_SYSTEM_GROUPING
;
3395 ia2_role_
= IA2_ROLE_SECTION
;
3396 ia_state_
|= STATE_SYSTEM_READONLY
;
3398 case ui::AX_ROLE_BUSY_INDICATOR
:
3399 ia_role_
= ROLE_SYSTEM_ANIMATION
;
3400 ia_state_
|= STATE_SYSTEM_READONLY
;
3402 case ui::AX_ROLE_BUTTON
:
3403 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3404 bool is_aria_pressed_defined
;
3406 if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined
, &is_mixed
))
3407 ia_state_
|= STATE_SYSTEM_PRESSED
;
3408 if (is_aria_pressed_defined
)
3409 ia2_role_
= IA2_ROLE_TOGGLE_BUTTON
;
3411 ia_state_
|= STATE_SYSTEM_MIXED
;
3413 case ui::AX_ROLE_CANVAS
:
3414 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK
)) {
3415 role_name_
= L
"canvas";
3416 ia2_role_
= IA2_ROLE_CANVAS
;
3418 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3421 case ui::AX_ROLE_CELL
:
3422 ia_role_
= ROLE_SYSTEM_CELL
;
3424 case ui::AX_ROLE_CHECK_BOX
:
3425 ia_role_
= ROLE_SYSTEM_CHECKBUTTON
;
3427 case ui::AX_ROLE_COLOR_WELL
:
3428 ia_role_
= ROLE_SYSTEM_CLIENT
;
3429 ia2_role_
= IA2_ROLE_COLOR_CHOOSER
;
3431 case ui::AX_ROLE_COLUMN
:
3432 ia_role_
= ROLE_SYSTEM_COLUMN
;
3433 ia_state_
|= STATE_SYSTEM_READONLY
;
3435 case ui::AX_ROLE_COLUMN_HEADER
:
3436 ia_role_
= ROLE_SYSTEM_COLUMNHEADER
;
3437 ia_state_
|= STATE_SYSTEM_READONLY
;
3439 case ui::AX_ROLE_COMBO_BOX
:
3440 ia_role_
= ROLE_SYSTEM_COMBOBOX
;
3442 case ui::AX_ROLE_DIV
:
3443 role_name_
= L
"div";
3444 ia2_role_
= IA2_ROLE_SECTION
;
3446 case ui::AX_ROLE_DEFINITION
:
3447 role_name_
= html_tag
;
3448 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3449 ia_state_
|= STATE_SYSTEM_READONLY
;
3451 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL
:
3452 role_name_
= html_tag
;
3453 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3454 ia_state_
|= STATE_SYSTEM_READONLY
;
3456 case ui::AX_ROLE_DESCRIPTION_LIST_TERM
:
3457 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3458 ia_state_
|= STATE_SYSTEM_READONLY
;
3460 case ui::AX_ROLE_DIALOG
:
3461 ia_role_
= ROLE_SYSTEM_DIALOG
;
3462 ia_state_
|= STATE_SYSTEM_READONLY
;
3464 case ui::AX_ROLE_DISCLOSURE_TRIANGLE
:
3465 ia_role_
= ROLE_SYSTEM_OUTLINEBUTTON
;
3466 ia_state_
|= STATE_SYSTEM_READONLY
;
3468 case ui::AX_ROLE_DOCUMENT
:
3469 case ui::AX_ROLE_ROOT_WEB_AREA
:
3470 case ui::AX_ROLE_WEB_AREA
:
3471 ia_role_
= ROLE_SYSTEM_DOCUMENT
;
3472 ia_state_
|= STATE_SYSTEM_READONLY
;
3473 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3475 case ui::AX_ROLE_EDITABLE_TEXT
:
3476 ia_role_
= ROLE_SYSTEM_TEXT
;
3477 ia2_state_
|= IA2_STATE_SINGLE_LINE
;
3478 ia2_state_
|= IA2_STATE_EDITABLE
;
3480 case ui::AX_ROLE_FORM
:
3481 role_name_
= L
"form";
3482 ia2_role_
= IA2_ROLE_FORM
;
3484 case ui::AX_ROLE_FOOTER
:
3485 ia_role_
= IA2_ROLE_FOOTER
;
3486 ia_state_
|= STATE_SYSTEM_READONLY
;
3488 case ui::AX_ROLE_GRID
:
3489 ia_role_
= ROLE_SYSTEM_TABLE
;
3490 ia_state_
|= STATE_SYSTEM_READONLY
;
3492 case ui::AX_ROLE_GROUP
: {
3493 base::string16 aria_role
= GetString16Attribute(
3495 if (aria_role
== L
"group" || html_tag
== L
"fieldset") {
3496 ia_role_
= ROLE_SYSTEM_GROUPING
;
3497 } else if (html_tag
== L
"li") {
3498 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3500 if (html_tag
.empty())
3501 role_name_
= L
"div";
3503 role_name_
= html_tag
;
3504 ia2_role_
= IA2_ROLE_SECTION
;
3506 ia_state_
|= STATE_SYSTEM_READONLY
;
3509 case ui::AX_ROLE_GROW_AREA
:
3510 ia_role_
= ROLE_SYSTEM_GRIP
;
3511 ia_state_
|= STATE_SYSTEM_READONLY
;
3513 case ui::AX_ROLE_HEADING
:
3514 role_name_
= html_tag
;
3515 ia2_role_
= IA2_ROLE_HEADING
;
3516 ia_state_
|= STATE_SYSTEM_READONLY
;
3518 case ui::AX_ROLE_HORIZONTAL_RULE
:
3519 ia_role_
= ROLE_SYSTEM_SEPARATOR
;
3521 case ui::AX_ROLE_IFRAME
:
3522 ia_role_
= ROLE_SYSTEM_CLIENT
;
3523 ia2_role_
= IA2_ROLE_INTERNAL_FRAME
;
3525 case ui::AX_ROLE_IMAGE
:
3526 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3527 ia_state_
|= STATE_SYSTEM_READONLY
;
3529 case ui::AX_ROLE_IMAGE_MAP
:
3530 role_name_
= html_tag
;
3531 ia2_role_
= IA2_ROLE_IMAGE_MAP
;
3532 ia_state_
|= STATE_SYSTEM_READONLY
;
3534 case ui::AX_ROLE_IMAGE_MAP_LINK
:
3535 ia_role_
= ROLE_SYSTEM_LINK
;
3536 ia_state_
|= STATE_SYSTEM_LINKED
;
3537 ia_state_
|= STATE_SYSTEM_READONLY
;
3539 case ui::AX_ROLE_LABEL_TEXT
:
3540 ia_role_
= ROLE_SYSTEM_TEXT
;
3541 ia2_role_
= IA2_ROLE_LABEL
;
3543 case ui::AX_ROLE_BANNER
:
3544 case ui::AX_ROLE_COMPLEMENTARY
:
3545 case ui::AX_ROLE_CONTENT_INFO
:
3546 case ui::AX_ROLE_MAIN
:
3547 case ui::AX_ROLE_NAVIGATION
:
3548 case ui::AX_ROLE_SEARCH
:
3549 ia_role_
= ROLE_SYSTEM_GROUPING
;
3550 ia2_role_
= IA2_ROLE_SECTION
;
3551 ia_state_
|= STATE_SYSTEM_READONLY
;
3553 case ui::AX_ROLE_LINK
:
3554 ia_role_
= ROLE_SYSTEM_LINK
;
3555 ia_state_
|= STATE_SYSTEM_LINKED
;
3557 case ui::AX_ROLE_LIST
:
3558 ia_role_
= ROLE_SYSTEM_LIST
;
3559 ia_state_
|= STATE_SYSTEM_READONLY
;
3561 case ui::AX_ROLE_LIST_BOX
:
3562 ia_role_
= ROLE_SYSTEM_LIST
;
3564 case ui::AX_ROLE_LIST_BOX_OPTION
:
3565 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3566 if (ia_state_
& STATE_SYSTEM_SELECTABLE
) {
3567 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3568 if (HasState(ui::AX_STATE_FOCUSED
))
3569 ia_state_
|= STATE_SYSTEM_FOCUSED
;
3572 case ui::AX_ROLE_LIST_ITEM
:
3573 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3574 ia_state_
|= STATE_SYSTEM_READONLY
;
3576 case ui::AX_ROLE_MATH_ELEMENT
:
3577 ia_role_
= ROLE_SYSTEM_EQUATION
;
3578 ia_state_
|= STATE_SYSTEM_READONLY
;
3580 case ui::AX_ROLE_MENU
:
3581 case ui::AX_ROLE_MENU_BUTTON
:
3582 ia_role_
= ROLE_SYSTEM_MENUPOPUP
;
3584 case ui::AX_ROLE_MENU_BAR
:
3585 ia_role_
= ROLE_SYSTEM_MENUBAR
;
3587 case ui::AX_ROLE_MENU_ITEM
:
3588 ia_role_
= ROLE_SYSTEM_MENUITEM
;
3590 case ui::AX_ROLE_MENU_LIST_POPUP
:
3591 ia_role_
= ROLE_SYSTEM_CLIENT
;
3593 case ui::AX_ROLE_MENU_LIST_OPTION
:
3594 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3595 if (ia_state_
& STATE_SYSTEM_SELECTABLE
) {
3596 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3597 if (HasState(ui::AX_STATE_FOCUSED
))
3598 ia_state_
|= STATE_SYSTEM_FOCUSED
;
3601 case ui::AX_ROLE_NOTE
:
3602 ia_role_
= ROLE_SYSTEM_GROUPING
;
3603 ia2_role_
= IA2_ROLE_NOTE
;
3604 ia_state_
|= STATE_SYSTEM_READONLY
;
3606 case ui::AX_ROLE_OUTLINE
:
3607 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3608 ia_state_
|= STATE_SYSTEM_READONLY
;
3610 case ui::AX_ROLE_PARAGRAPH
:
3612 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3614 case ui::AX_ROLE_POP_UP_BUTTON
:
3615 if (html_tag
== L
"select") {
3616 ia_role_
= ROLE_SYSTEM_COMBOBOX
;
3618 ia_role_
= ROLE_SYSTEM_BUTTONMENU
;
3621 case ui::AX_ROLE_PROGRESS_INDICATOR
:
3622 ia_role_
= ROLE_SYSTEM_PROGRESSBAR
;
3623 ia_state_
|= STATE_SYSTEM_READONLY
;
3625 case ui::AX_ROLE_RADIO_BUTTON
:
3626 ia_role_
= ROLE_SYSTEM_RADIOBUTTON
;
3628 case ui::AX_ROLE_RADIO_GROUP
:
3629 ia_role_
= ROLE_SYSTEM_GROUPING
;
3630 ia2_role_
= IA2_ROLE_SECTION
;
3632 case ui::AX_ROLE_REGION
:
3633 ia_role_
= ROLE_SYSTEM_GROUPING
;
3634 ia2_role_
= IA2_ROLE_SECTION
;
3635 ia_state_
|= STATE_SYSTEM_READONLY
;
3637 case ui::AX_ROLE_ROW
:
3638 ia_role_
= ROLE_SYSTEM_ROW
;
3639 ia_state_
|= STATE_SYSTEM_READONLY
;
3641 case ui::AX_ROLE_ROW_HEADER
:
3642 ia_role_
= ROLE_SYSTEM_ROWHEADER
;
3643 ia_state_
|= STATE_SYSTEM_READONLY
;
3645 case ui::AX_ROLE_RULER
:
3646 ia_role_
= ROLE_SYSTEM_CLIENT
;
3647 ia2_role_
= IA2_ROLE_RULER
;
3648 ia_state_
|= STATE_SYSTEM_READONLY
;
3650 case ui::AX_ROLE_SCROLL_AREA
:
3651 ia_role_
= ROLE_SYSTEM_CLIENT
;
3652 ia2_role_
= IA2_ROLE_SCROLL_PANE
;
3653 ia_state_
|= STATE_SYSTEM_READONLY
;
3654 ia2_state_
&= ~(IA2_STATE_EDITABLE
);
3656 case ui::AX_ROLE_SCROLL_BAR
:
3657 ia_role_
= ROLE_SYSTEM_SCROLLBAR
;
3659 case ui::AX_ROLE_SLIDER
:
3660 ia_role_
= ROLE_SYSTEM_SLIDER
;
3662 case ui::AX_ROLE_SPIN_BUTTON
:
3663 ia_role_
= ROLE_SYSTEM_SPINBUTTON
;
3665 case ui::AX_ROLE_SPIN_BUTTON_PART
:
3666 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3668 case ui::AX_ROLE_SPLIT_GROUP
:
3669 ia_role_
= ROLE_SYSTEM_CLIENT
;
3670 ia2_role_
= IA2_ROLE_SPLIT_PANE
;
3671 ia_state_
|= STATE_SYSTEM_READONLY
;
3673 case ui::AX_ROLE_ANNOTATION
:
3674 case ui::AX_ROLE_LIST_MARKER
:
3675 case ui::AX_ROLE_STATIC_TEXT
:
3676 ia_role_
= ROLE_SYSTEM_STATICTEXT
;
3678 case ui::AX_ROLE_STATUS
:
3679 ia_role_
= ROLE_SYSTEM_STATUSBAR
;
3680 ia_state_
|= STATE_SYSTEM_READONLY
;
3682 case ui::AX_ROLE_SPLITTER
:
3683 ia_role_
= ROLE_SYSTEM_SEPARATOR
;
3685 case ui::AX_ROLE_SVG_ROOT
:
3686 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3688 case ui::AX_ROLE_TAB
:
3689 ia_role_
= ROLE_SYSTEM_PAGETAB
;
3691 case ui::AX_ROLE_TABLE
: {
3692 base::string16 aria_role
= GetString16Attribute(
3694 if (aria_role
== L
"treegrid") {
3695 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3697 ia_role_
= ROLE_SYSTEM_TABLE
;
3698 ia_state_
|= STATE_SYSTEM_READONLY
;
3702 case ui::AX_ROLE_TABLE_HEADER_CONTAINER
:
3703 ia_role_
= ROLE_SYSTEM_GROUPING
;
3704 ia2_role_
= IA2_ROLE_SECTION
;
3705 ia_state_
|= STATE_SYSTEM_READONLY
;
3707 case ui::AX_ROLE_TAB_LIST
:
3708 ia_role_
= ROLE_SYSTEM_PAGETABLIST
;
3710 case ui::AX_ROLE_TAB_PANEL
:
3711 ia_role_
= ROLE_SYSTEM_PROPERTYPAGE
;
3713 case ui::AX_ROLE_TOGGLE_BUTTON
:
3714 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3715 ia2_role_
= IA2_ROLE_TOGGLE_BUTTON
;
3717 case ui::AX_ROLE_TEXT_AREA
:
3718 ia_role_
= ROLE_SYSTEM_TEXT
;
3719 ia2_state_
|= IA2_STATE_MULTI_LINE
;
3720 ia2_state_
|= IA2_STATE_EDITABLE
;
3721 ia2_state_
|= IA2_STATE_SELECTABLE_TEXT
;
3723 case ui::AX_ROLE_TEXT_FIELD
:
3724 ia_role_
= ROLE_SYSTEM_TEXT
;
3725 ia2_state_
|= IA2_STATE_SINGLE_LINE
;
3726 ia2_state_
|= IA2_STATE_EDITABLE
;
3727 ia2_state_
|= IA2_STATE_SELECTABLE_TEXT
;
3729 case ui::AX_ROLE_TIMER
:
3730 ia_role_
= ROLE_SYSTEM_CLOCK
;
3731 ia_state_
|= STATE_SYSTEM_READONLY
;
3733 case ui::AX_ROLE_TOOLBAR
:
3734 ia_role_
= ROLE_SYSTEM_TOOLBAR
;
3735 ia_state_
|= STATE_SYSTEM_READONLY
;
3737 case ui::AX_ROLE_TOOLTIP
:
3738 ia_role_
= ROLE_SYSTEM_TOOLTIP
;
3739 ia_state_
|= STATE_SYSTEM_READONLY
;
3741 case ui::AX_ROLE_TREE
:
3742 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3743 ia_state_
|= STATE_SYSTEM_READONLY
;
3745 case ui::AX_ROLE_TREE_GRID
:
3746 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3747 ia_state_
|= STATE_SYSTEM_READONLY
;
3749 case ui::AX_ROLE_TREE_ITEM
:
3750 ia_role_
= ROLE_SYSTEM_OUTLINEITEM
;
3751 ia_state_
|= STATE_SYSTEM_READONLY
;
3753 case ui::AX_ROLE_WINDOW
:
3754 ia_role_
= ROLE_SYSTEM_WINDOW
;
3757 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3758 case ui::AX_ROLE_BROWSER
:
3759 case ui::AX_ROLE_DIRECTORY
:
3760 case ui::AX_ROLE_DRAWER
:
3761 case ui::AX_ROLE_HELP_TAG
:
3762 case ui::AX_ROLE_IGNORED
:
3763 case ui::AX_ROLE_INCREMENTOR
:
3764 case ui::AX_ROLE_LOG
:
3765 case ui::AX_ROLE_MARQUEE
:
3766 case ui::AX_ROLE_MATTE
:
3767 case ui::AX_ROLE_PRESENTATIONAL
:
3768 case ui::AX_ROLE_RULER_MARKER
:
3769 case ui::AX_ROLE_SHEET
:
3770 case ui::AX_ROLE_SLIDER_THUMB
:
3771 case ui::AX_ROLE_SYSTEM_WIDE
:
3772 case ui::AX_ROLE_VALUE_INDICATOR
:
3774 ia_role_
= ROLE_SYSTEM_CLIENT
;
3778 // Compute the final value of READONLY for MSAA.
3780 // We always set the READONLY state for elements that have the
3781 // aria-readonly attribute and for a few roles (in the switch above).
3782 // We clear the READONLY state on focusable controls and on a document.
3783 // Everything else, the majority of objects, do not have this state set.
3784 if (HasState(ui::AX_STATE_FOCUSABLE
) &&
3785 ia_role_
!= ROLE_SYSTEM_DOCUMENT
) {
3786 ia_state_
&= ~(STATE_SYSTEM_READONLY
);
3788 if (!HasState(ui::AX_STATE_READ_ONLY
))
3789 ia_state_
&= ~(STATE_SYSTEM_READONLY
);
3790 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY
))
3791 ia_state_
|= STATE_SYSTEM_READONLY
;
3793 // The role should always be set.
3794 DCHECK(!role_name_
.empty() || ia_role_
);
3796 // If we didn't explicitly set the IAccessible2 role, make it the same
3797 // as the MSAA role.
3799 ia2_role_
= ia_role_
;
3802 } // namespace content