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::WinAttributes
181 BrowserAccessibilityWin::WinAttributes::WinAttributes()
189 // BrowserAccessibilityWin
193 BrowserAccessibility
* BrowserAccessibility::Create() {
194 ui::win::CreateATLModuleIfNeeded();
195 CComObject
<BrowserAccessibilityWin
>* instance
;
196 HRESULT hr
= CComObject
<BrowserAccessibilityWin
>::CreateInstance(&instance
);
197 DCHECK(SUCCEEDED(hr
));
198 return instance
->NewReference();
201 BrowserAccessibilityWin
* BrowserAccessibility::ToBrowserAccessibilityWin() {
202 return static_cast<BrowserAccessibilityWin
*>(this);
205 BrowserAccessibilityWin::BrowserAccessibilityWin()
206 : win_attributes_(new WinAttributes()),
207 previous_scroll_x_(0),
208 previous_scroll_y_(0) {
209 // Start unique IDs at -1 and decrement each time, because get_accChild
210 // uses positive IDs to enumerate children, so we use negative IDs to
211 // clearly distinguish between indices and unique IDs.
212 unique_id_win_
= next_unique_id_win_
;
213 if (next_unique_id_win_
==
214 base::win::kLastBrowserAccessibilityManagerAccessibilityId
) {
215 next_unique_id_win_
=
216 base::win::kFirstBrowserAccessibilityManagerAccessibilityId
;
218 next_unique_id_win_
--;
221 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
222 for (size_t i
= 0; i
< relations_
.size(); ++i
)
223 relations_
[i
]->Release();
227 // IAccessible methods.
230 // * Always test for instance_active() first and return E_FAIL if it's false.
231 // * Always check for invalid arguments first, even if they're unused.
232 // * Return S_FALSE if the only output is a string argument and it's empty.
235 HRESULT
BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id
) {
236 if (!instance_active())
239 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
243 manager()->DoDefaultAction(*target
);
247 STDMETHODIMP
BrowserAccessibilityWin::accHitTest(LONG x_left
,
250 if (!instance_active())
256 gfx::Point
point(x_left
, y_top
);
257 if (!GetGlobalBoundsRect().Contains(point
)) {
258 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
259 child
->vt
= VT_EMPTY
;
263 BrowserAccessibility
* result
= BrowserAccessibilityForPoint(point
);
264 if (result
== this) {
265 // Point is within this object.
267 child
->lVal
= CHILDID_SELF
;
269 child
->vt
= VT_DISPATCH
;
270 child
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
275 STDMETHODIMP
BrowserAccessibilityWin::accLocation(LONG
* x_left
,
280 if (!instance_active())
283 if (!x_left
|| !y_top
|| !width
|| !height
)
286 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
290 gfx::Rect bounds
= target
->GetGlobalBoundsRect();
291 *x_left
= bounds
.x();
293 *width
= bounds
.width();
294 *height
= bounds
.height();
299 STDMETHODIMP
BrowserAccessibilityWin::accNavigate(LONG nav_dir
,
302 BrowserAccessibilityWin
* target
= GetTargetFromChildID(start
);
306 if ((nav_dir
== NAVDIR_LASTCHILD
|| nav_dir
== NAVDIR_FIRSTCHILD
) &&
307 start
.lVal
!= CHILDID_SELF
) {
308 // MSAA states that navigating to first/last child can only be from self.
312 uint32 child_count
= target
->PlatformChildCount();
314 BrowserAccessibility
* result
= NULL
;
320 // These directions are not implemented, matching Mozilla and IE.
322 case NAVDIR_FIRSTCHILD
:
324 result
= target
->PlatformGetChild(0);
326 case NAVDIR_LASTCHILD
:
328 result
= target
->PlatformGetChild(child_count
- 1);
331 result
= target
->GetNextSibling();
333 case NAVDIR_PREVIOUS
:
334 result
= target
->GetPreviousSibling();
343 end
->vt
= VT_DISPATCH
;
344 end
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
348 STDMETHODIMP
BrowserAccessibilityWin::get_accChild(VARIANT var_child
,
349 IDispatch
** disp_child
) {
350 if (!instance_active())
358 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_child
);
362 (*disp_child
) = target
->NewReference();
366 STDMETHODIMP
BrowserAccessibilityWin::get_accChildCount(LONG
* child_count
) {
367 if (!instance_active())
373 *child_count
= PlatformChildCount();
378 STDMETHODIMP
BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id
,
380 if (!instance_active())
386 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
390 return target
->GetStringAttributeAsBstr(
391 ui::AX_ATTR_ACTION
, def_action
);
394 STDMETHODIMP
BrowserAccessibilityWin::get_accDescription(VARIANT var_id
,
396 if (!instance_active())
402 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
406 base::string16 description_str
= target
->description();
407 if (description_str
.empty())
410 *desc
= SysAllocString(description_str
.c_str());
416 STDMETHODIMP
BrowserAccessibilityWin::get_accFocus(VARIANT
* focus_child
) {
417 if (!instance_active())
423 BrowserAccessibilityWin
* focus
= static_cast<BrowserAccessibilityWin
*>(
424 manager()->GetFocus(this));
426 focus_child
->vt
= VT_I4
;
427 focus_child
->lVal
= CHILDID_SELF
;
428 } else if (focus
== NULL
) {
429 focus_child
->vt
= VT_EMPTY
;
431 focus_child
->vt
= VT_DISPATCH
;
432 focus_child
->pdispVal
= focus
->NewReference();
438 STDMETHODIMP
BrowserAccessibilityWin::get_accHelp(VARIANT var_id
, BSTR
* help
) {
439 if (!instance_active())
445 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
449 base::string16 help_str
= target
->help();
450 if (help_str
.empty())
453 *help
= SysAllocString(help_str
.c_str());
459 STDMETHODIMP
BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id
,
461 if (!instance_active())
467 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
471 return target
->GetStringAttributeAsBstr(
472 ui::AX_ATTR_SHORTCUT
, acc_key
);
475 STDMETHODIMP
BrowserAccessibilityWin::get_accName(VARIANT var_id
, BSTR
* name
) {
476 if (!instance_active())
482 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
486 base::string16 name_str
= target
->name();
488 // If the name is empty, see if it's labeled by another element.
489 if (name_str
.empty()) {
491 if (target
->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT
,
493 BrowserAccessibilityWin
* title_elem
=
494 manager()->GetFromID(title_elem_id
)->ToBrowserAccessibilityWin();
496 name_str
= title_elem
->GetNameRecursive();
500 if (name_str
.empty())
503 *name
= SysAllocString(name_str
.c_str());
509 STDMETHODIMP
BrowserAccessibilityWin::get_accParent(IDispatch
** disp_parent
) {
510 if (!instance_active())
516 IAccessible
* parent_obj
= GetParent()->ToBrowserAccessibilityWin();
517 if (parent_obj
== NULL
) {
518 // This happens if we're the root of the tree;
519 // return the IAccessible for the window.
521 manager()->ToBrowserAccessibilityManagerWin()->GetParentIAccessible();
522 // |parent| can only be NULL if the manager was created before the parent
523 // IAccessible was known and it wasn't subsequently set before a client
524 // requested it. This has been fixed. |parent| may also be NULL during
525 // destruction. Possible cases where this could occur include tabs being
526 // dragged to a new window, etc.
528 DVLOG(1) << "In Function: "
530 << ". Parent IAccessible interface is NULL. Returning failure";
534 parent_obj
->AddRef();
535 *disp_parent
= parent_obj
;
539 STDMETHODIMP
BrowserAccessibilityWin::get_accRole(VARIANT var_id
,
541 if (!instance_active())
547 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
551 if (!target
->role_name().empty()) {
553 role
->bstrVal
= SysAllocString(target
->role_name().c_str());
556 role
->lVal
= target
->ia_role();
561 STDMETHODIMP
BrowserAccessibilityWin::get_accState(VARIANT var_id
,
563 if (!instance_active())
569 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
574 state
->lVal
= target
->ia_state();
575 if (manager()->GetFocus(NULL
) == this)
576 state
->lVal
|= STATE_SYSTEM_FOCUSED
;
581 STDMETHODIMP
BrowserAccessibilityWin::get_accValue(VARIANT var_id
,
583 if (!instance_active())
589 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
593 if (target
->ia_role() == ROLE_SYSTEM_PROGRESSBAR
||
594 target
->ia_role() == ROLE_SYSTEM_SCROLLBAR
||
595 target
->ia_role() == ROLE_SYSTEM_SLIDER
) {
596 base::string16 value_text
= target
->GetValueText();
597 *value
= SysAllocString(value_text
.c_str());
602 // Expose color well value.
603 if (target
->ia2_role() == IA2_ROLE_COLOR_CHOOSER
) {
604 int r
= target
->GetIntAttribute(
605 ui::AX_ATTR_COLOR_VALUE_RED
);
606 int g
= target
->GetIntAttribute(
607 ui::AX_ATTR_COLOR_VALUE_GREEN
);
608 int b
= target
->GetIntAttribute(
609 ui::AX_ATTR_COLOR_VALUE_BLUE
);
610 base::string16 value_text
;
611 value_text
= base::IntToString16((r
* 100) / 255) + L
"% red " +
612 base::IntToString16((g
* 100) / 255) + L
"% green " +
613 base::IntToString16((b
* 100) / 255) + L
"% blue";
614 *value
= SysAllocString(value_text
.c_str());
619 *value
= SysAllocString(target
->value().c_str());
624 STDMETHODIMP
BrowserAccessibilityWin::get_accHelpTopic(BSTR
* help_file
,
630 STDMETHODIMP
BrowserAccessibilityWin::get_accSelection(VARIANT
* selected
) {
631 if (!instance_active())
634 if (GetRole() != ui::AX_ROLE_LIST_BOX
)
637 unsigned long selected_count
= 0;
638 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
639 if (InternalGetChild(i
)->HasState(ui::AX_STATE_SELECTED
))
643 if (selected_count
== 0) {
644 selected
->vt
= VT_EMPTY
;
648 if (selected_count
== 1) {
649 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
650 if (InternalGetChild(i
)->HasState(ui::AX_STATE_SELECTED
)) {
651 selected
->vt
= VT_DISPATCH
;
653 InternalGetChild(i
)->ToBrowserAccessibilityWin()->NewReference();
659 // Multiple items are selected.
660 base::win::EnumVariant
* enum_variant
=
661 new base::win::EnumVariant(selected_count
);
662 enum_variant
->AddRef();
663 unsigned long index
= 0;
664 for (size_t i
= 0; i
< InternalChildCount(); ++i
) {
665 if (InternalGetChild(i
)->HasState(ui::AX_STATE_SELECTED
)) {
666 enum_variant
->ItemAt(index
)->vt
= VT_DISPATCH
;
667 enum_variant
->ItemAt(index
)->pdispVal
=
668 InternalGetChild(i
)->ToBrowserAccessibilityWin()->NewReference();
672 selected
->vt
= VT_UNKNOWN
;
673 selected
->punkVal
= static_cast<IUnknown
*>(
674 static_cast<base::win::IUnknownImpl
*>(enum_variant
));
678 STDMETHODIMP
BrowserAccessibilityWin::accSelect(
679 LONG flags_sel
, VARIANT var_id
) {
680 if (!instance_active())
683 if (flags_sel
& SELFLAG_TAKEFOCUS
) {
684 manager()->SetFocus(this, true);
692 // IAccessible2 methods.
695 STDMETHODIMP
BrowserAccessibilityWin::role(LONG
* role
) {
696 if (!instance_active())
707 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(BSTR
* attributes
) {
708 if (!instance_active())
714 // The iaccessible2 attributes are a set of key-value pairs
715 // separated by semicolons, with a colon between the key and the value.
717 const std::vector
<base::string16
>& attributes_list
= ia2_attributes();
718 for (unsigned int i
= 0; i
< attributes_list
.size(); ++i
) {
719 str
+= attributes_list
[i
] + L
';';
725 *attributes
= SysAllocString(str
.c_str());
730 STDMETHODIMP
BrowserAccessibilityWin::get_states(AccessibleStates
* states
) {
731 if (!instance_active())
737 *states
= ia2_state();
742 STDMETHODIMP
BrowserAccessibilityWin::get_uniqueID(LONG
* unique_id
) {
743 if (!instance_active())
749 *unique_id
= unique_id_win_
;
753 STDMETHODIMP
BrowserAccessibilityWin::get_windowHandle(HWND
* window_handle
) {
754 if (!instance_active())
761 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
768 STDMETHODIMP
BrowserAccessibilityWin::get_indexInParent(LONG
* index_in_parent
) {
769 if (!instance_active())
772 if (!index_in_parent
)
775 *index_in_parent
= this->GetIndexInParent();
779 STDMETHODIMP
BrowserAccessibilityWin::get_nRelations(LONG
* n_relations
) {
780 if (!instance_active())
786 *n_relations
= relations_
.size();
790 STDMETHODIMP
BrowserAccessibilityWin::get_relation(
792 IAccessibleRelation
** relation
) {
793 if (!instance_active())
796 if (relation_index
< 0 ||
797 relation_index
>= static_cast<long>(relations_
.size())) {
804 relations_
[relation_index
]->AddRef();
805 *relation
= relations_
[relation_index
];
809 STDMETHODIMP
BrowserAccessibilityWin::get_relations(
811 IAccessibleRelation
** relations
,
813 if (!instance_active())
816 if (!relations
|| !n_relations
)
819 long count
= static_cast<long>(relations_
.size());
820 *n_relations
= count
;
824 for (long i
= 0; i
< count
; ++i
) {
825 relations_
[i
]->AddRef();
826 relations
[i
] = relations_
[i
];
832 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type
) {
833 if (!instance_active())
836 gfx::Rect r
= GetLocation();
837 switch(scroll_type
) {
838 case IA2_SCROLL_TYPE_TOP_LEFT
:
839 manager()->ScrollToMakeVisible(*this, gfx::Rect(r
.x(), r
.y(), 0, 0));
841 case IA2_SCROLL_TYPE_BOTTOM_RIGHT
:
842 manager()->ScrollToMakeVisible(
843 *this, gfx::Rect(r
.right(), r
.bottom(), 0, 0));
845 case IA2_SCROLL_TYPE_TOP_EDGE
:
846 manager()->ScrollToMakeVisible(
847 *this, gfx::Rect(r
.x(), r
.y(), r
.width(), 0));
849 case IA2_SCROLL_TYPE_BOTTOM_EDGE
:
850 manager()->ScrollToMakeVisible(
851 *this, gfx::Rect(r
.x(), r
.bottom(), r
.width(), 0));
853 case IA2_SCROLL_TYPE_LEFT_EDGE
:
854 manager()->ScrollToMakeVisible(
855 *this, gfx::Rect(r
.x(), r
.y(), 0, r
.height()));
857 case IA2_SCROLL_TYPE_RIGHT_EDGE
:
858 manager()->ScrollToMakeVisible(
859 *this, gfx::Rect(r
.right(), r
.y(), 0, r
.height()));
861 case IA2_SCROLL_TYPE_ANYWHERE
:
863 manager()->ScrollToMakeVisible(*this, r
);
867 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
872 STDMETHODIMP
BrowserAccessibilityWin::scrollToPoint(
873 enum IA2CoordinateType coordinate_type
,
876 if (!instance_active())
879 gfx::Point
scroll_to(x
, y
);
881 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
882 scroll_to
-= manager()->GetViewBounds().OffsetFromOrigin();
883 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
885 scroll_to
+= GetParent()->GetLocation().OffsetFromOrigin();
890 manager()->ScrollToPoint(*this, scroll_to
);
891 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
896 STDMETHODIMP
BrowserAccessibilityWin::get_groupPosition(
898 LONG
* similar_items_in_group
,
899 LONG
* position_in_group
) {
900 if (!instance_active())
903 if (!group_level
|| !similar_items_in_group
|| !position_in_group
)
906 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
908 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX
) {
910 *similar_items_in_group
= GetParent()->PlatformChildCount();
911 *position_in_group
= GetIndexInParent() + 1;
919 // IAccessibleApplication methods.
922 STDMETHODIMP
BrowserAccessibilityWin::get_appName(BSTR
* app_name
) {
923 // No need to check |instance_active()| because this interface is
924 // global, and doesn't depend on any local state.
929 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
930 // the part before the "/".
931 std::vector
<std::string
> product_components
;
932 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
933 DCHECK_EQ(2U, product_components
.size());
934 if (product_components
.size() != 2)
936 *app_name
= SysAllocString(base::UTF8ToUTF16(product_components
[0]).c_str());
938 return *app_name
? S_OK
: E_FAIL
;
941 STDMETHODIMP
BrowserAccessibilityWin::get_appVersion(BSTR
* app_version
) {
942 // No need to check |instance_active()| because this interface is
943 // global, and doesn't depend on any local state.
948 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
949 // the part after the "/".
950 std::vector
<std::string
> product_components
;
951 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
952 DCHECK_EQ(2U, product_components
.size());
953 if (product_components
.size() != 2)
956 SysAllocString(base::UTF8ToUTF16(product_components
[1]).c_str());
957 DCHECK(*app_version
);
958 return *app_version
? S_OK
: E_FAIL
;
961 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitName(BSTR
* toolkit_name
) {
962 // No need to check |instance_active()| because this interface is
963 // global, and doesn't depend on any local state.
968 // This is hard-coded; all products based on the Chromium engine
969 // will have the same toolkit name, so that assistive technology can
970 // detect any Chrome-based product.
971 *toolkit_name
= SysAllocString(L
"Chrome");
972 DCHECK(*toolkit_name
);
973 return *toolkit_name
? S_OK
: E_FAIL
;
976 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitVersion(
977 BSTR
* toolkit_version
) {
978 // No need to check |instance_active()| because this interface is
979 // global, and doesn't depend on any local state.
981 if (!toolkit_version
)
984 std::string user_agent
= GetContentClient()->GetUserAgent();
985 *toolkit_version
= SysAllocString(base::UTF8ToUTF16(user_agent
).c_str());
986 DCHECK(*toolkit_version
);
987 return *toolkit_version
? S_OK
: E_FAIL
;
991 // IAccessibleImage methods.
994 STDMETHODIMP
BrowserAccessibilityWin::get_description(BSTR
* desc
) {
995 if (!instance_active())
1001 if (description().empty())
1004 *desc
= SysAllocString(description().c_str());
1010 STDMETHODIMP
BrowserAccessibilityWin::get_imagePosition(
1011 enum IA2CoordinateType coordinate_type
,
1014 if (!instance_active())
1018 return E_INVALIDARG
;
1020 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
1022 manager()->ToBrowserAccessibilityManagerWin()->GetParentHWND();
1025 POINT top_left
= {0, 0};
1026 ::ClientToScreen(parent_hwnd
, &top_left
);
1027 *x
= GetLocation().x() + top_left
.x
;
1028 *y
= GetLocation().y() + top_left
.y
;
1029 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
1030 *x
= GetLocation().x();
1031 *y
= GetLocation().y();
1033 *x
-= GetParent()->GetLocation().x();
1034 *y
-= GetParent()->GetLocation().y();
1037 return E_INVALIDARG
;
1043 STDMETHODIMP
BrowserAccessibilityWin::get_imageSize(LONG
* height
, LONG
* width
) {
1044 if (!instance_active())
1047 if (!height
|| !width
)
1048 return E_INVALIDARG
;
1050 *height
= GetLocation().height();
1051 *width
= GetLocation().width();
1056 // IAccessibleTable methods.
1059 STDMETHODIMP
BrowserAccessibilityWin::get_accessibleAt(
1062 IUnknown
** accessible
) {
1063 if (!instance_active())
1067 return E_INVALIDARG
;
1071 if (!GetIntAttribute(
1072 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1074 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1080 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1081 return E_INVALIDARG
;
1083 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1084 ui::AX_ATTR_CELL_IDS
);
1085 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1087 int cell_id
= cell_ids
[row
* columns
+ column
];
1088 BrowserAccessibilityWin
* cell
= GetFromID(cell_id
);
1090 *accessible
= static_cast<IAccessible
*>(cell
->NewReference());
1095 return E_INVALIDARG
;
1098 STDMETHODIMP
BrowserAccessibilityWin::get_caption(IUnknown
** accessible
) {
1099 if (!instance_active())
1103 return E_INVALIDARG
;
1105 // TODO(dmazzoni): implement
1109 STDMETHODIMP
BrowserAccessibilityWin::get_childIndex(long row
,
1112 if (!instance_active())
1116 return E_INVALIDARG
;
1120 if (!GetIntAttribute(
1121 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1123 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1129 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1130 return E_INVALIDARG
;
1132 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1133 ui::AX_ATTR_CELL_IDS
);
1134 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1135 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1136 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1137 int cell_id
= cell_ids
[row
* columns
+ column
];
1138 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
1139 if (unique_cell_ids
[i
] == cell_id
) {
1140 *cell_index
= (long)i
;
1148 STDMETHODIMP
BrowserAccessibilityWin::get_columnDescription(long column
,
1149 BSTR
* description
) {
1150 if (!instance_active())
1154 return E_INVALIDARG
;
1158 if (!GetIntAttribute(
1159 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1160 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1166 if (column
< 0 || column
>= columns
)
1167 return E_INVALIDARG
;
1169 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1170 ui::AX_ATTR_CELL_IDS
);
1171 for (int i
= 0; i
< rows
; ++i
) {
1172 int cell_id
= cell_ids
[i
* columns
+ column
];
1173 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1174 manager()->GetFromID(cell_id
));
1175 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
) {
1176 base::string16 cell_name
= cell
->GetString16Attribute(
1178 if (cell_name
.size() > 0) {
1179 *description
= SysAllocString(cell_name
.c_str());
1183 if (cell
->description().size() > 0) {
1184 *description
= SysAllocString(cell
->description().c_str());
1193 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtentAt(
1196 long* n_columns_spanned
) {
1197 if (!instance_active())
1200 if (!n_columns_spanned
)
1201 return E_INVALIDARG
;
1205 if (!GetIntAttribute(
1206 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1207 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1213 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1214 return E_INVALIDARG
;
1216 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1217 ui::AX_ATTR_CELL_IDS
);
1218 int cell_id
= cell_ids
[row
* columns
+ column
];
1219 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1220 manager()->GetFromID(cell_id
));
1223 cell
->GetIntAttribute(
1224 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1226 *n_columns_spanned
= colspan
;
1233 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeader(
1234 IAccessibleTable
** accessible_table
,
1235 long* starting_row_index
) {
1236 // TODO(dmazzoni): implement
1240 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long cell_index
,
1241 long* column_index
) {
1242 if (!instance_active())
1246 return E_INVALIDARG
;
1248 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1249 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1250 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1252 return E_INVALIDARG
;
1253 if (cell_index
>= cell_id_count
)
1256 int cell_id
= unique_cell_ids
[cell_index
];
1257 BrowserAccessibilityWin
* cell
=
1258 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1261 cell
->GetIntAttribute(
1262 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &col_index
)) {
1263 *column_index
= col_index
;
1270 STDMETHODIMP
BrowserAccessibilityWin::get_nColumns(long* column_count
) {
1271 if (!instance_active())
1275 return E_INVALIDARG
;
1278 if (GetIntAttribute(
1279 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
)) {
1280 *column_count
= columns
;
1287 STDMETHODIMP
BrowserAccessibilityWin::get_nRows(long* row_count
) {
1288 if (!instance_active())
1292 return E_INVALIDARG
;
1295 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1303 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count
) {
1304 if (!instance_active())
1308 return E_INVALIDARG
;
1310 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1315 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedColumns(long* column_count
) {
1316 if (!instance_active())
1320 return E_INVALIDARG
;
1326 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedRows(long* row_count
) {
1327 if (!instance_active())
1331 return E_INVALIDARG
;
1337 STDMETHODIMP
BrowserAccessibilityWin::get_rowDescription(long row
,
1338 BSTR
* description
) {
1339 if (!instance_active())
1343 return E_INVALIDARG
;
1347 if (!GetIntAttribute(
1348 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1349 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1355 if (row
< 0 || row
>= rows
)
1356 return E_INVALIDARG
;
1358 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1359 ui::AX_ATTR_CELL_IDS
);
1360 for (int i
= 0; i
< columns
; ++i
) {
1361 int cell_id
= cell_ids
[row
* columns
+ i
];
1362 BrowserAccessibilityWin
* cell
=
1363 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1364 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
) {
1365 base::string16 cell_name
= cell
->GetString16Attribute(
1367 if (cell_name
.size() > 0) {
1368 *description
= SysAllocString(cell_name
.c_str());
1372 if (cell
->description().size() > 0) {
1373 *description
= SysAllocString(cell
->description().c_str());
1382 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtentAt(long row
,
1384 long* n_rows_spanned
) {
1385 if (!instance_active())
1388 if (!n_rows_spanned
)
1389 return E_INVALIDARG
;
1393 if (!GetIntAttribute(
1394 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1395 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1401 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1402 return E_INVALIDARG
;
1404 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1405 ui::AX_ATTR_CELL_IDS
);
1406 int cell_id
= cell_ids
[row
* columns
+ column
];
1407 BrowserAccessibilityWin
* cell
=
1408 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1411 cell
->GetIntAttribute(
1412 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1414 *n_rows_spanned
= rowspan
;
1421 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeader(
1422 IAccessibleTable
** accessible_table
,
1423 long* starting_column_index
) {
1424 // TODO(dmazzoni): implement
1428 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long cell_index
,
1430 if (!instance_active())
1434 return E_INVALIDARG
;
1436 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1437 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1438 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1440 return E_INVALIDARG
;
1441 if (cell_index
>= cell_id_count
)
1444 int cell_id
= unique_cell_ids
[cell_index
];
1445 BrowserAccessibilityWin
* cell
=
1446 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1449 cell
->GetIntAttribute(
1450 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &cell_row_index
)) {
1451 *row_index
= cell_row_index
;
1458 STDMETHODIMP
BrowserAccessibilityWin::get_selectedChildren(long max_children
,
1461 if (!instance_active())
1464 if (!children
|| !n_children
)
1465 return E_INVALIDARG
;
1467 // TODO(dmazzoni): Implement this.
1472 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long max_columns
,
1475 if (!instance_active())
1478 if (!columns
|| !n_columns
)
1479 return E_INVALIDARG
;
1481 // TODO(dmazzoni): Implement this.
1486 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long max_rows
,
1489 if (!instance_active())
1492 if (!rows
|| !n_rows
)
1493 return E_INVALIDARG
;
1495 // TODO(dmazzoni): Implement this.
1500 STDMETHODIMP
BrowserAccessibilityWin::get_summary(IUnknown
** accessible
) {
1501 if (!instance_active())
1505 return E_INVALIDARG
;
1507 // TODO(dmazzoni): implement
1511 STDMETHODIMP
BrowserAccessibilityWin::get_isColumnSelected(
1513 boolean
* is_selected
) {
1514 if (!instance_active())
1518 return E_INVALIDARG
;
1520 // TODO(dmazzoni): Implement this.
1521 *is_selected
= false;
1525 STDMETHODIMP
BrowserAccessibilityWin::get_isRowSelected(long row
,
1526 boolean
* is_selected
) {
1527 if (!instance_active())
1531 return E_INVALIDARG
;
1533 // TODO(dmazzoni): Implement this.
1534 *is_selected
= false;
1538 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(long row
,
1540 boolean
* is_selected
) {
1541 if (!instance_active())
1545 return E_INVALIDARG
;
1547 // TODO(dmazzoni): Implement this.
1548 *is_selected
= false;
1552 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1557 long* column_extents
,
1558 boolean
* is_selected
) {
1559 if (!instance_active())
1562 if (!row
|| !column
|| !row_extents
|| !column_extents
|| !is_selected
)
1563 return E_INVALIDARG
;
1565 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1566 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1567 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1569 return E_INVALIDARG
;
1570 if (index
>= cell_id_count
)
1573 int cell_id
= unique_cell_ids
[index
];
1574 BrowserAccessibilityWin
* cell
=
1575 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1579 cell
->GetIntAttribute(
1580 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1581 cell
->GetIntAttribute(
1582 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1585 *row_extents
= rowspan
;
1586 *column_extents
= colspan
;
1594 // IAccessibleTable2 methods.
1597 STDMETHODIMP
BrowserAccessibilityWin::get_cellAt(long row
,
1600 return get_accessibleAt(row
, column
, cell
);
1603 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedCells(long* cell_count
) {
1604 return get_nSelectedChildren(cell_count
);
1607 STDMETHODIMP
BrowserAccessibilityWin::get_selectedCells(
1609 long* n_selected_cells
) {
1610 if (!instance_active())
1613 if (!cells
|| !n_selected_cells
)
1614 return E_INVALIDARG
;
1616 // TODO(dmazzoni): Implement this.
1617 *n_selected_cells
= 0;
1621 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long** columns
,
1623 if (!instance_active())
1626 if (!columns
|| !n_columns
)
1627 return E_INVALIDARG
;
1629 // TODO(dmazzoni): Implement this.
1634 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long** rows
,
1636 if (!instance_active())
1639 if (!rows
|| !n_rows
)
1640 return E_INVALIDARG
;
1642 // TODO(dmazzoni): Implement this.
1649 // IAccessibleTableCell methods.
1652 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtent(
1653 long* n_columns_spanned
) {
1654 if (!instance_active())
1657 if (!n_columns_spanned
)
1658 return E_INVALIDARG
;
1661 if (GetIntAttribute(
1662 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1664 *n_columns_spanned
= colspan
;
1671 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeaderCells(
1672 IUnknown
*** cell_accessibles
,
1673 long* n_column_header_cells
) {
1674 if (!instance_active())
1677 if (!cell_accessibles
|| !n_column_header_cells
)
1678 return E_INVALIDARG
;
1680 *n_column_header_cells
= 0;
1683 if (!GetIntAttribute(
1684 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1688 BrowserAccessibility
* table
= GetParent();
1689 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
1690 table
= table
->GetParent();
1698 if (!table
->GetIntAttribute(
1699 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1700 !table
->GetIntAttribute(
1701 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1704 if (columns
<= 0 || rows
<= 0 || column
< 0 || column
>= columns
)
1707 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1708 ui::AX_ATTR_CELL_IDS
);
1710 for (int i
= 0; i
< rows
; ++i
) {
1711 int cell_id
= cell_ids
[i
* columns
+ column
];
1712 BrowserAccessibilityWin
* cell
=
1713 manager()->GetFromID(cell_id
)->ToBrowserAccessibilityWin();
1714 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
)
1715 (*n_column_header_cells
)++;
1718 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1719 (*n_column_header_cells
) * sizeof(cell_accessibles
[0])));
1721 for (int i
= 0; i
< rows
; ++i
) {
1722 int cell_id
= cell_ids
[i
* columns
+ column
];
1723 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1724 if (cell
&& cell
->GetRole() == ui::AX_ROLE_COLUMN_HEADER
) {
1725 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1726 cell
->ToBrowserAccessibilityWin()->NewReference());
1734 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long* column_index
) {
1735 if (!instance_active())
1739 return E_INVALIDARG
;
1742 if (GetIntAttribute(
1743 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1744 *column_index
= column
;
1751 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned
) {
1752 if (!instance_active())
1755 if (!n_rows_spanned
)
1756 return E_INVALIDARG
;
1759 if (GetIntAttribute(
1760 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1762 *n_rows_spanned
= rowspan
;
1769 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeaderCells(
1770 IUnknown
*** cell_accessibles
,
1771 long* n_row_header_cells
) {
1772 if (!instance_active())
1775 if (!cell_accessibles
|| !n_row_header_cells
)
1776 return E_INVALIDARG
;
1778 *n_row_header_cells
= 0;
1781 if (!GetIntAttribute(
1782 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1786 BrowserAccessibility
* table
= GetParent();
1787 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
1788 table
= table
->GetParent();
1796 if (!table
->GetIntAttribute(
1797 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1798 !table
->GetIntAttribute(
1799 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1802 if (columns
<= 0 || rows
<= 0 || row
< 0 || row
>= rows
)
1805 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1806 ui::AX_ATTR_CELL_IDS
);
1808 for (int i
= 0; i
< columns
; ++i
) {
1809 int cell_id
= cell_ids
[row
* columns
+ i
];
1810 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1811 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
)
1812 (*n_row_header_cells
)++;
1815 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1816 (*n_row_header_cells
) * sizeof(cell_accessibles
[0])));
1818 for (int i
= 0; i
< columns
; ++i
) {
1819 int cell_id
= cell_ids
[row
* columns
+ i
];
1820 BrowserAccessibility
* cell
= manager()->GetFromID(cell_id
);
1821 if (cell
&& cell
->GetRole() == ui::AX_ROLE_ROW_HEADER
) {
1822 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1823 cell
->ToBrowserAccessibilityWin()->NewReference());
1831 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long* row_index
) {
1832 if (!instance_active())
1836 return E_INVALIDARG
;
1839 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1846 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(boolean
* is_selected
) {
1847 if (!instance_active())
1851 return E_INVALIDARG
;
1853 *is_selected
= false;
1857 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtents(
1861 long* column_extents
,
1862 boolean
* is_selected
) {
1863 if (!instance_active())
1871 return E_INVALIDARG
;
1878 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
) &&
1880 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
) &&
1882 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1884 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
)) {
1886 *column_index
= column
;
1887 *row_extents
= rowspan
;
1888 *column_extents
= colspan
;
1889 *is_selected
= false;
1896 STDMETHODIMP
BrowserAccessibilityWin::get_table(IUnknown
** table
) {
1897 if (!instance_active())
1901 return E_INVALIDARG
;
1906 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
);
1907 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
);
1909 BrowserAccessibility
* find_table
= GetParent();
1910 while (find_table
&& find_table
->GetRole() != ui::AX_ROLE_TABLE
)
1911 find_table
= find_table
->GetParent();
1917 *table
= static_cast<IAccessibleTable
*>(
1918 find_table
->ToBrowserAccessibilityWin()->NewReference());
1924 // IAccessibleText methods.
1927 STDMETHODIMP
BrowserAccessibilityWin::get_nCharacters(LONG
* n_characters
) {
1928 if (!instance_active())
1932 return E_INVALIDARG
;
1934 *n_characters
= TextForIAccessibleText().length();
1938 STDMETHODIMP
BrowserAccessibilityWin::get_caretOffset(LONG
* offset
) {
1939 if (!instance_active())
1943 return E_INVALIDARG
;
1945 // IA2 spec says that caret offset should be -1 if the object is not focused.
1946 if (manager()->GetFocus(this) != this) {
1952 if (IsEditableText()) {
1954 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
1956 *offset
= sel_start
;
1962 STDMETHODIMP
BrowserAccessibilityWin::get_characterExtents(
1964 enum IA2CoordinateType coordinate_type
,
1969 if (!instance_active())
1972 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
1973 return E_INVALIDARG
;
1975 const base::string16
& text_str
= TextForIAccessibleText();
1976 HandleSpecialTextOffset(text_str
, &offset
);
1978 if (offset
< 0 || offset
> static_cast<LONG
>(text_str
.size()))
1979 return E_INVALIDARG
;
1981 gfx::Rect character_bounds
;
1982 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
1983 character_bounds
= GetGlobalBoundsForRange(offset
, 1);
1984 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
1985 character_bounds
= GetLocalBoundsForRange(offset
, 1);
1986 character_bounds
-= GetLocation().OffsetFromOrigin();
1988 return E_INVALIDARG
;
1991 *out_x
= character_bounds
.x();
1992 *out_y
= character_bounds
.y();
1993 *out_width
= character_bounds
.width();
1994 *out_height
= character_bounds
.height();
1999 STDMETHODIMP
BrowserAccessibilityWin::get_nSelections(LONG
* n_selections
) {
2000 if (!instance_active())
2004 return E_INVALIDARG
;
2007 if (IsEditableText()) {
2010 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
2012 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
) &&
2013 sel_start
!= sel_end
)
2020 STDMETHODIMP
BrowserAccessibilityWin::get_selection(LONG selection_index
,
2023 if (!instance_active())
2026 if (!start_offset
|| !end_offset
|| selection_index
!= 0)
2027 return E_INVALIDARG
;
2029 LONG n_selections
= 0;
2030 if (FAILED(get_nSelections(&n_selections
)) || n_selections
< 1)
2031 return E_INVALIDARG
;
2035 if (IsEditableText()) {
2038 if (GetIntAttribute(
2039 ui::AX_ATTR_TEXT_SEL_START
, &sel_start
) &&
2040 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
)) {
2041 *start_offset
= sel_start
;
2042 *end_offset
= sel_end
;
2049 STDMETHODIMP
BrowserAccessibilityWin::get_text(LONG start_offset
,
2052 if (!instance_active())
2056 return E_INVALIDARG
;
2058 const base::string16
& text_str
= TextForIAccessibleText();
2060 // Handle special text offsets.
2061 HandleSpecialTextOffset(text_str
, &start_offset
);
2062 HandleSpecialTextOffset(text_str
, &end_offset
);
2064 // The spec allows the arguments to be reversed.
2065 if (start_offset
> end_offset
) {
2066 LONG tmp
= start_offset
;
2067 start_offset
= end_offset
;
2071 // The spec does not allow the start or end offsets to be out or range;
2072 // we must return an error if so.
2073 LONG len
= text_str
.length();
2074 if (start_offset
< 0)
2075 return E_INVALIDARG
;
2076 if (end_offset
> len
)
2077 return E_INVALIDARG
;
2079 base::string16 substr
= text_str
.substr(start_offset
,
2080 end_offset
- start_offset
);
2084 *text
= SysAllocString(substr
.c_str());
2089 STDMETHODIMP
BrowserAccessibilityWin::get_textAtOffset(
2091 enum IA2TextBoundaryType boundary_type
,
2095 if (!instance_active())
2098 if (!start_offset
|| !end_offset
|| !text
)
2099 return E_INVALIDARG
;
2101 const base::string16
& text_str
= TextForIAccessibleText();
2102 HandleSpecialTextOffset(text_str
, &offset
);
2104 return E_INVALIDARG
;
2106 LONG text_len
= text_str
.length();
2107 if (offset
> text_len
)
2108 return E_INVALIDARG
;
2110 // The IAccessible2 spec says we don't have to implement the "sentence"
2111 // boundary type, we can just let the screenreader handle it.
2112 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2119 // According to the IA2 Spec, only line boundaries should succeed when
2120 // the offset is one past the end of the text.
2121 if (offset
== text_len
&& boundary_type
!= IA2_TEXT_BOUNDARY_LINE
) {
2128 *start_offset
= FindBoundary(
2129 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2130 *end_offset
= FindBoundary(
2131 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2132 return get_text(*start_offset
, *end_offset
, text
);
2135 STDMETHODIMP
BrowserAccessibilityWin::get_textBeforeOffset(
2137 enum IA2TextBoundaryType boundary_type
,
2141 if (!instance_active())
2144 if (!start_offset
|| !end_offset
|| !text
)
2145 return E_INVALIDARG
;
2147 // The IAccessible2 spec says we don't have to implement the "sentence"
2148 // boundary type, we can just let the screenreader handle it.
2149 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2156 const base::string16
& text_str
= TextForIAccessibleText();
2158 *start_offset
= FindBoundary(
2159 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2160 *end_offset
= offset
;
2161 return get_text(*start_offset
, *end_offset
, text
);
2164 STDMETHODIMP
BrowserAccessibilityWin::get_textAfterOffset(
2166 enum IA2TextBoundaryType boundary_type
,
2170 if (!instance_active())
2173 if (!start_offset
|| !end_offset
|| !text
)
2174 return E_INVALIDARG
;
2176 // The IAccessible2 spec says we don't have to implement the "sentence"
2177 // boundary type, we can just let the screenreader handle it.
2178 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2185 const base::string16
& text_str
= TextForIAccessibleText();
2187 *start_offset
= offset
;
2188 *end_offset
= FindBoundary(
2189 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2190 return get_text(*start_offset
, *end_offset
, text
);
2193 STDMETHODIMP
BrowserAccessibilityWin::get_newText(IA2TextSegment
* new_text
) {
2194 if (!instance_active())
2198 return E_INVALIDARG
;
2200 if (!old_win_attributes_
)
2203 int start
, old_len
, new_len
;
2204 ComputeHypertextRemovedAndInserted(&start
, &old_len
, &new_len
);
2208 base::string16 substr
= hypertext().substr(start
, new_len
);
2209 new_text
->text
= SysAllocString(substr
.c_str());
2210 new_text
->start
= static_cast<long>(start
);
2211 new_text
->end
= static_cast<long>(start
+ new_len
);
2215 STDMETHODIMP
BrowserAccessibilityWin::get_oldText(IA2TextSegment
* old_text
) {
2216 if (!instance_active())
2220 return E_INVALIDARG
;
2222 if (!old_win_attributes_
)
2225 int start
, old_len
, new_len
;
2226 ComputeHypertextRemovedAndInserted(&start
, &old_len
, &new_len
);
2230 base::string16 old_hypertext
= old_win_attributes_
->hypertext
;
2231 base::string16 substr
= old_hypertext
.substr(start
, old_len
);
2232 old_text
->text
= SysAllocString(substr
.c_str());
2233 old_text
->start
= static_cast<long>(start
);
2234 old_text
->end
= static_cast<long>(start
+ old_len
);
2238 STDMETHODIMP
BrowserAccessibilityWin::get_offsetAtPoint(
2241 enum IA2CoordinateType coord_type
,
2243 if (!instance_active())
2247 return E_INVALIDARG
;
2249 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2250 // screen readers still return partially accurate results rather than
2251 // completely failing.
2256 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringTo(
2259 enum IA2ScrollType scroll_type
) {
2260 // TODO(dmazzoni): adjust this for the start and end index, too.
2261 return scrollTo(scroll_type
);
2264 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringToPoint(
2267 enum IA2CoordinateType coordinate_type
,
2269 // TODO(dmazzoni): adjust this for the start and end index, too.
2270 return scrollToPoint(coordinate_type
, x
, y
);
2273 STDMETHODIMP
BrowserAccessibilityWin::addSelection(LONG start_offset
,
2275 if (!instance_active())
2278 const base::string16
& text_str
= TextForIAccessibleText();
2279 HandleSpecialTextOffset(text_str
, &start_offset
);
2280 HandleSpecialTextOffset(text_str
, &end_offset
);
2282 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2286 STDMETHODIMP
BrowserAccessibilityWin::removeSelection(LONG selection_index
) {
2287 if (!instance_active())
2290 if (selection_index
!= 0)
2291 return E_INVALIDARG
;
2293 manager()->SetTextSelection(*this, 0, 0);
2297 STDMETHODIMP
BrowserAccessibilityWin::setCaretOffset(LONG offset
) {
2298 if (!instance_active())
2301 const base::string16
& text_str
= TextForIAccessibleText();
2302 HandleSpecialTextOffset(text_str
, &offset
);
2303 manager()->SetTextSelection(*this, offset
, offset
);
2307 STDMETHODIMP
BrowserAccessibilityWin::setSelection(LONG selection_index
,
2310 if (!instance_active())
2313 if (selection_index
!= 0)
2314 return E_INVALIDARG
;
2316 const base::string16
& text_str
= TextForIAccessibleText();
2317 HandleSpecialTextOffset(text_str
, &start_offset
);
2318 HandleSpecialTextOffset(text_str
, &end_offset
);
2320 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2325 // IAccessibleHypertext methods.
2328 STDMETHODIMP
BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count
) {
2329 if (!instance_active())
2332 if (!hyperlink_count
)
2333 return E_INVALIDARG
;
2335 *hyperlink_count
= hyperlink_offset_to_index().size();
2339 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlink(
2341 IAccessibleHyperlink
** hyperlink
) {
2342 if (!instance_active())
2347 index
>= static_cast<long>(hyperlinks().size())) {
2348 return E_INVALIDARG
;
2351 int32 id
= hyperlinks()[index
];
2352 BrowserAccessibilityWin
* child
=
2353 manager()->GetFromID(id
)->ToBrowserAccessibilityWin();
2355 *hyperlink
= static_cast<IAccessibleHyperlink
*>(child
->NewReference());
2362 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlinkIndex(
2364 long* hyperlink_index
) {
2365 if (!instance_active())
2368 if (!hyperlink_index
)
2369 return E_INVALIDARG
;
2371 *hyperlink_index
= -1;
2373 if (char_index
< 0 ||
2374 char_index
>= static_cast<long>(hypertext().size())) {
2375 return E_INVALIDARG
;
2378 std::map
<int32
, int32
>::iterator it
=
2379 hyperlink_offset_to_index().find(char_index
);
2380 if (it
== hyperlink_offset_to_index().end())
2383 *hyperlink_index
= it
->second
;
2388 // IAccessibleValue methods.
2391 STDMETHODIMP
BrowserAccessibilityWin::get_currentValue(VARIANT
* value
) {
2392 if (!instance_active())
2396 return E_INVALIDARG
;
2399 if (GetFloatAttribute(
2400 ui::AX_ATTR_VALUE_FOR_RANGE
, &float_val
)) {
2402 value
->dblVal
= float_val
;
2406 value
->vt
= VT_EMPTY
;
2410 STDMETHODIMP
BrowserAccessibilityWin::get_minimumValue(VARIANT
* value
) {
2411 if (!instance_active())
2415 return E_INVALIDARG
;
2418 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE
,
2421 value
->dblVal
= float_val
;
2425 value
->vt
= VT_EMPTY
;
2429 STDMETHODIMP
BrowserAccessibilityWin::get_maximumValue(VARIANT
* value
) {
2430 if (!instance_active())
2434 return E_INVALIDARG
;
2437 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE
,
2440 value
->dblVal
= float_val
;
2444 value
->vt
= VT_EMPTY
;
2448 STDMETHODIMP
BrowserAccessibilityWin::setCurrentValue(VARIANT new_value
) {
2449 // TODO(dmazzoni): Implement this.
2454 // ISimpleDOMDocument methods.
2457 STDMETHODIMP
BrowserAccessibilityWin::get_URL(BSTR
* url
) {
2458 if (!instance_active())
2462 return E_INVALIDARG
;
2464 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL
, url
);
2467 STDMETHODIMP
BrowserAccessibilityWin::get_title(BSTR
* title
) {
2468 if (!instance_active())
2472 return E_INVALIDARG
;
2474 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE
, title
);
2477 STDMETHODIMP
BrowserAccessibilityWin::get_mimeType(BSTR
* mime_type
) {
2478 if (!instance_active())
2482 return E_INVALIDARG
;
2484 return GetStringAttributeAsBstr(
2485 ui::AX_ATTR_DOC_MIMETYPE
, mime_type
);
2488 STDMETHODIMP
BrowserAccessibilityWin::get_docType(BSTR
* doc_type
) {
2489 if (!instance_active())
2493 return E_INVALIDARG
;
2495 return GetStringAttributeAsBstr(
2496 ui::AX_ATTR_DOC_DOCTYPE
, doc_type
);
2500 // ISimpleDOMNode methods.
2503 STDMETHODIMP
BrowserAccessibilityWin::get_nodeInfo(
2505 short* name_space_id
,
2507 unsigned int* num_children
,
2508 unsigned int* unique_id
,
2509 unsigned short* node_type
) {
2510 if (!instance_active())
2513 if (!node_name
|| !name_space_id
|| !node_value
|| !num_children
||
2514 !unique_id
|| !node_type
) {
2515 return E_INVALIDARG
;
2519 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG
, &tag
))
2520 *node_name
= SysAllocString(tag
.c_str());
2525 *node_value
= SysAllocString(value().c_str());
2526 *num_children
= PlatformChildCount();
2527 *unique_id
= unique_id_win_
;
2529 if (ia_role() == ROLE_SYSTEM_DOCUMENT
) {
2530 *node_type
= NODETYPE_DOCUMENT
;
2531 } else if (ia_role() == ROLE_SYSTEM_TEXT
&&
2532 ((ia2_state() & IA2_STATE_EDITABLE
) == 0)) {
2533 *node_type
= NODETYPE_TEXT
;
2535 *node_type
= NODETYPE_ELEMENT
;
2541 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(
2542 unsigned short max_attribs
,
2544 short* name_space_id
,
2545 BSTR
* attrib_values
,
2546 unsigned short* num_attribs
) {
2547 if (!instance_active())
2550 if (!attrib_names
|| !name_space_id
|| !attrib_values
|| !num_attribs
)
2551 return E_INVALIDARG
;
2553 *num_attribs
= max_attribs
;
2554 if (*num_attribs
> GetHtmlAttributes().size())
2555 *num_attribs
= GetHtmlAttributes().size();
2557 for (unsigned short i
= 0; i
< *num_attribs
; ++i
) {
2558 attrib_names
[i
] = SysAllocString(
2559 base::UTF8ToUTF16(GetHtmlAttributes()[i
].first
).c_str());
2560 name_space_id
[i
] = 0;
2561 attrib_values
[i
] = SysAllocString(
2562 base::UTF8ToUTF16(GetHtmlAttributes()[i
].second
).c_str());
2567 STDMETHODIMP
BrowserAccessibilityWin::get_attributesForNames(
2568 unsigned short num_attribs
,
2570 short* name_space_id
,
2571 BSTR
* attrib_values
) {
2572 if (!instance_active())
2575 if (!attrib_names
|| !name_space_id
|| !attrib_values
)
2576 return E_INVALIDARG
;
2578 for (unsigned short i
= 0; i
< num_attribs
; ++i
) {
2579 name_space_id
[i
] = 0;
2581 std::string name
= base::UTF16ToUTF8((LPCWSTR
)attrib_names
[i
]);
2582 for (unsigned int j
= 0; j
< GetHtmlAttributes().size(); ++j
) {
2583 if (GetHtmlAttributes()[j
].first
== name
) {
2584 attrib_values
[i
] = SysAllocString(
2585 base::UTF8ToUTF16(GetHtmlAttributes()[j
].second
).c_str());
2591 attrib_values
[i
] = NULL
;
2597 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyle(
2598 unsigned short max_style_properties
,
2599 boolean use_alternate_view
,
2600 BSTR
* style_properties
,
2602 unsigned short *num_style_properties
) {
2603 if (!instance_active())
2606 if (!style_properties
|| !style_values
)
2607 return E_INVALIDARG
;
2609 // We only cache a single style property for now: DISPLAY
2611 base::string16 display
;
2612 if (max_style_properties
== 0 ||
2613 !GetString16Attribute(ui::AX_ATTR_DISPLAY
, &display
)) {
2614 *num_style_properties
= 0;
2618 *num_style_properties
= 1;
2619 style_properties
[0] = SysAllocString(L
"display");
2620 style_values
[0] = SysAllocString(display
.c_str());
2625 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyleForProperties(
2626 unsigned short num_style_properties
,
2627 boolean use_alternate_view
,
2628 BSTR
* style_properties
,
2629 BSTR
* style_values
) {
2630 if (!instance_active())
2633 if (!style_properties
|| !style_values
)
2634 return E_INVALIDARG
;
2636 // We only cache a single style property for now: DISPLAY
2638 for (unsigned short i
= 0; i
< num_style_properties
; ++i
) {
2639 base::string16 name
= (LPCWSTR
)style_properties
[i
];
2640 base::StringToLowerASCII(&name
);
2641 if (name
== L
"display") {
2642 base::string16 display
= GetString16Attribute(
2643 ui::AX_ATTR_DISPLAY
);
2644 style_values
[i
] = SysAllocString(display
.c_str());
2646 style_values
[i
] = NULL
;
2653 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(boolean placeTopLeft
) {
2654 return scrollTo(placeTopLeft
?
2655 IA2_SCROLL_TYPE_TOP_LEFT
: IA2_SCROLL_TYPE_ANYWHERE
);
2658 STDMETHODIMP
BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode
** node
) {
2659 if (!instance_active())
2663 return E_INVALIDARG
;
2665 *node
= GetParent()->ToBrowserAccessibilityWin()->NewReference();
2669 STDMETHODIMP
BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode
** node
) {
2670 if (!instance_active())
2674 return E_INVALIDARG
;
2676 if (PlatformChildCount() == 0) {
2681 *node
= PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2685 STDMETHODIMP
BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode
** node
) {
2686 if (!instance_active())
2690 return E_INVALIDARG
;
2692 if (PlatformChildCount() == 0) {
2697 *node
= PlatformGetChild(PlatformChildCount() - 1)
2698 ->ToBrowserAccessibilityWin()->NewReference();
2702 STDMETHODIMP
BrowserAccessibilityWin::get_previousSibling(
2703 ISimpleDOMNode
** node
) {
2704 if (!instance_active())
2708 return E_INVALIDARG
;
2710 if (!GetParent() || GetIndexInParent() <= 0) {
2715 *node
= GetParent()->InternalGetChild(GetIndexInParent() - 1)->
2716 ToBrowserAccessibilityWin()->NewReference();
2720 STDMETHODIMP
BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode
** node
) {
2721 if (!instance_active())
2725 return E_INVALIDARG
;
2728 GetIndexInParent() < 0 ||
2729 GetIndexInParent() >= static_cast<int>(
2730 GetParent()->InternalChildCount()) - 1) {
2735 *node
= GetParent()->InternalGetChild(GetIndexInParent() + 1)->
2736 ToBrowserAccessibilityWin()->NewReference();
2740 STDMETHODIMP
BrowserAccessibilityWin::get_childAt(
2741 unsigned int child_index
,
2742 ISimpleDOMNode
** node
) {
2743 if (!instance_active())
2747 return E_INVALIDARG
;
2749 if (child_index
>= PlatformChildCount())
2750 return E_INVALIDARG
;
2752 BrowserAccessibility
* child
= PlatformGetChild(child_index
);
2758 *node
= child
->ToBrowserAccessibilityWin()->NewReference();
2763 // ISimpleDOMText methods.
2766 STDMETHODIMP
BrowserAccessibilityWin::get_domText(BSTR
* dom_text
) {
2767 if (!instance_active())
2771 return E_INVALIDARG
;
2773 return GetStringAttributeAsBstr(
2774 ui::AX_ATTR_NAME
, dom_text
);
2777 STDMETHODIMP
BrowserAccessibilityWin::get_clippedSubstringBounds(
2778 unsigned int start_index
,
2779 unsigned int end_index
,
2784 // TODO(dmazzoni): fully support this API by intersecting the
2785 // rect with the container's rect.
2786 return get_unclippedSubstringBounds(
2787 start_index
, end_index
, out_x
, out_y
, out_width
, out_height
);
2790 STDMETHODIMP
BrowserAccessibilityWin::get_unclippedSubstringBounds(
2791 unsigned int start_index
,
2792 unsigned int end_index
,
2797 if (!instance_active())
2800 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
2801 return E_INVALIDARG
;
2803 const base::string16
& text_str
= TextForIAccessibleText();
2804 if (start_index
> text_str
.size() ||
2805 end_index
> text_str
.size() ||
2806 start_index
> end_index
) {
2807 return E_INVALIDARG
;
2810 gfx::Rect bounds
= GetGlobalBoundsForRange(
2811 start_index
, end_index
- start_index
);
2812 *out_x
= bounds
.x();
2813 *out_y
= bounds
.y();
2814 *out_width
= bounds
.width();
2815 *out_height
= bounds
.height();
2819 STDMETHODIMP
BrowserAccessibilityWin::scrollToSubstring(
2820 unsigned int start_index
,
2821 unsigned int end_index
) {
2822 if (!instance_active())
2825 const base::string16
& text_str
= TextForIAccessibleText();
2826 if (start_index
> text_str
.size() ||
2827 end_index
> text_str
.size() ||
2828 start_index
> end_index
) {
2829 return E_INVALIDARG
;
2832 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2833 start_index
, end_index
- start_index
));
2834 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2840 // IServiceProvider methods.
2843 STDMETHODIMP
BrowserAccessibilityWin::QueryService(REFGUID guidService
,
2846 if (!instance_active())
2849 // The system uses IAccessible APIs for many purposes, but only
2850 // assistive technology like screen readers uses IAccessible2.
2851 // Enable full accessibility support when IAccessible2 APIs are queried.
2852 if (riid
== IID_IAccessible2
)
2853 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2855 if (guidService
== GUID_IAccessibleContentDocument
) {
2856 // Special Mozilla extension: return the accessible for the root document.
2857 // Screen readers use this to distinguish between a document loaded event
2858 // on the root document vs on an iframe.
2859 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2860 IID_IAccessible2
, object
);
2863 if (guidService
== IID_IAccessible
||
2864 guidService
== IID_IAccessible2
||
2865 guidService
== IID_IAccessibleAction
||
2866 guidService
== IID_IAccessibleApplication
||
2867 guidService
== IID_IAccessibleHyperlink
||
2868 guidService
== IID_IAccessibleHypertext
||
2869 guidService
== IID_IAccessibleImage
||
2870 guidService
== IID_IAccessibleTable
||
2871 guidService
== IID_IAccessibleTable2
||
2872 guidService
== IID_IAccessibleTableCell
||
2873 guidService
== IID_IAccessibleText
||
2874 guidService
== IID_IAccessibleValue
||
2875 guidService
== IID_ISimpleDOMDocument
||
2876 guidService
== IID_ISimpleDOMNode
||
2877 guidService
== IID_ISimpleDOMText
||
2878 guidService
== GUID_ISimpleDOM
) {
2879 return QueryInterface(riid
, object
);
2882 // We only support the IAccessibleEx interface on Windows 8 and above. This
2883 // is needed for the on-screen Keyboard to show up in metro mode, when the
2884 // user taps an editable portion on the page.
2885 // All methods in the IAccessibleEx interface are unimplemented.
2886 if (riid
== IID_IAccessibleEx
&&
2887 base::win::GetVersion() >= base::win::VERSION_WIN8
) {
2888 return QueryInterface(riid
, object
);
2895 STDMETHODIMP
BrowserAccessibilityWin::GetPatternProvider(PATTERNID id
,
2896 IUnknown
** provider
) {
2897 DVLOG(1) << "In Function: "
2899 << " for pattern id: "
2901 if (id
== UIA_ValuePatternId
|| id
== UIA_TextPatternId
) {
2902 if (IsEditableText()) {
2903 DVLOG(1) << "Returning UIA text provider";
2904 base::win::UIATextProvider::CreateTextProvider(
2905 GetValueText(), true, provider
);
2912 STDMETHODIMP
BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id
,
2914 DVLOG(1) << "In Function: "
2916 << " for property id: "
2918 V_VT(ret
) = VT_EMPTY
;
2919 if (id
== UIA_ControlTypePropertyId
) {
2920 if (IsEditableText()) {
2922 ret
->lVal
= UIA_EditControlTypeId
;
2923 DVLOG(1) << "Returning Edit control type";
2925 DVLOG(1) << "Returning empty control type";
2932 // CComObjectRootEx methods.
2936 HRESULT WINAPI
BrowserAccessibilityWin::InternalQueryInterface(
2938 const _ATL_INTMAP_ENTRY
* entries
,
2941 BrowserAccessibilityWin
* accessibility
=
2942 reinterpret_cast<BrowserAccessibilityWin
*>(this_ptr
);
2943 int32 ia_role
= accessibility
->ia_role();
2944 if (iid
== IID_IAccessibleImage
) {
2945 if (ia_role
!= ROLE_SYSTEM_GRAPHIC
) {
2947 return E_NOINTERFACE
;
2949 } else if (iid
== IID_IAccessibleTable
|| iid
== IID_IAccessibleTable2
) {
2950 if (ia_role
!= ROLE_SYSTEM_TABLE
) {
2952 return E_NOINTERFACE
;
2954 } else if (iid
== IID_IAccessibleTableCell
) {
2955 if (!accessibility
->IsCellOrTableHeaderRole()) {
2957 return E_NOINTERFACE
;
2959 } else if (iid
== IID_IAccessibleValue
) {
2960 if (ia_role
!= ROLE_SYSTEM_PROGRESSBAR
&&
2961 ia_role
!= ROLE_SYSTEM_SCROLLBAR
&&
2962 ia_role
!= ROLE_SYSTEM_SLIDER
) {
2964 return E_NOINTERFACE
;
2966 } else if (iid
== IID_ISimpleDOMDocument
) {
2967 if (ia_role
!= ROLE_SYSTEM_DOCUMENT
) {
2969 return E_NOINTERFACE
;
2973 return CComObjectRootBase::InternalQueryInterface(
2974 this_ptr
, entries
, iid
, object
);
2981 void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() {
2982 // Swap win_attributes_ to old_win_attributes_, allowing us to see
2983 // exactly what changed and fire appropriate events. Note that
2984 // old_win_attributes_ is cleared at the end of UpdateStep3FireEvents.
2985 old_win_attributes_
.swap(win_attributes_
);
2986 win_attributes_
.reset(new WinAttributes());
2990 win_attributes_
->ia2_attributes
.clear();
2992 // Expose autocomplete attribute for combobox and textbox.
2993 StringAttributeToIA2(ui::AX_ATTR_AUTO_COMPLETE
, "autocomplete");
2995 // Expose the "display" and "tag" attributes.
2996 StringAttributeToIA2(ui::AX_ATTR_DISPLAY
, "display");
2997 StringAttributeToIA2(ui::AX_ATTR_DROPEFFECT
, "dropeffect");
2998 StringAttributeToIA2(ui::AX_ATTR_TEXT_INPUT_TYPE
, "text-input-type");
2999 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG
, "tag");
3000 StringAttributeToIA2(ui::AX_ATTR_ROLE
, "xml-roles");
3002 // Expose "level" attribute for headings, trees, etc.
3003 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL
, "level");
3005 // Expose the set size and position in set for listbox options.
3006 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
3008 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX
) {
3009 win_attributes_
->ia2_attributes
.push_back(
3010 L
"setsize:" + base::IntToString16(GetParent()->PlatformChildCount()));
3011 win_attributes_
->ia2_attributes
.push_back(
3012 L
"setsize:" + base::IntToString16(GetIndexInParent() + 1));
3015 if (ia_role() == ROLE_SYSTEM_CHECKBUTTON
||
3016 ia_role() == ROLE_SYSTEM_RADIOBUTTON
||
3017 ia2_role() == IA2_ROLE_CHECK_MENU_ITEM
||
3018 ia2_role() == IA2_ROLE_RADIO_MENU_ITEM
||
3019 ia2_role() == IA2_ROLE_TOGGLE_BUTTON
) {
3020 win_attributes_
->ia2_attributes
.push_back(L
"checkable:true");
3023 // Expose live region attributes.
3024 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS
, "live");
3025 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT
, "relevant");
3026 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC
, "atomic");
3027 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY
, "busy");
3029 // Expose aria-grabbed attributes.
3030 BoolAttributeToIA2(ui::AX_ATTR_GRABBED
, "grabbed");
3032 // Expose container live region attributes.
3033 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS
,
3035 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT
,
3036 "container-relevant");
3037 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC
,
3038 "container-atomic");
3039 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY
,
3042 // Expose table cell index.
3043 if (IsCellOrTableHeaderRole()) {
3044 BrowserAccessibility
* table
= GetParent();
3045 while (table
&& table
->GetRole() != ui::AX_ROLE_TABLE
)
3046 table
= table
->GetParent();
3048 const std::vector
<int32
>& unique_cell_ids
= table
->GetIntListAttribute(
3049 ui::AX_ATTR_UNIQUE_CELL_IDS
);
3050 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
3051 if (unique_cell_ids
[i
] == GetId()) {
3052 win_attributes_
->ia2_attributes
.push_back(
3053 base::string16(L
"table-cell-index:") + base::IntToString16(i
));
3059 // Expose invalid state for form controls and elements with aria-invalid.
3061 if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE
, &invalid_state
)) {
3062 // TODO(nektar): Handle the possibility of having multiple aria-invalid
3063 // attributes defined, e.g., "invalid:spelling,grammar".
3064 switch (invalid_state
) {
3065 case ui::AX_INVALID_STATE_FALSE
:
3066 win_attributes_
->ia2_attributes
.push_back(L
"invalid:false");
3068 case ui::AX_INVALID_STATE_TRUE
:
3069 win_attributes_
->ia2_attributes
.push_back(L
"invalid:true");
3071 case ui::AX_INVALID_STATE_SPELLING
:
3072 win_attributes_
->ia2_attributes
.push_back(L
"invalid:spelling");
3074 case ui::AX_INVALID_STATE_GRAMMAR
:
3075 win_attributes_
->ia2_attributes
.push_back(L
"invalid:grammar");
3077 case ui::AX_INVALID_STATE_OTHER
:
3079 base::string16 aria_invalid_value
;
3080 if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE
,
3081 &aria_invalid_value
)) {
3082 win_attributes_
->ia2_attributes
.push_back(
3083 L
"invalid:" + aria_invalid_value
);
3085 // Set the attribute to L"true", since we cannot be more specific.
3086 win_attributes_
->ia2_attributes
.push_back(L
"invalid:true");
3095 // Expose row or column header sort direction.
3096 int32 sort_direction
;
3097 if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER
||
3098 ia_role() == ROLE_SYSTEM_ROWHEADER
) &&
3099 GetIntAttribute(ui::AX_ATTR_SORT_DIRECTION
, &sort_direction
)) {
3100 switch (sort_direction
) {
3101 case ui::AX_SORT_DIRECTION_UNSORTED
:
3102 win_attributes_
->ia2_attributes
.push_back(L
"sort:none");
3104 case ui::AX_SORT_DIRECTION_ASCENDING
:
3105 win_attributes_
->ia2_attributes
.push_back(L
"sort:ascending");
3107 case ui::AX_SORT_DIRECTION_DESCENDING
:
3108 win_attributes_
->ia2_attributes
.push_back(L
"sort:descending");
3110 case ui::AX_SORT_DIRECTION_OTHER
:
3111 win_attributes_
->ia2_attributes
.push_back(L
"sort:other");
3118 // The calculation of the accessible name of an element has been
3119 // standardized in the HTML to Platform Accessibility APIs Implementation
3120 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
3121 // appropriate accessible name on Windows, we need to apply some logic
3122 // to the fields we get from WebKit.
3124 // TODO(dmazzoni): move most of this logic into WebKit.
3128 // name: the default name, e.g. inner text
3129 // title ui element: a reference to a <label> element on the same
3130 // page that labels this node.
3131 // description: accessible labels that override the default name:
3132 // aria-label or aria-labelledby or aria-describedby
3133 // help: the value of the "title" attribute
3135 // On Windows, the logic we apply lets some fields take precedence and
3136 // always returns the primary name in "name" and the secondary name,
3137 // if any, in "description".
3139 int title_elem_id
= GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT
);
3140 base::string16 name
= GetString16Attribute(ui::AX_ATTR_NAME
);
3141 base::string16 description
= GetString16Attribute(ui::AX_ATTR_DESCRIPTION
);
3142 base::string16 help
= GetString16Attribute(ui::AX_ATTR_HELP
);
3143 base::string16 value
= GetString16Attribute(ui::AX_ATTR_VALUE
);
3145 // WebKit annoyingly puts the title in the description if there's no other
3146 // description, which just confuses the rest of the logic. Put it back.
3147 // Now "help" is always the value of the "title" attribute, if present.
3148 base::string16 title_attr
;
3149 if (GetHtmlAttribute("title", &title_attr
) &&
3150 description
== title_attr
&&
3153 description
.clear();
3156 // Now implement the main logic: the descripion should become the name if
3157 // it's nonempty, and the help should become the description if
3158 // there's no description - or the name if there's no name or description.
3159 if (!description
.empty()) {
3161 description
.clear();
3163 if (!help
.empty() && description
.empty()) {
3167 if (!description
.empty() && name
.empty() && !title_elem_id
) {
3169 description
.clear();
3172 // If it's a text field, also consider the placeholder.
3173 base::string16 placeholder
;
3174 if (GetRole() == ui::AX_ROLE_TEXT_FIELD
&&
3175 HasState(ui::AX_STATE_FOCUSABLE
) &&
3176 GetHtmlAttribute("placeholder", &placeholder
)) {
3177 if (name
.empty() && !title_elem_id
) {
3179 } else if (description
.empty()) {
3180 description
= placeholder
;
3184 // On Windows, the value of a document should be its url.
3185 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA
||
3186 GetRole() == ui::AX_ROLE_WEB_AREA
) {
3187 value
= GetString16Attribute(ui::AX_ATTR_DOC_URL
);
3190 // For certain roles (listbox option, static text, and list marker)
3191 // WebKit stores the main accessible text in the "value" - swap it so
3192 // that it's the "name".
3194 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
||
3195 GetRole() == ui::AX_ROLE_STATIC_TEXT
||
3196 GetRole() == ui::AX_ROLE_LIST_MARKER
)) {
3197 base::string16 tmp
= value
;
3202 // If this doesn't have a value and is linked then set its value to the url
3203 // attribute. This allows screen readers to read an empty link's destination.
3204 if (value
.empty() && (ia_state() & STATE_SYSTEM_LINKED
))
3205 value
= GetString16Attribute(ui::AX_ATTR_URL
);
3207 win_attributes_
->name
= name
;
3208 win_attributes_
->description
= description
;
3209 win_attributes_
->help
= help
;
3210 win_attributes_
->value
= value
;
3212 // Clear any old relationships between this node and other nodes.
3213 for (size_t i
= 0; i
< relations_
.size(); ++i
)
3214 relations_
[i
]->Release();
3217 // Handle title UI element.
3218 if (title_elem_id
) {
3219 // Add a labelled by relationship.
3220 CComObject
<BrowserAccessibilityRelation
>* relation
;
3221 HRESULT hr
= CComObject
<BrowserAccessibilityRelation
>::CreateInstance(
3223 DCHECK(SUCCEEDED(hr
));
3225 relation
->Initialize(this, IA2_RELATION_LABELLED_BY
);
3226 relation
->AddTarget(title_elem_id
);
3227 relations_
.push_back(relation
);
3230 // Expose slider value.
3231 if (ia_role() == ROLE_SYSTEM_PROGRESSBAR
||
3232 ia_role() == ROLE_SYSTEM_SCROLLBAR
||
3233 ia_role() == ROLE_SYSTEM_SLIDER
) {
3234 win_attributes_
->ia2_attributes
.push_back(L
"valuetext:" + GetValueText());
3237 // If this is a web area for a presentational iframe, give it a role of
3238 // something other than DOCUMENT so that the fact that it's a separate doc
3239 // is not exposed to AT.
3240 if (IsWebAreaForPresentationalIframe()) {
3241 win_attributes_
->ia_role
= ROLE_SYSTEM_GROUPING
;
3242 win_attributes_
->ia2_role
= ROLE_SYSTEM_GROUPING
;
3246 void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() {
3247 // Construct the hypertext for this node, which contains the concatenation
3248 // of all of the static text of this node's children and an embedded object
3249 // character for all non-static-text children. Build up a map from the
3250 // character index of each embedded object character to the id of the
3251 // child object it points to.
3252 for (unsigned int i
= 0; i
< PlatformChildCount(); ++i
) {
3253 BrowserAccessibilityWin
* child
=
3254 PlatformGetChild(i
)->ToBrowserAccessibilityWin();
3255 if (child
->GetRole() == ui::AX_ROLE_STATIC_TEXT
) {
3256 win_attributes_
->hypertext
+= child
->name();
3258 int32 char_offset
= hypertext().size();
3259 int32 child_id
= child
->GetId();
3260 int32 index
= hyperlinks().size();
3261 win_attributes_
->hyperlink_offset_to_index
[char_offset
] = index
;
3262 win_attributes_
->hyperlinks
.push_back(child_id
);
3263 win_attributes_
->hypertext
+= kEmbeddedCharacter
;
3268 void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation
) {
3269 BrowserAccessibilityManagerWin
* manager
=
3270 this->manager()->ToBrowserAccessibilityManagerWin();
3272 // Fire an event when an alert first appears.
3273 if (ia_role() == ROLE_SYSTEM_ALERT
&&
3274 old_win_attributes_
->ia_role
!= ROLE_SYSTEM_ALERT
) {
3275 manager
->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, this);
3278 // Fire an event when a new subtree is created.
3279 if (is_subtree_creation
)
3280 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW
, this);
3282 // The rest of the events only fire on changes, not on new objects.
3283 if (old_win_attributes_
->ia_role
!= 0 ||
3284 !old_win_attributes_
->role_name
.empty()) {
3285 // Fire an event if the name, description, help, or value changes.
3286 if (name() != old_win_attributes_
->name
)
3287 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE
, this);
3288 if (description() != old_win_attributes_
->description
)
3289 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE
, this);
3290 if (help() != old_win_attributes_
->help
)
3291 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_HELPCHANGE
, this);
3292 if (value() != old_win_attributes_
->value
)
3293 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE
, this);
3294 if (ia_state() != old_win_attributes_
->ia_state
)
3295 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE
, this);
3297 // Normally focus events are handled elsewhere, however
3298 // focus for managed descendants is platform-specific.
3299 // Fire a focus event if the focused descendant in a multi-select
3300 // list box changes.
3301 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION
&&
3302 (ia_state() & STATE_SYSTEM_FOCUSABLE
) &&
3303 (ia_state() & STATE_SYSTEM_SELECTABLE
) &&
3304 (ia_state() & STATE_SYSTEM_FOCUSED
) &&
3305 !(old_win_attributes_
->ia_state
& STATE_SYSTEM_FOCUSED
)) {
3306 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS
, this);
3309 // Handle selection being added or removed.
3310 bool is_selected_now
= (ia_state() & STATE_SYSTEM_SELECTED
) != 0;
3311 bool was_selected_before
=
3312 (old_win_attributes_
->ia_state
& STATE_SYSTEM_SELECTED
) != 0;
3313 if (is_selected_now
&& !was_selected_before
) {
3314 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD
, this);
3315 } else if (!is_selected_now
&& was_selected_before
) {
3316 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE
, this);
3319 // Fire an event if this container object has scrolled.
3322 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
3323 GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
3324 if (sx
!= previous_scroll_x_
|| sy
!= previous_scroll_y_
)
3325 manager
->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND
, this);
3326 previous_scroll_x_
= sx
;
3327 previous_scroll_y_
= sy
;
3330 // Changing a static text node can affect the IAccessibleText hypertext
3331 // of the parent node, so force an update on the parent.
3332 BrowserAccessibilityWin
* parent
= GetParent()->ToBrowserAccessibilityWin();
3334 GetRole() == ui::AX_ROLE_STATIC_TEXT
&&
3335 name() != old_win_attributes_
->name
) {
3336 parent
->UpdateStep1ComputeWinAttributes();
3337 parent
->UpdateStep2ComputeHypertext();
3338 parent
->UpdateStep3FireEvents(false);
3341 // Fire hypertext-related events.
3342 int start
, old_len
, new_len
;
3343 ComputeHypertextRemovedAndInserted(&start
, &old_len
, &new_len
);
3345 // In-process screen readers may call IAccessibleText::get_oldText
3346 // in reaction to this event to retrieve the text that was removed.
3347 manager
->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_REMOVED
, this);
3350 // In-process screen readers may call IAccessibleText::get_newText
3351 // in reaction to this event to retrieve the text that was inserted.
3352 manager
->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_INSERTED
, this);
3356 old_win_attributes_
.reset(nullptr);
3359 void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() {
3360 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3361 EVENT_OBJECT_HIDE
, this);
3364 void BrowserAccessibilityWin::NativeAddReference() {
3368 void BrowserAccessibilityWin::NativeReleaseReference() {
3372 bool BrowserAccessibilityWin::IsNative() const {
3376 void BrowserAccessibilityWin::OnLocationChanged() {
3377 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3378 EVENT_OBJECT_LOCATIONCHANGE
, this);
3381 BrowserAccessibilityWin
* BrowserAccessibilityWin::NewReference() {
3386 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetTargetFromChildID(
3387 const VARIANT
& var_id
) {
3388 if (var_id
.vt
!= VT_I4
)
3391 LONG child_id
= var_id
.lVal
;
3392 if (child_id
== CHILDID_SELF
)
3395 if (child_id
>= 1 && child_id
<= static_cast<LONG
>(PlatformChildCount()))
3396 return PlatformGetChild(child_id
- 1)->ToBrowserAccessibilityWin();
3398 return manager()->ToBrowserAccessibilityManagerWin()->
3399 GetFromUniqueIdWin(child_id
);
3402 HRESULT
BrowserAccessibilityWin::GetStringAttributeAsBstr(
3403 ui::AXStringAttribute attribute
,
3407 if (!GetString16Attribute(attribute
, &str
))
3413 *value_bstr
= SysAllocString(str
.c_str());
3414 DCHECK(*value_bstr
);
3419 void BrowserAccessibilityWin::StringAttributeToIA2(
3420 ui::AXStringAttribute attribute
,
3421 const char* ia2_attr
) {
3422 base::string16 value
;
3423 if (GetString16Attribute(attribute
, &value
)) {
3424 win_attributes_
->ia2_attributes
.push_back(
3425 base::ASCIIToUTF16(ia2_attr
) + L
":" + value
);
3429 void BrowserAccessibilityWin::BoolAttributeToIA2(
3430 ui::AXBoolAttribute attribute
,
3431 const char* ia2_attr
) {
3433 if (GetBoolAttribute(attribute
, &value
)) {
3434 win_attributes_
->ia2_attributes
.push_back(
3435 (base::ASCIIToUTF16(ia2_attr
) + L
":") +
3436 (value
? L
"true" : L
"false"));
3440 void BrowserAccessibilityWin::IntAttributeToIA2(
3441 ui::AXIntAttribute attribute
,
3442 const char* ia2_attr
) {
3444 if (GetIntAttribute(attribute
, &value
)) {
3445 win_attributes_
->ia2_attributes
.push_back(
3446 base::ASCIIToUTF16(ia2_attr
) + L
":" +
3447 base::IntToString16(value
));
3451 base::string16
BrowserAccessibilityWin::GetNameRecursive() const {
3452 if (!name().empty()) {
3456 base::string16 result
;
3457 for (uint32 i
= 0; i
< PlatformChildCount(); ++i
) {
3458 result
+= PlatformGetChild(i
)->ToBrowserAccessibilityWin()->
3464 base::string16
BrowserAccessibilityWin::GetValueText() {
3466 base::string16 value
= this->value();
3468 if (value
.empty() &&
3469 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE
, &fval
)) {
3470 value
= base::UTF8ToUTF16(base::DoubleToString(fval
));
3475 base::string16
BrowserAccessibilityWin::TextForIAccessibleText() {
3476 if (IsEditableText())
3478 return (GetRole() == ui::AX_ROLE_STATIC_TEXT
) ? name() : hypertext();
3481 bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index
,
3482 size_t new_char_index
) {
3483 CHECK(old_win_attributes_
);
3485 // For anything other than the "embedded character", we just compare the
3486 // characters directly.
3487 base::char16 old_ch
= old_win_attributes_
->hypertext
[old_char_index
];
3488 base::char16 new_ch
= win_attributes_
->hypertext
[new_char_index
];
3489 if (old_ch
!= new_ch
)
3491 if (old_ch
== new_ch
&& new_ch
!= kEmbeddedCharacter
)
3494 // If it's an embedded character, they're only identical if the child id
3495 // the hyperlink points to is the same.
3496 std::map
<int32
, int32
>& old_offset_to_index
=
3497 old_win_attributes_
->hyperlink_offset_to_index
;
3498 std::vector
<int32
>& old_hyperlinks
= old_win_attributes_
->hyperlinks
;
3499 int32 old_hyperlinks_count
= static_cast<int32
>(old_hyperlinks
.size());
3500 std::map
<int32
, int32
>::iterator iter
;
3501 iter
= old_offset_to_index
.find(old_char_index
);
3502 int old_index
= (iter
!= old_offset_to_index
.end()) ? iter
->second
: -1;
3503 int old_child_id
= (old_index
>= 0 && old_index
< old_hyperlinks_count
) ?
3504 old_hyperlinks
[old_index
] : -1;
3506 std::map
<int32
, int32
>& new_offset_to_index
=
3507 win_attributes_
->hyperlink_offset_to_index
;
3508 std::vector
<int32
>& new_hyperlinks
= win_attributes_
->hyperlinks
;
3509 int32 new_hyperlinks_count
= static_cast<int32
>(new_hyperlinks
.size());
3510 iter
= new_offset_to_index
.find(new_char_index
);
3511 int new_index
= (iter
!= new_offset_to_index
.end()) ? iter
->second
: -1;
3512 int new_child_id
= (new_index
>= 0 && new_index
< new_hyperlinks_count
) ?
3513 new_hyperlinks
[new_index
] : -1;
3515 return old_child_id
== new_child_id
;
3518 void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted(
3519 int* start
, int* old_len
, int* new_len
) {
3520 CHECK(old_win_attributes_
);
3526 const base::string16
& old_text
= old_win_attributes_
->hypertext
;
3527 const base::string16
& new_text
= hypertext();
3529 size_t common_prefix
= 0;
3530 while (common_prefix
< old_text
.size() &&
3531 common_prefix
< new_text
.size() &&
3532 IsSameHypertextCharacter(common_prefix
, common_prefix
)) {
3536 size_t common_suffix
= 0;
3537 while (common_prefix
+ common_suffix
< old_text
.size() &&
3538 common_prefix
+ common_suffix
< new_text
.size() &&
3539 IsSameHypertextCharacter(
3540 old_text
.size() - common_suffix
- 1,
3541 new_text
.size() - common_suffix
- 1)) {
3545 *start
= common_prefix
;
3546 *old_len
= old_text
.size() - common_prefix
- common_suffix
;
3547 *new_len
= new_text
.size() - common_prefix
- common_suffix
;
3550 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3551 const base::string16
& text
,
3553 if (*offset
== IA2_TEXT_OFFSET_LENGTH
)
3554 *offset
= static_cast<LONG
>(text
.size());
3555 else if (*offset
== IA2_TEXT_OFFSET_CARET
)
3556 get_caretOffset(offset
);
3559 ui::TextBoundaryType
BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3560 IA2TextBoundaryType ia2_boundary
) {
3561 switch(ia2_boundary
) {
3562 case IA2_TEXT_BOUNDARY_CHAR
:
3563 return ui::CHAR_BOUNDARY
;
3564 case IA2_TEXT_BOUNDARY_WORD
:
3565 return ui::WORD_BOUNDARY
;
3566 case IA2_TEXT_BOUNDARY_LINE
:
3567 return ui::LINE_BOUNDARY
;
3568 case IA2_TEXT_BOUNDARY_SENTENCE
:
3569 return ui::SENTENCE_BOUNDARY
;
3570 case IA2_TEXT_BOUNDARY_PARAGRAPH
:
3571 return ui::PARAGRAPH_BOUNDARY
;
3572 case IA2_TEXT_BOUNDARY_ALL
:
3573 return ui::ALL_BOUNDARY
;
3577 return ui::CHAR_BOUNDARY
;
3580 LONG
BrowserAccessibilityWin::FindBoundary(
3581 const base::string16
& text
,
3582 IA2TextBoundaryType ia2_boundary
,
3584 ui::TextBoundaryDirection direction
) {
3585 HandleSpecialTextOffset(text
, &start_offset
);
3586 if (ia2_boundary
== IA2_TEXT_BOUNDARY_WORD
&&
3587 GetRole() == ui::AX_ROLE_TEXT_FIELD
) {
3588 return GetWordStartBoundary(static_cast<int>(start_offset
), direction
);
3591 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
3592 const std::vector
<int32
>& line_breaks
= GetIntListAttribute(
3593 ui::AX_ATTR_LINE_BREAKS
);
3594 return ui::FindAccessibleTextBoundary(
3595 text
, line_breaks
, boundary
, start_offset
, direction
);
3598 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetFromID(int32 id
) {
3599 return manager()->GetFromID(id
)->ToBrowserAccessibilityWin();
3602 void BrowserAccessibilityWin::InitRoleAndState() {
3605 base::string16 role_name
;
3607 int32 ia2_state
= IA2_STATE_OPAQUE
;
3609 if (HasState(ui::AX_STATE_BUSY
))
3610 ia_state
|= STATE_SYSTEM_BUSY
;
3611 if (HasState(ui::AX_STATE_CHECKED
))
3612 ia_state
|= STATE_SYSTEM_CHECKED
;
3613 if (HasState(ui::AX_STATE_COLLAPSED
))
3614 ia_state
|= STATE_SYSTEM_COLLAPSED
;
3615 if (HasState(ui::AX_STATE_EXPANDED
))
3616 ia_state
|= STATE_SYSTEM_EXPANDED
;
3617 if (HasState(ui::AX_STATE_FOCUSABLE
))
3618 ia_state
|= STATE_SYSTEM_FOCUSABLE
;
3619 if (HasState(ui::AX_STATE_HASPOPUP
))
3620 ia_state
|= STATE_SYSTEM_HASPOPUP
;
3621 if (HasState(ui::AX_STATE_INDETERMINATE
))
3622 ia_state
|= STATE_SYSTEM_INDETERMINATE
;
3623 if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE
) &&
3624 GetIntAttribute(ui::AX_ATTR_INVALID_STATE
) != ui::AX_INVALID_STATE_FALSE
)
3625 ia2_state
|= IA2_STATE_INVALID_ENTRY
;
3626 if (HasState(ui::AX_STATE_INVISIBLE
))
3627 ia_state
|= STATE_SYSTEM_INVISIBLE
;
3628 if (HasState(ui::AX_STATE_LINKED
))
3629 ia_state
|= STATE_SYSTEM_LINKED
;
3630 if (HasState(ui::AX_STATE_MULTISELECTABLE
)) {
3631 ia_state
|= STATE_SYSTEM_EXTSELECTABLE
;
3632 ia_state
|= STATE_SYSTEM_MULTISELECTABLE
;
3634 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3635 if (HasState(ui::AX_STATE_OFFSCREEN
))
3636 ia_state
|= STATE_SYSTEM_OFFSCREEN
;
3637 if (HasState(ui::AX_STATE_PRESSED
))
3638 ia_state
|= STATE_SYSTEM_PRESSED
;
3639 if (HasState(ui::AX_STATE_PROTECTED
))
3640 ia_state
|= STATE_SYSTEM_PROTECTED
;
3641 if (HasState(ui::AX_STATE_REQUIRED
))
3642 ia2_state
|= IA2_STATE_REQUIRED
;
3643 if (HasState(ui::AX_STATE_SELECTABLE
))
3644 ia_state
|= STATE_SYSTEM_SELECTABLE
;
3645 if (HasState(ui::AX_STATE_SELECTED
))
3646 ia_state
|= STATE_SYSTEM_SELECTED
;
3647 if (HasState(ui::AX_STATE_VISITED
))
3648 ia_state
|= STATE_SYSTEM_TRAVERSED
;
3649 if (!HasState(ui::AX_STATE_ENABLED
))
3650 ia_state
|= STATE_SYSTEM_UNAVAILABLE
;
3651 if (HasState(ui::AX_STATE_VERTICAL
))
3652 ia2_state
|= IA2_STATE_VERTICAL
;
3653 if (HasState(ui::AX_STATE_HORIZONTAL
))
3654 ia2_state
|= IA2_STATE_HORIZONTAL
;
3655 if (HasState(ui::AX_STATE_VISITED
))
3656 ia_state
|= STATE_SYSTEM_TRAVERSED
;
3658 // Expose whether or not the mouse is over an element, but suppress
3659 // this for tests because it can make the test results flaky depending
3660 // on the position of the mouse.
3661 BrowserAccessibilityStateImpl
* accessibility_state
=
3662 BrowserAccessibilityStateImpl::GetInstance();
3663 if (!accessibility_state
->disable_hot_tracking_for_testing()) {
3664 if (HasState(ui::AX_STATE_HOVERED
))
3665 ia_state
|= STATE_SYSTEM_HOTTRACKED
;
3668 // WebKit marks everything as readonly unless it's editable text, so if it's
3669 // not readonly, mark it as editable now. The final computation of the
3670 // READONLY state for MSAA is below, after the switch.
3671 if (!HasState(ui::AX_STATE_READ_ONLY
))
3672 ia2_state
|= IA2_STATE_EDITABLE
;
3674 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED
))
3675 ia_state
|= STATE_SYSTEM_MIXED
;
3677 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE
))
3678 ia2_state
|= IA2_STATE_EDITABLE
;
3680 if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE
).empty())
3681 ia2_state
|= IA2_STATE_SUPPORTS_AUTOCOMPLETION
;
3683 base::string16 html_tag
= GetString16Attribute(
3684 ui::AX_ATTR_HTML_TAG
);
3685 switch (GetRole()) {
3686 case ui::AX_ROLE_ALERT
:
3687 ia_role
= ROLE_SYSTEM_ALERT
;
3689 case ui::AX_ROLE_ALERT_DIALOG
:
3690 ia_role
= ROLE_SYSTEM_DIALOG
;
3692 case ui::AX_ROLE_APPLICATION
:
3693 ia_role
= ROLE_SYSTEM_APPLICATION
;
3695 case ui::AX_ROLE_ARTICLE
:
3696 ia_role
= ROLE_SYSTEM_DOCUMENT
;
3697 ia_state
|= STATE_SYSTEM_READONLY
;
3699 case ui::AX_ROLE_BANNER
:
3700 ia_role
= ROLE_SYSTEM_GROUPING
;
3701 ia2_role
= IA2_ROLE_HEADER
;
3703 case ui::AX_ROLE_BLOCKQUOTE
:
3704 role_name
= html_tag
;
3705 ia2_role
= IA2_ROLE_SECTION
;
3707 case ui::AX_ROLE_BUSY_INDICATOR
:
3708 ia_role
= ROLE_SYSTEM_ANIMATION
;
3709 ia_state
|= STATE_SYSTEM_READONLY
;
3711 case ui::AX_ROLE_BUTTON
:
3712 ia_role
= ROLE_SYSTEM_PUSHBUTTON
;
3714 case ui::AX_ROLE_CANVAS
:
3715 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK
)) {
3716 role_name
= L
"canvas";
3717 ia2_role
= IA2_ROLE_CANVAS
;
3719 ia_role
= ROLE_SYSTEM_GRAPHIC
;
3722 case ui::AX_ROLE_CAPTION
:
3723 ia_role
= ROLE_SYSTEM_TEXT
;
3724 ia2_role
= IA2_ROLE_CAPTION
;
3726 case ui::AX_ROLE_CELL
:
3727 ia_role
= ROLE_SYSTEM_CELL
;
3729 case ui::AX_ROLE_CHECK_BOX
:
3730 ia_role
= ROLE_SYSTEM_CHECKBUTTON
;
3731 ia2_state
|= IA2_STATE_CHECKABLE
;
3733 case ui::AX_ROLE_COLOR_WELL
:
3734 ia_role
= ROLE_SYSTEM_TEXT
;
3735 ia2_role
= IA2_ROLE_COLOR_CHOOSER
;
3737 case ui::AX_ROLE_COLUMN
:
3738 ia_role
= ROLE_SYSTEM_COLUMN
;
3740 case ui::AX_ROLE_COLUMN_HEADER
:
3741 ia_role
= ROLE_SYSTEM_COLUMNHEADER
;
3743 case ui::AX_ROLE_COMBO_BOX
:
3744 ia_role
= ROLE_SYSTEM_COMBOBOX
;
3746 case ui::AX_ROLE_COMPLEMENTARY
:
3747 ia_role
= ROLE_SYSTEM_GROUPING
;
3748 ia2_role
= IA2_ROLE_NOTE
;
3750 case ui::AX_ROLE_CONTENT_INFO
:
3751 ia_role
= ROLE_SYSTEM_TEXT
;
3752 ia2_role
= IA2_ROLE_PARAGRAPH
;
3754 case ui::AX_ROLE_DATE
:
3755 case ui::AX_ROLE_DATE_TIME
:
3756 ia_role
= ROLE_SYSTEM_DROPLIST
;
3757 ia2_role
= IA2_ROLE_DATE_EDITOR
;
3759 case ui::AX_ROLE_DIV
:
3761 ia_role
= ROLE_SYSTEM_GROUPING
;
3762 ia2_role
= IA2_ROLE_SECTION
;
3764 case ui::AX_ROLE_DEFINITION
:
3765 role_name
= html_tag
;
3766 ia2_role
= IA2_ROLE_PARAGRAPH
;
3767 ia_state
|= STATE_SYSTEM_READONLY
;
3769 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL
:
3770 role_name
= html_tag
;
3771 ia_role
= ROLE_SYSTEM_TEXT
;
3772 ia2_role
= IA2_ROLE_PARAGRAPH
;
3774 case ui::AX_ROLE_DESCRIPTION_LIST
:
3775 role_name
= html_tag
;
3776 ia_role
= ROLE_SYSTEM_LIST
;
3777 ia_state
|= STATE_SYSTEM_READONLY
;
3779 case ui::AX_ROLE_DESCRIPTION_LIST_TERM
:
3780 ia_role
= ROLE_SYSTEM_LISTITEM
;
3781 ia_state
|= STATE_SYSTEM_READONLY
;
3783 case ui::AX_ROLE_DETAILS
:
3784 role_name
= html_tag
;
3785 ia_role
= ROLE_SYSTEM_GROUPING
;
3787 case ui::AX_ROLE_DIALOG
:
3788 ia_role
= ROLE_SYSTEM_DIALOG
;
3790 case ui::AX_ROLE_DISCLOSURE_TRIANGLE
:
3791 ia_role
= ROLE_SYSTEM_PUSHBUTTON
;
3793 case ui::AX_ROLE_DOCUMENT
:
3794 case ui::AX_ROLE_ROOT_WEB_AREA
:
3795 case ui::AX_ROLE_WEB_AREA
:
3796 ia_role
= ROLE_SYSTEM_DOCUMENT
;
3797 ia_state
|= STATE_SYSTEM_READONLY
;
3798 ia_state
|= STATE_SYSTEM_FOCUSABLE
;
3800 case ui::AX_ROLE_EMBEDDED_OBJECT
:
3801 ia_role
= ROLE_SYSTEM_CLIENT
;
3802 ia2_role
= IA2_ROLE_EMBEDDED_OBJECT
;
3804 case ui::AX_ROLE_FIGCAPTION
:
3805 role_name
= html_tag
;
3806 ia2_role
= IA2_ROLE_CAPTION
;
3808 case ui::AX_ROLE_FIGURE
:
3809 ia_role
= ROLE_SYSTEM_GROUPING
;
3811 case ui::AX_ROLE_FORM
:
3812 role_name
= L
"form";
3813 ia2_role
= IA2_ROLE_FORM
;
3815 case ui::AX_ROLE_FOOTER
:
3816 ia_role
= ROLE_SYSTEM_GROUPING
;
3817 ia2_role
= IA2_ROLE_FOOTER
;
3819 case ui::AX_ROLE_GRID
:
3820 ia_role
= ROLE_SYSTEM_TABLE
;
3821 ia_state
|= STATE_SYSTEM_READONLY
;
3823 case ui::AX_ROLE_GROUP
: {
3824 base::string16 aria_role
= GetString16Attribute(
3826 if (aria_role
== L
"group" || html_tag
== L
"fieldset") {
3827 ia_role
= ROLE_SYSTEM_GROUPING
;
3828 } else if (html_tag
== L
"li") {
3829 ia_role
= ROLE_SYSTEM_LISTITEM
;
3830 ia_state
|= STATE_SYSTEM_READONLY
;
3832 if (html_tag
.empty())
3835 role_name
= html_tag
;
3836 ia2_role
= IA2_ROLE_SECTION
;
3840 case ui::AX_ROLE_HEADING
:
3841 role_name
= html_tag
;
3842 ia2_role
= IA2_ROLE_HEADING
;
3844 case ui::AX_ROLE_IFRAME
:
3845 ia_role
= ROLE_SYSTEM_DOCUMENT
;
3846 ia2_role
= IA2_ROLE_INTERNAL_FRAME
;
3847 ia_state
= STATE_SYSTEM_READONLY
;
3849 case ui::AX_ROLE_IFRAME_PRESENTATIONAL
:
3850 ia_role
= ROLE_SYSTEM_GROUPING
;
3852 case ui::AX_ROLE_IMAGE
:
3853 ia_role
= ROLE_SYSTEM_GRAPHIC
;
3854 ia_state
|= STATE_SYSTEM_READONLY
;
3856 case ui::AX_ROLE_IMAGE_MAP
:
3857 role_name
= html_tag
;
3858 ia2_role
= IA2_ROLE_IMAGE_MAP
;
3859 ia_state
|= STATE_SYSTEM_READONLY
;
3861 case ui::AX_ROLE_IMAGE_MAP_LINK
:
3862 ia_role
= ROLE_SYSTEM_LINK
;
3863 ia_state
|= STATE_SYSTEM_LINKED
;
3864 ia_state
|= STATE_SYSTEM_READONLY
;
3866 case ui::AX_ROLE_LABEL_TEXT
:
3867 case ui::AX_ROLE_LEGEND
:
3868 ia_role
= ROLE_SYSTEM_TEXT
;
3869 ia2_role
= IA2_ROLE_LABEL
;
3871 case ui::AX_ROLE_LINK
:
3872 ia_role
= ROLE_SYSTEM_LINK
;
3873 ia_state
|= STATE_SYSTEM_LINKED
;
3875 case ui::AX_ROLE_LIST
:
3876 ia_role
= ROLE_SYSTEM_LIST
;
3877 ia_state
|= STATE_SYSTEM_READONLY
;
3879 case ui::AX_ROLE_LIST_BOX
:
3880 ia_role
= ROLE_SYSTEM_LIST
;
3882 case ui::AX_ROLE_LIST_BOX_OPTION
:
3883 ia_role
= ROLE_SYSTEM_LISTITEM
;
3884 if (ia_state
& STATE_SYSTEM_SELECTABLE
) {
3885 ia_state
|= STATE_SYSTEM_FOCUSABLE
;
3886 if (HasState(ui::AX_STATE_FOCUSED
))
3887 ia_state
|= STATE_SYSTEM_FOCUSED
;
3890 case ui::AX_ROLE_LIST_ITEM
:
3891 ia_role
= ROLE_SYSTEM_LISTITEM
;
3892 ia_state
|= STATE_SYSTEM_READONLY
;
3894 case ui::AX_ROLE_MAIN
:
3895 ia_role
= ROLE_SYSTEM_GROUPING
;
3896 ia2_role
= IA2_ROLE_PARAGRAPH
;
3898 case ui::AX_ROLE_MARQUEE
:
3899 ia_role
= ROLE_SYSTEM_ANIMATION
;
3901 case ui::AX_ROLE_MATH
:
3902 ia_role
= ROLE_SYSTEM_EQUATION
;
3904 case ui::AX_ROLE_MENU
:
3905 case ui::AX_ROLE_MENU_BUTTON
:
3906 ia_role
= ROLE_SYSTEM_MENUPOPUP
;
3908 case ui::AX_ROLE_MENU_BAR
:
3909 ia_role
= ROLE_SYSTEM_MENUBAR
;
3911 case ui::AX_ROLE_MENU_ITEM
:
3912 ia_role
= ROLE_SYSTEM_MENUITEM
;
3914 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX
:
3915 ia_role
= ROLE_SYSTEM_MENUITEM
;
3916 ia2_role
= IA2_ROLE_CHECK_MENU_ITEM
;
3917 ia2_state
|= IA2_STATE_CHECKABLE
;
3919 case ui::AX_ROLE_MENU_ITEM_RADIO
:
3920 ia_role
= ROLE_SYSTEM_MENUITEM
;
3921 ia2_role
= IA2_ROLE_RADIO_MENU_ITEM
;
3923 case ui::AX_ROLE_MENU_LIST_POPUP
:
3924 ia_role
= ROLE_SYSTEM_CLIENT
;
3926 case ui::AX_ROLE_MENU_LIST_OPTION
:
3927 ia_role
= ROLE_SYSTEM_LISTITEM
;
3928 if (ia_state
& STATE_SYSTEM_SELECTABLE
) {
3929 ia_state
|= STATE_SYSTEM_FOCUSABLE
;
3930 if (HasState(ui::AX_STATE_FOCUSED
))
3931 ia_state
|= STATE_SYSTEM_FOCUSED
;
3934 case ui::AX_ROLE_METER
:
3935 role_name
= html_tag
;
3936 ia_role
= ROLE_SYSTEM_PROGRESSBAR
;
3938 case ui::AX_ROLE_NAVIGATION
:
3939 ia_role
= ROLE_SYSTEM_GROUPING
;
3940 ia2_role
= IA2_ROLE_SECTION
;
3942 case ui::AX_ROLE_NOTE
:
3943 ia_role
= ROLE_SYSTEM_GROUPING
;
3944 ia2_role
= IA2_ROLE_NOTE
;
3946 case ui::AX_ROLE_OUTLINE
:
3947 ia_role
= ROLE_SYSTEM_OUTLINE
;
3949 case ui::AX_ROLE_PARAGRAPH
:
3951 ia2_role
= IA2_ROLE_PARAGRAPH
;
3953 case ui::AX_ROLE_POP_UP_BUTTON
:
3954 if (html_tag
== L
"select") {
3955 ia_role
= ROLE_SYSTEM_COMBOBOX
;
3957 ia_role
= ROLE_SYSTEM_BUTTONMENU
;
3960 case ui::AX_ROLE_PRE
:
3961 role_name
= html_tag
;
3962 ia_role
= ROLE_SYSTEM_TEXT
;
3963 ia2_role
= IA2_ROLE_PARAGRAPH
;
3965 case ui::AX_ROLE_PROGRESS_INDICATOR
:
3966 ia_role
= ROLE_SYSTEM_PROGRESSBAR
;
3967 ia_state
|= STATE_SYSTEM_READONLY
;
3969 case ui::AX_ROLE_RADIO_BUTTON
:
3970 ia_role
= ROLE_SYSTEM_RADIOBUTTON
;
3971 ia2_state
= IA2_STATE_CHECKABLE
;
3973 case ui::AX_ROLE_RADIO_GROUP
:
3974 ia_role
= ROLE_SYSTEM_GROUPING
;
3976 case ui::AX_ROLE_REGION
:
3977 if (html_tag
== L
"section") {
3978 ia_role
= ROLE_SYSTEM_GROUPING
;
3979 ia2_role
= IA2_ROLE_SECTION
;
3981 ia_role
= ROLE_SYSTEM_PANE
;
3984 case ui::AX_ROLE_ROW
:
3985 ia_role
= ROLE_SYSTEM_ROW
;
3987 case ui::AX_ROLE_ROW_HEADER
:
3988 ia_role
= ROLE_SYSTEM_ROWHEADER
;
3990 case ui::AX_ROLE_RUBY
:
3991 ia_role
= ROLE_SYSTEM_TEXT
;
3992 ia2_role
= IA2_ROLE_TEXT_FRAME
;
3994 case ui::AX_ROLE_RULER
:
3995 ia_role
= ROLE_SYSTEM_CLIENT
;
3996 ia2_role
= IA2_ROLE_RULER
;
3997 ia_state
|= STATE_SYSTEM_READONLY
;
3999 case ui::AX_ROLE_SCROLL_AREA
:
4000 ia_role
= ROLE_SYSTEM_CLIENT
;
4001 ia2_role
= IA2_ROLE_SCROLL_PANE
;
4002 ia_state
|= STATE_SYSTEM_READONLY
;
4003 ia2_state
&= ~(IA2_STATE_EDITABLE
);
4005 case ui::AX_ROLE_SCROLL_BAR
:
4006 ia_role
= ROLE_SYSTEM_SCROLLBAR
;
4008 case ui::AX_ROLE_SEARCH
:
4009 ia_role
= ROLE_SYSTEM_GROUPING
;
4010 ia2_role
= IA2_ROLE_SECTION
;
4012 case ui::AX_ROLE_SLIDER
:
4013 ia_role
= ROLE_SYSTEM_SLIDER
;
4015 case ui::AX_ROLE_SPIN_BUTTON
:
4016 ia_role
= ROLE_SYSTEM_SPINBUTTON
;
4018 case ui::AX_ROLE_SPIN_BUTTON_PART
:
4019 ia_role
= ROLE_SYSTEM_PUSHBUTTON
;
4021 case ui::AX_ROLE_ANNOTATION
:
4022 case ui::AX_ROLE_LIST_MARKER
:
4023 case ui::AX_ROLE_STATIC_TEXT
:
4024 ia_role
= ROLE_SYSTEM_STATICTEXT
;
4026 case ui::AX_ROLE_STATUS
:
4027 ia_role
= ROLE_SYSTEM_STATUSBAR
;
4029 case ui::AX_ROLE_SPLITTER
:
4030 ia_role
= ROLE_SYSTEM_SEPARATOR
;
4032 case ui::AX_ROLE_SVG_ROOT
:
4033 ia_role
= ROLE_SYSTEM_GRAPHIC
;
4035 case ui::AX_ROLE_SWITCH
:
4036 role_name
= L
"switch";
4037 ia2_role
= IA2_ROLE_TOGGLE_BUTTON
;
4039 case ui::AX_ROLE_TAB
:
4040 ia_role
= ROLE_SYSTEM_PAGETAB
;
4042 case ui::AX_ROLE_TABLE
: {
4043 base::string16 aria_role
= GetString16Attribute(
4045 if (aria_role
== L
"treegrid") {
4046 ia_role
= ROLE_SYSTEM_OUTLINE
;
4048 ia_role
= ROLE_SYSTEM_TABLE
;
4052 case ui::AX_ROLE_TABLE_HEADER_CONTAINER
:
4053 ia_role
= ROLE_SYSTEM_GROUPING
;
4054 ia2_role
= IA2_ROLE_SECTION
;
4055 ia_state
|= STATE_SYSTEM_READONLY
;
4057 case ui::AX_ROLE_TAB_LIST
:
4058 ia_role
= ROLE_SYSTEM_PAGETABLIST
;
4060 case ui::AX_ROLE_TAB_PANEL
:
4061 ia_role
= ROLE_SYSTEM_PROPERTYPAGE
;
4063 case ui::AX_ROLE_TOGGLE_BUTTON
:
4064 ia_role
= ROLE_SYSTEM_PUSHBUTTON
;
4065 ia2_role
= IA2_ROLE_TOGGLE_BUTTON
;
4067 case ui::AX_ROLE_TEXT_AREA
:
4068 ia_role
= ROLE_SYSTEM_TEXT
;
4069 ia2_state
|= IA2_STATE_MULTI_LINE
;
4070 ia2_state
|= IA2_STATE_EDITABLE
;
4071 ia2_state
|= IA2_STATE_SELECTABLE_TEXT
;
4073 case ui::AX_ROLE_TEXT_FIELD
:
4074 case ui::AX_ROLE_SEARCH_BOX
:
4075 ia_role
= ROLE_SYSTEM_TEXT
;
4076 ia2_state
|= IA2_STATE_SINGLE_LINE
;
4077 ia2_state
|= IA2_STATE_EDITABLE
;
4078 ia2_state
|= IA2_STATE_SELECTABLE_TEXT
;
4080 case ui::AX_ROLE_TIME
:
4081 ia_role
= ROLE_SYSTEM_SPINBUTTON
;
4083 case ui::AX_ROLE_TIMER
:
4084 ia_role
= ROLE_SYSTEM_CLOCK
;
4085 ia_state
|= STATE_SYSTEM_READONLY
;
4087 case ui::AX_ROLE_TOOLBAR
:
4088 ia_role
= ROLE_SYSTEM_TOOLBAR
;
4089 ia_state
|= STATE_SYSTEM_READONLY
;
4091 case ui::AX_ROLE_TOOLTIP
:
4092 ia_role
= ROLE_SYSTEM_TOOLTIP
;
4093 ia_state
|= STATE_SYSTEM_READONLY
;
4095 case ui::AX_ROLE_TREE
:
4096 ia_role
= ROLE_SYSTEM_OUTLINE
;
4098 case ui::AX_ROLE_TREE_GRID
:
4099 ia_role
= ROLE_SYSTEM_OUTLINE
;
4101 case ui::AX_ROLE_TREE_ITEM
:
4102 ia_role
= ROLE_SYSTEM_OUTLINEITEM
;
4104 case ui::AX_ROLE_LINE_BREAK
:
4105 ia_role
= ROLE_SYSTEM_WHITESPACE
;
4107 case ui::AX_ROLE_WINDOW
:
4108 ia_role
= ROLE_SYSTEM_WINDOW
;
4111 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
4112 case ui::AX_ROLE_DIRECTORY
:
4113 case ui::AX_ROLE_IGNORED
:
4114 case ui::AX_ROLE_LOG
:
4115 case ui::AX_ROLE_NONE
:
4116 case ui::AX_ROLE_PRESENTATIONAL
:
4117 case ui::AX_ROLE_SLIDER_THUMB
:
4119 ia_role
= ROLE_SYSTEM_CLIENT
;
4123 // Compute the final value of READONLY for MSAA.
4125 // We always set the READONLY state for elements that have the
4126 // aria-readonly attribute and for a few roles (in the switch above).
4127 // We clear the READONLY state on focusable controls and on a document.
4128 // Everything else, the majority of objects, do not have this state set.
4129 if (HasState(ui::AX_STATE_FOCUSABLE
) &&
4130 ia_role
!= ROLE_SYSTEM_DOCUMENT
) {
4131 ia_state
&= ~(STATE_SYSTEM_READONLY
);
4133 if (!HasState(ui::AX_STATE_READ_ONLY
))
4134 ia_state
&= ~(STATE_SYSTEM_READONLY
);
4135 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY
))
4136 ia_state
|= STATE_SYSTEM_READONLY
;
4138 // The role should always be set.
4139 DCHECK(!role_name
.empty() || ia_role
);
4141 // If we didn't explicitly set the IAccessible2 role, make it the same
4142 // as the MSAA role.
4146 win_attributes_
->ia_role
= ia_role
;
4147 win_attributes_
->ia_state
= ia_state
;
4148 win_attributes_
->role_name
= role_name
;
4149 win_attributes_
->ia2_role
= ia2_role
;
4150 win_attributes_
->ia2_state
= ia2_state
;
4153 } // namespace content