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/base/accessibility/accessible_text_utils.h"
22 #include "ui/base/win/accessibility_ids_win.h"
23 #include "ui/base/win/accessibility_misc_utils.h"
27 // These nonstandard GUIDs are taken directly from the Mozilla sources
28 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here:
29 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA
30 const GUID GUID_ISimpleDOM
= {
31 0x0c539790, 0x12e4, 0x11cf,
32 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
33 const GUID GUID_IAccessibleContentDocument
= {
34 0xa5d8e1f3, 0x3571, 0x4d8f,
35 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e};
37 const base::char16
BrowserAccessibilityWin::kEmbeddedCharacter
[] = L
"\xfffc";
40 LONG
BrowserAccessibilityWin::next_unique_id_win_
=
41 base::win::kFirstBrowserAccessibilityManagerAccessibilityId
;
44 // BrowserAccessibilityRelation
46 // A simple implementation of IAccessibleRelation, used to represent
47 // a relationship between two accessible nodes in the tree.
50 class BrowserAccessibilityRelation
51 : public CComObjectRootEx
<CComMultiThreadModel
>,
52 public IAccessibleRelation
{
53 BEGIN_COM_MAP(BrowserAccessibilityRelation
)
54 COM_INTERFACE_ENTRY(IAccessibleRelation
)
57 CONTENT_EXPORT
BrowserAccessibilityRelation() {}
58 CONTENT_EXPORT
virtual ~BrowserAccessibilityRelation() {}
60 CONTENT_EXPORT
void Initialize(BrowserAccessibilityWin
* owner
,
61 const base::string16
& type
);
62 CONTENT_EXPORT
void AddTarget(int target_id
);
64 // IAccessibleRelation methods.
65 CONTENT_EXPORT STDMETHODIMP
get_relationType(BSTR
* relation_type
);
66 CONTENT_EXPORT STDMETHODIMP
get_nTargets(long* n_targets
);
67 CONTENT_EXPORT STDMETHODIMP
get_target(long target_index
, IUnknown
** target
);
68 CONTENT_EXPORT STDMETHODIMP
get_targets(long max_targets
,
72 // IAccessibleRelation methods not implemented.
73 CONTENT_EXPORT STDMETHODIMP
get_localizedRelationType(BSTR
* relation_type
) {
79 base::win::ScopedComPtr
<BrowserAccessibilityWin
> owner_
;
80 std::vector
<int> target_ids_
;
83 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin
* owner
,
84 const base::string16
& type
) {
89 void BrowserAccessibilityRelation::AddTarget(int target_id
) {
90 target_ids_
.push_back(target_id
);
93 STDMETHODIMP
BrowserAccessibilityRelation::get_relationType(
94 BSTR
* relation_type
) {
98 if (!owner_
->instance_active())
101 *relation_type
= SysAllocString(type_
.c_str());
102 DCHECK(*relation_type
);
106 STDMETHODIMP
BrowserAccessibilityRelation::get_nTargets(long* n_targets
) {
110 if (!owner_
->instance_active())
113 *n_targets
= static_cast<long>(target_ids_
.size());
115 BrowserAccessibilityManager
* manager
= owner_
->manager();
116 for (long i
= *n_targets
- 1; i
>= 0; --i
) {
117 BrowserAccessibility
* result
= manager
->GetFromRendererID(target_ids_
[i
]);
118 if (!result
|| !result
->instance_active()) {
126 STDMETHODIMP
BrowserAccessibilityRelation::get_target(long target_index
,
131 if (!owner_
->instance_active())
134 if (target_index
< 0 ||
135 target_index
>= static_cast<long>(target_ids_
.size())) {
139 BrowserAccessibilityManager
* manager
= owner_
->manager();
140 BrowserAccessibility
* result
=
141 manager
->GetFromRendererID(target_ids_
[target_index
]);
142 if (!result
|| !result
->instance_active())
145 *target
= static_cast<IAccessible
*>(
146 result
->ToBrowserAccessibilityWin()->NewReference());
150 STDMETHODIMP
BrowserAccessibilityRelation::get_targets(long max_targets
,
153 if (!targets
|| !n_targets
)
156 if (!owner_
->instance_active())
159 long count
= static_cast<long>(target_ids_
.size());
160 if (count
> max_targets
)
167 for (long i
= 0; i
< count
; ++i
) {
168 HRESULT result
= get_target(i
, &targets
[i
]);
177 // BrowserAccessibilityWin
181 BrowserAccessibility
* BrowserAccessibility::Create() {
182 CComObject
<BrowserAccessibilityWin
>* instance
;
183 HRESULT hr
= CComObject
<BrowserAccessibilityWin
>::CreateInstance(&instance
);
184 DCHECK(SUCCEEDED(hr
));
185 return instance
->NewReference();
188 BrowserAccessibilityWin
* BrowserAccessibility::ToBrowserAccessibilityWin() {
189 return static_cast<BrowserAccessibilityWin
*>(this);
192 BrowserAccessibilityWin::BrowserAccessibilityWin()
199 previous_scroll_x_(0),
200 previous_scroll_y_(0) {
201 // Start unique IDs at -1 and decrement each time, because get_accChild
202 // uses positive IDs to enumerate children, so we use negative IDs to
203 // clearly distinguish between indices and unique IDs.
204 unique_id_win_
= next_unique_id_win_
;
205 if (next_unique_id_win_
==
206 base::win::kLastBrowserAccessibilityManagerAccessibilityId
) {
207 next_unique_id_win_
=
208 base::win::kFirstBrowserAccessibilityManagerAccessibilityId
;
210 next_unique_id_win_
--;
213 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
214 for (size_t i
= 0; i
< relations_
.size(); ++i
)
215 relations_
[i
]->Release();
219 // IAccessible methods.
222 // * Always test for instance_active() first and return E_FAIL if it's false.
223 // * Always check for invalid arguments first, even if they're unused.
224 // * Return S_FALSE if the only output is a string argument and it's empty.
227 HRESULT
BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id
) {
228 if (!instance_active())
231 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
235 manager()->DoDefaultAction(*target
);
239 STDMETHODIMP
BrowserAccessibilityWin::accHitTest(LONG x_left
,
242 if (!instance_active())
248 gfx::Point
point(x_left
, y_top
);
249 if (!GetGlobalBoundsRect().Contains(point
)) {
250 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries.
251 child
->vt
= VT_EMPTY
;
255 BrowserAccessibility
* result
= BrowserAccessibilityForPoint(point
);
256 if (result
== this) {
257 // Point is within this object.
259 child
->lVal
= CHILDID_SELF
;
261 child
->vt
= VT_DISPATCH
;
262 child
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
267 STDMETHODIMP
BrowserAccessibilityWin::accLocation(LONG
* x_left
,
272 if (!instance_active())
275 if (!x_left
|| !y_top
|| !width
|| !height
)
278 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
282 gfx::Rect bounds
= target
->GetGlobalBoundsRect();
283 *x_left
= bounds
.x();
285 *width
= bounds
.width();
286 *height
= bounds
.height();
291 STDMETHODIMP
BrowserAccessibilityWin::accNavigate(LONG nav_dir
,
294 BrowserAccessibilityWin
* target
= GetTargetFromChildID(start
);
298 if ((nav_dir
== NAVDIR_LASTCHILD
|| nav_dir
== NAVDIR_FIRSTCHILD
) &&
299 start
.lVal
!= CHILDID_SELF
) {
300 // MSAA states that navigating to first/last child can only be from self.
304 uint32 child_count
= target
->PlatformChildCount();
306 BrowserAccessibility
* result
= NULL
;
312 // These directions are not implemented, matching Mozilla and IE.
314 case NAVDIR_FIRSTCHILD
:
316 result
= target
->PlatformGetChild(0);
318 case NAVDIR_LASTCHILD
:
320 result
= target
->PlatformGetChild(child_count
- 1);
323 result
= target
->GetNextSibling();
325 case NAVDIR_PREVIOUS
:
326 result
= target
->GetPreviousSibling();
335 end
->vt
= VT_DISPATCH
;
336 end
->pdispVal
= result
->ToBrowserAccessibilityWin()->NewReference();
340 STDMETHODIMP
BrowserAccessibilityWin::get_accChild(VARIANT var_child
,
341 IDispatch
** disp_child
) {
342 if (!instance_active())
350 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_child
);
354 (*disp_child
) = target
->NewReference();
358 STDMETHODIMP
BrowserAccessibilityWin::get_accChildCount(LONG
* child_count
) {
359 if (!instance_active())
365 *child_count
= PlatformChildCount();
370 STDMETHODIMP
BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id
,
372 if (!instance_active())
378 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
382 return target
->GetStringAttributeAsBstr(
383 ui::AX_ATTR_SHORTCUT
, def_action
);
386 STDMETHODIMP
BrowserAccessibilityWin::get_accDescription(VARIANT var_id
,
388 if (!instance_active())
394 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
398 return target
->GetStringAttributeAsBstr(
399 ui::AX_ATTR_DESCRIPTION
, desc
);
402 STDMETHODIMP
BrowserAccessibilityWin::get_accFocus(VARIANT
* focus_child
) {
403 if (!instance_active())
409 BrowserAccessibilityWin
* focus
= static_cast<BrowserAccessibilityWin
*>(
410 manager()->GetFocus(this));
412 focus_child
->vt
= VT_I4
;
413 focus_child
->lVal
= CHILDID_SELF
;
414 } else if (focus
== NULL
) {
415 focus_child
->vt
= VT_EMPTY
;
417 focus_child
->vt
= VT_DISPATCH
;
418 focus_child
->pdispVal
= focus
->NewReference();
424 STDMETHODIMP
BrowserAccessibilityWin::get_accHelp(VARIANT var_id
, BSTR
* help
) {
425 if (!instance_active())
431 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
435 return target
->GetStringAttributeAsBstr(
436 ui::AX_ATTR_HELP
, help
);
439 STDMETHODIMP
BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id
,
441 if (!instance_active())
447 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
451 return target
->GetStringAttributeAsBstr(
452 ui::AX_ATTR_SHORTCUT
, acc_key
);
455 STDMETHODIMP
BrowserAccessibilityWin::get_accName(VARIANT var_id
, BSTR
* name
) {
456 if (!instance_active())
462 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
466 std::string name_str
= target
->name();
468 // If the name is empty, see if it's labeled by another element.
469 if (name_str
.empty()) {
471 if (target
->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT
,
473 BrowserAccessibility
* title_elem
=
474 manager()->GetFromRendererID(title_elem_id
);
476 name_str
= title_elem
->GetTextRecursive();
480 if (name_str
.empty())
483 *name
= SysAllocString(base::UTF8ToUTF16(name_str
).c_str());
489 STDMETHODIMP
BrowserAccessibilityWin::get_accParent(IDispatch
** disp_parent
) {
490 if (!instance_active())
496 IAccessible
* parent_obj
= parent()->ToBrowserAccessibilityWin();
497 if (parent_obj
== NULL
) {
498 // This happens if we're the root of the tree;
499 // return the IAccessible for the window.
501 manager()->ToBrowserAccessibilityManagerWin()->parent_iaccessible();
503 // |parent| can only be NULL if the manager was created before the parent
504 // IAccessible was known and it wasn't subsequently set before a client
505 // requested it. Crash hard if this happens so that we get crash reports.
509 parent_obj
->AddRef();
510 *disp_parent
= parent_obj
;
514 STDMETHODIMP
BrowserAccessibilityWin::get_accRole(VARIANT var_id
,
516 if (!instance_active())
522 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
526 if (!target
->role_name_
.empty()) {
528 role
->bstrVal
= SysAllocString(target
->role_name_
.c_str());
531 role
->lVal
= target
->ia_role_
;
536 STDMETHODIMP
BrowserAccessibilityWin::get_accState(VARIANT var_id
,
538 if (!instance_active())
544 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
549 state
->lVal
= target
->ia_state_
;
550 if (manager()->GetFocus(NULL
) == this)
551 state
->lVal
|= STATE_SYSTEM_FOCUSED
;
556 STDMETHODIMP
BrowserAccessibilityWin::get_accValue(VARIANT var_id
,
558 if (!instance_active())
564 BrowserAccessibilityWin
* target
= GetTargetFromChildID(var_id
);
568 if (target
->ia_role() == ROLE_SYSTEM_PROGRESSBAR
||
569 target
->ia_role() == ROLE_SYSTEM_SCROLLBAR
||
570 target
->ia_role() == ROLE_SYSTEM_SLIDER
) {
571 base::string16 value_text
= target
->GetValueText();
572 *value
= SysAllocString(value_text
.c_str());
577 // Expose color well value.
578 if (target
->ia2_role() == IA2_ROLE_COLOR_CHOOSER
) {
579 int r
= target
->GetIntAttribute(
580 ui::AX_ATTR_COLOR_VALUE_RED
);
581 int g
= target
->GetIntAttribute(
582 ui::AX_ATTR_COLOR_VALUE_GREEN
);
583 int b
= target
->GetIntAttribute(
584 ui::AX_ATTR_COLOR_VALUE_BLUE
);
585 base::string16 value_text
;
586 value_text
= base::IntToString16((r
* 100) / 255) + L
"% red " +
587 base::IntToString16((g
* 100) / 255) + L
"% green " +
588 base::IntToString16((b
* 100) / 255) + L
"% blue";
589 *value
= SysAllocString(value_text
.c_str());
594 *value
= SysAllocString(base::UTF8ToUTF16(target
->value()).c_str());
599 STDMETHODIMP
BrowserAccessibilityWin::get_accHelpTopic(BSTR
* help_file
,
605 STDMETHODIMP
BrowserAccessibilityWin::get_accSelection(VARIANT
* selected
) {
606 if (!instance_active())
609 if (blink_role() != ui::AX_ROLE_LIST_BOX
)
612 unsigned long selected_count
= 0;
613 for (size_t i
= 0; i
< children().size(); ++i
) {
614 if (children()[i
]->HasState(ui::AX_STATE_SELECTED
))
618 if (selected_count
== 0) {
619 selected
->vt
= VT_EMPTY
;
623 if (selected_count
== 1) {
624 for (size_t i
= 0; i
< children().size(); ++i
) {
625 if (children()[i
]->HasState(ui::AX_STATE_SELECTED
)) {
626 selected
->vt
= VT_DISPATCH
;
628 children()[i
]->ToBrowserAccessibilityWin()->NewReference();
634 // Multiple items are selected.
635 base::win::EnumVariant
* enum_variant
=
636 new base::win::EnumVariant(selected_count
);
637 enum_variant
->AddRef();
638 unsigned long index
= 0;
639 for (size_t i
= 0; i
< children().size(); ++i
) {
640 if (children()[i
]->HasState(ui::AX_STATE_SELECTED
)) {
641 enum_variant
->ItemAt(index
)->vt
= VT_DISPATCH
;
642 enum_variant
->ItemAt(index
)->pdispVal
=
643 children()[i
]->ToBrowserAccessibilityWin()->NewReference();
647 selected
->vt
= VT_UNKNOWN
;
648 selected
->punkVal
= static_cast<IUnknown
*>(
649 static_cast<base::win::IUnknownImpl
*>(enum_variant
));
653 STDMETHODIMP
BrowserAccessibilityWin::accSelect(
654 LONG flags_sel
, VARIANT var_id
) {
655 if (!instance_active())
658 if (flags_sel
& SELFLAG_TAKEFOCUS
) {
659 manager()->SetFocus(this, true);
667 // IAccessible2 methods.
670 STDMETHODIMP
BrowserAccessibilityWin::role(LONG
* role
) {
671 if (!instance_active())
682 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(BSTR
* attributes
) {
683 if (!instance_active())
689 // The iaccessible2 attributes are a set of key-value pairs
690 // separated by semicolons, with a colon between the key and the value.
692 for (unsigned int i
= 0; i
< ia2_attributes_
.size(); ++i
) {
695 str
+= ia2_attributes_
[i
];
701 *attributes
= SysAllocString(str
.c_str());
706 STDMETHODIMP
BrowserAccessibilityWin::get_states(AccessibleStates
* states
) {
707 if (!instance_active())
713 *states
= ia2_state_
;
718 STDMETHODIMP
BrowserAccessibilityWin::get_uniqueID(LONG
* unique_id
) {
719 if (!instance_active())
725 *unique_id
= unique_id_win_
;
729 STDMETHODIMP
BrowserAccessibilityWin::get_windowHandle(HWND
* window_handle
) {
730 if (!instance_active())
736 *window_handle
= manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
740 STDMETHODIMP
BrowserAccessibilityWin::get_indexInParent(LONG
* index_in_parent
) {
741 if (!instance_active())
744 if (!index_in_parent
)
747 *index_in_parent
= this->index_in_parent();
751 STDMETHODIMP
BrowserAccessibilityWin::get_nRelations(LONG
* n_relations
) {
752 if (!instance_active())
758 *n_relations
= relations_
.size();
762 STDMETHODIMP
BrowserAccessibilityWin::get_relation(
764 IAccessibleRelation
** relation
) {
765 if (!instance_active())
768 if (relation_index
< 0 ||
769 relation_index
>= static_cast<long>(relations_
.size())) {
776 relations_
[relation_index
]->AddRef();
777 *relation
= relations_
[relation_index
];
781 STDMETHODIMP
BrowserAccessibilityWin::get_relations(
783 IAccessibleRelation
** relations
,
785 if (!instance_active())
788 if (!relations
|| !n_relations
)
791 long count
= static_cast<long>(relations_
.size());
792 *n_relations
= count
;
796 for (long i
= 0; i
< count
; ++i
) {
797 relations_
[i
]->AddRef();
798 relations
[i
] = relations_
[i
];
804 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type
) {
805 if (!instance_active())
808 gfx::Rect r
= location();
809 switch(scroll_type
) {
810 case IA2_SCROLL_TYPE_TOP_LEFT
:
811 manager()->ScrollToMakeVisible(*this, gfx::Rect(r
.x(), r
.y(), 0, 0));
813 case IA2_SCROLL_TYPE_BOTTOM_RIGHT
:
814 manager()->ScrollToMakeVisible(
815 *this, gfx::Rect(r
.right(), r
.bottom(), 0, 0));
817 case IA2_SCROLL_TYPE_TOP_EDGE
:
818 manager()->ScrollToMakeVisible(
819 *this, gfx::Rect(r
.x(), r
.y(), r
.width(), 0));
821 case IA2_SCROLL_TYPE_BOTTOM_EDGE
:
822 manager()->ScrollToMakeVisible(
823 *this, gfx::Rect(r
.x(), r
.bottom(), r
.width(), 0));
825 case IA2_SCROLL_TYPE_LEFT_EDGE
:
826 manager()->ScrollToMakeVisible(
827 *this, gfx::Rect(r
.x(), r
.y(), 0, r
.height()));
829 case IA2_SCROLL_TYPE_RIGHT_EDGE
:
830 manager()->ScrollToMakeVisible(
831 *this, gfx::Rect(r
.right(), r
.y(), 0, r
.height()));
833 case IA2_SCROLL_TYPE_ANYWHERE
:
835 manager()->ScrollToMakeVisible(*this, r
);
839 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
844 STDMETHODIMP
BrowserAccessibilityWin::scrollToPoint(
845 enum IA2CoordinateType coordinate_type
,
848 if (!instance_active())
851 gfx::Point
scroll_to(x
, y
);
853 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
854 scroll_to
-= manager()->GetViewBounds().OffsetFromOrigin();
855 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
857 scroll_to
+= parent()->location().OffsetFromOrigin();
862 manager()->ScrollToPoint(*this, scroll_to
);
863 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
868 STDMETHODIMP
BrowserAccessibilityWin::get_groupPosition(
870 LONG
* similar_items_in_group
,
871 LONG
* position_in_group
) {
872 if (!instance_active())
875 if (!group_level
|| !similar_items_in_group
|| !position_in_group
)
878 if (blink_role() == ui::AX_ROLE_LIST_BOX_OPTION
&&
880 parent()->role() == ui::AX_ROLE_LIST_BOX
) {
882 *similar_items_in_group
= parent()->PlatformChildCount();
883 *position_in_group
= index_in_parent() + 1;
891 // IAccessibleApplication methods.
894 STDMETHODIMP
BrowserAccessibilityWin::get_appName(BSTR
* app_name
) {
895 // No need to check |instance_active()| because this interface is
896 // global, and doesn't depend on any local state.
901 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
902 // the part before the "/".
903 std::vector
<std::string
> product_components
;
904 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
905 DCHECK_EQ(2U, product_components
.size());
906 if (product_components
.size() != 2)
908 *app_name
= SysAllocString(base::UTF8ToUTF16(product_components
[0]).c_str());
910 return *app_name
? S_OK
: E_FAIL
;
913 STDMETHODIMP
BrowserAccessibilityWin::get_appVersion(BSTR
* app_version
) {
914 // No need to check |instance_active()| because this interface is
915 // global, and doesn't depend on any local state.
920 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
921 // the part after the "/".
922 std::vector
<std::string
> product_components
;
923 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components
);
924 DCHECK_EQ(2U, product_components
.size());
925 if (product_components
.size() != 2)
928 SysAllocString(base::UTF8ToUTF16(product_components
[1]).c_str());
929 DCHECK(*app_version
);
930 return *app_version
? S_OK
: E_FAIL
;
933 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitName(BSTR
* toolkit_name
) {
934 // No need to check |instance_active()| because this interface is
935 // global, and doesn't depend on any local state.
940 // This is hard-coded; all products based on the Chromium engine
941 // will have the same toolkit name, so that assistive technology can
942 // detect any Chrome-based product.
943 *toolkit_name
= SysAllocString(L
"Chrome");
944 DCHECK(*toolkit_name
);
945 return *toolkit_name
? S_OK
: E_FAIL
;
948 STDMETHODIMP
BrowserAccessibilityWin::get_toolkitVersion(
949 BSTR
* toolkit_version
) {
950 // No need to check |instance_active()| because this interface is
951 // global, and doesn't depend on any local state.
953 if (!toolkit_version
)
956 std::string user_agent
= GetContentClient()->GetUserAgent();
957 *toolkit_version
= SysAllocString(base::UTF8ToUTF16(user_agent
).c_str());
958 DCHECK(*toolkit_version
);
959 return *toolkit_version
? S_OK
: E_FAIL
;
963 // IAccessibleImage methods.
966 STDMETHODIMP
BrowserAccessibilityWin::get_description(BSTR
* desc
) {
967 if (!instance_active())
973 return GetStringAttributeAsBstr(
974 ui::AX_ATTR_DESCRIPTION
, desc
);
977 STDMETHODIMP
BrowserAccessibilityWin::get_imagePosition(
978 enum IA2CoordinateType coordinate_type
,
981 if (!instance_active())
987 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
989 manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd();
990 POINT top_left
= {0, 0};
991 ::ClientToScreen(parent_hwnd
, &top_left
);
992 *x
= location().x() + top_left
.x
;
993 *y
= location().y() + top_left
.y
;
994 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
998 *x
-= parent()->location().x();
999 *y
-= parent()->location().y();
1002 return E_INVALIDARG
;
1008 STDMETHODIMP
BrowserAccessibilityWin::get_imageSize(LONG
* height
, LONG
* width
) {
1009 if (!instance_active())
1012 if (!height
|| !width
)
1013 return E_INVALIDARG
;
1015 *height
= location().height();
1016 *width
= location().width();
1021 // IAccessibleTable methods.
1024 STDMETHODIMP
BrowserAccessibilityWin::get_accessibleAt(
1027 IUnknown
** accessible
) {
1028 if (!instance_active())
1032 return E_INVALIDARG
;
1036 if (!GetIntAttribute(
1037 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1039 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1045 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1046 return E_INVALIDARG
;
1048 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1049 ui::AX_ATTR_CELL_IDS
);
1050 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1052 int cell_id
= cell_ids
[row
* columns
+ column
];
1053 BrowserAccessibilityWin
* cell
= GetFromRendererID(cell_id
);
1055 *accessible
= static_cast<IAccessible
*>(cell
->NewReference());
1060 return E_INVALIDARG
;
1063 STDMETHODIMP
BrowserAccessibilityWin::get_caption(IUnknown
** accessible
) {
1064 if (!instance_active())
1068 return E_INVALIDARG
;
1070 // TODO(dmazzoni): implement
1074 STDMETHODIMP
BrowserAccessibilityWin::get_childIndex(long row
,
1077 if (!instance_active())
1081 return E_INVALIDARG
;
1085 if (!GetIntAttribute(
1086 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1088 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1094 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1095 return E_INVALIDARG
;
1097 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1098 ui::AX_ATTR_CELL_IDS
);
1099 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1100 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1101 DCHECK_EQ(columns
* rows
, static_cast<int>(cell_ids
.size()));
1102 int cell_id
= cell_ids
[row
* columns
+ column
];
1103 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
1104 if (unique_cell_ids
[i
] == cell_id
) {
1105 *cell_index
= (long)i
;
1113 STDMETHODIMP
BrowserAccessibilityWin::get_columnDescription(long column
,
1114 BSTR
* description
) {
1115 if (!instance_active())
1119 return E_INVALIDARG
;
1123 if (!GetIntAttribute(
1124 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1125 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1131 if (column
< 0 || column
>= columns
)
1132 return E_INVALIDARG
;
1134 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1135 ui::AX_ATTR_CELL_IDS
);
1136 for (int i
= 0; i
< rows
; ++i
) {
1137 int cell_id
= cell_ids
[i
* columns
+ column
];
1138 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1139 manager()->GetFromRendererID(cell_id
));
1140 if (cell
&& cell
->blink_role() == ui::AX_ROLE_COLUMN_HEADER
) {
1141 base::string16 cell_name
= cell
->GetString16Attribute(
1143 if (cell_name
.size() > 0) {
1144 *description
= SysAllocString(cell_name
.c_str());
1148 return cell
->GetStringAttributeAsBstr(
1149 ui::AX_ATTR_DESCRIPTION
, description
);
1156 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtentAt(
1159 long* n_columns_spanned
) {
1160 if (!instance_active())
1163 if (!n_columns_spanned
)
1164 return E_INVALIDARG
;
1168 if (!GetIntAttribute(
1169 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1170 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1176 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1177 return E_INVALIDARG
;
1179 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1180 ui::AX_ATTR_CELL_IDS
);
1181 int cell_id
= cell_ids
[row
* columns
+ column
];
1182 BrowserAccessibilityWin
* cell
= static_cast<BrowserAccessibilityWin
*>(
1183 manager()->GetFromRendererID(cell_id
));
1186 cell
->GetIntAttribute(
1187 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1189 *n_columns_spanned
= colspan
;
1196 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeader(
1197 IAccessibleTable
** accessible_table
,
1198 long* starting_row_index
) {
1199 // TODO(dmazzoni): implement
1203 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long cell_index
,
1204 long* column_index
) {
1205 if (!instance_active())
1209 return E_INVALIDARG
;
1211 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1212 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1213 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1215 return E_INVALIDARG
;
1216 if (cell_index
>= cell_id_count
)
1219 int cell_id
= unique_cell_ids
[cell_index
];
1220 BrowserAccessibilityWin
* cell
=
1221 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1224 cell
->GetIntAttribute(
1225 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &col_index
)) {
1226 *column_index
= col_index
;
1233 STDMETHODIMP
BrowserAccessibilityWin::get_nColumns(long* column_count
) {
1234 if (!instance_active())
1238 return E_INVALIDARG
;
1241 if (GetIntAttribute(
1242 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
)) {
1243 *column_count
= columns
;
1250 STDMETHODIMP
BrowserAccessibilityWin::get_nRows(long* row_count
) {
1251 if (!instance_active())
1255 return E_INVALIDARG
;
1258 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1266 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count
) {
1267 if (!instance_active())
1271 return E_INVALIDARG
;
1273 // TODO(dmazzoni): add support for selected cells/rows/columns in tables.
1278 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedColumns(long* column_count
) {
1279 if (!instance_active())
1283 return E_INVALIDARG
;
1289 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedRows(long* row_count
) {
1290 if (!instance_active())
1294 return E_INVALIDARG
;
1300 STDMETHODIMP
BrowserAccessibilityWin::get_rowDescription(long row
,
1301 BSTR
* description
) {
1302 if (!instance_active())
1306 return E_INVALIDARG
;
1310 if (!GetIntAttribute(
1311 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1312 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1318 if (row
< 0 || row
>= rows
)
1319 return E_INVALIDARG
;
1321 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1322 ui::AX_ATTR_CELL_IDS
);
1323 for (int i
= 0; i
< columns
; ++i
) {
1324 int cell_id
= cell_ids
[row
* columns
+ i
];
1325 BrowserAccessibilityWin
* cell
=
1326 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1327 if (cell
&& cell
->blink_role() == ui::AX_ROLE_ROW_HEADER
) {
1328 base::string16 cell_name
= cell
->GetString16Attribute(
1330 if (cell_name
.size() > 0) {
1331 *description
= SysAllocString(cell_name
.c_str());
1335 return cell
->GetStringAttributeAsBstr(
1336 ui::AX_ATTR_DESCRIPTION
, description
);
1343 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtentAt(long row
,
1345 long* n_rows_spanned
) {
1346 if (!instance_active())
1349 if (!n_rows_spanned
)
1350 return E_INVALIDARG
;
1354 if (!GetIntAttribute(
1355 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1356 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
) ||
1362 if (row
< 0 || row
>= rows
|| column
< 0 || column
>= columns
)
1363 return E_INVALIDARG
;
1365 const std::vector
<int32
>& cell_ids
= GetIntListAttribute(
1366 ui::AX_ATTR_CELL_IDS
);
1367 int cell_id
= cell_ids
[row
* columns
+ column
];
1368 BrowserAccessibilityWin
* cell
=
1369 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1372 cell
->GetIntAttribute(
1373 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1375 *n_rows_spanned
= rowspan
;
1382 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeader(
1383 IAccessibleTable
** accessible_table
,
1384 long* starting_column_index
) {
1385 // TODO(dmazzoni): implement
1389 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long cell_index
,
1391 if (!instance_active())
1395 return E_INVALIDARG
;
1397 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1398 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1399 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1401 return E_INVALIDARG
;
1402 if (cell_index
>= cell_id_count
)
1405 int cell_id
= unique_cell_ids
[cell_index
];
1406 BrowserAccessibilityWin
* cell
=
1407 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1410 cell
->GetIntAttribute(
1411 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &cell_row_index
)) {
1412 *row_index
= cell_row_index
;
1419 STDMETHODIMP
BrowserAccessibilityWin::get_selectedChildren(long max_children
,
1422 if (!instance_active())
1425 if (!children
|| !n_children
)
1426 return E_INVALIDARG
;
1428 // TODO(dmazzoni): Implement this.
1433 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long max_columns
,
1436 if (!instance_active())
1439 if (!columns
|| !n_columns
)
1440 return E_INVALIDARG
;
1442 // TODO(dmazzoni): Implement this.
1447 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long max_rows
,
1450 if (!instance_active())
1453 if (!rows
|| !n_rows
)
1454 return E_INVALIDARG
;
1456 // TODO(dmazzoni): Implement this.
1461 STDMETHODIMP
BrowserAccessibilityWin::get_summary(IUnknown
** accessible
) {
1462 if (!instance_active())
1466 return E_INVALIDARG
;
1468 // TODO(dmazzoni): implement
1472 STDMETHODIMP
BrowserAccessibilityWin::get_isColumnSelected(
1474 boolean
* is_selected
) {
1475 if (!instance_active())
1479 return E_INVALIDARG
;
1481 // TODO(dmazzoni): Implement this.
1482 *is_selected
= false;
1486 STDMETHODIMP
BrowserAccessibilityWin::get_isRowSelected(long row
,
1487 boolean
* is_selected
) {
1488 if (!instance_active())
1492 return E_INVALIDARG
;
1494 // TODO(dmazzoni): Implement this.
1495 *is_selected
= false;
1499 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(long row
,
1501 boolean
* is_selected
) {
1502 if (!instance_active())
1506 return E_INVALIDARG
;
1508 // TODO(dmazzoni): Implement this.
1509 *is_selected
= false;
1513 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtentsAtIndex(
1518 long* column_extents
,
1519 boolean
* is_selected
) {
1520 if (!instance_active())
1523 if (!row
|| !column
|| !row_extents
|| !column_extents
|| !is_selected
)
1524 return E_INVALIDARG
;
1526 const std::vector
<int32
>& unique_cell_ids
= GetIntListAttribute(
1527 ui::AX_ATTR_UNIQUE_CELL_IDS
);
1528 int cell_id_count
= static_cast<int>(unique_cell_ids
.size());
1530 return E_INVALIDARG
;
1531 if (index
>= cell_id_count
)
1534 int cell_id
= unique_cell_ids
[index
];
1535 BrowserAccessibilityWin
* cell
=
1536 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1540 cell
->GetIntAttribute(
1541 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1542 cell
->GetIntAttribute(
1543 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1546 *row_extents
= rowspan
;
1547 *column_extents
= colspan
;
1555 // IAccessibleTable2 methods.
1558 STDMETHODIMP
BrowserAccessibilityWin::get_cellAt(long row
,
1561 return get_accessibleAt(row
, column
, cell
);
1564 STDMETHODIMP
BrowserAccessibilityWin::get_nSelectedCells(long* cell_count
) {
1565 return get_nSelectedChildren(cell_count
);
1568 STDMETHODIMP
BrowserAccessibilityWin::get_selectedCells(
1570 long* n_selected_cells
) {
1571 if (!instance_active())
1574 if (!cells
|| !n_selected_cells
)
1575 return E_INVALIDARG
;
1577 // TODO(dmazzoni): Implement this.
1578 *n_selected_cells
= 0;
1582 STDMETHODIMP
BrowserAccessibilityWin::get_selectedColumns(long** columns
,
1584 if (!instance_active())
1587 if (!columns
|| !n_columns
)
1588 return E_INVALIDARG
;
1590 // TODO(dmazzoni): Implement this.
1595 STDMETHODIMP
BrowserAccessibilityWin::get_selectedRows(long** rows
,
1597 if (!instance_active())
1600 if (!rows
|| !n_rows
)
1601 return E_INVALIDARG
;
1603 // TODO(dmazzoni): Implement this.
1610 // IAccessibleTableCell methods.
1613 STDMETHODIMP
BrowserAccessibilityWin::get_columnExtent(
1614 long* n_columns_spanned
) {
1615 if (!instance_active())
1618 if (!n_columns_spanned
)
1619 return E_INVALIDARG
;
1622 if (GetIntAttribute(
1623 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
) &&
1625 *n_columns_spanned
= colspan
;
1632 STDMETHODIMP
BrowserAccessibilityWin::get_columnHeaderCells(
1633 IUnknown
*** cell_accessibles
,
1634 long* n_column_header_cells
) {
1635 if (!instance_active())
1638 if (!cell_accessibles
|| !n_column_header_cells
)
1639 return E_INVALIDARG
;
1641 *n_column_header_cells
= 0;
1644 if (!GetIntAttribute(
1645 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1649 BrowserAccessibility
* table
= parent();
1650 while (table
&& table
->role() != ui::AX_ROLE_TABLE
)
1651 table
= table
->parent();
1659 if (!table
->GetIntAttribute(
1660 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1661 !table
->GetIntAttribute(
1662 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1665 if (columns
<= 0 || rows
<= 0 || column
< 0 || column
>= columns
)
1668 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1669 ui::AX_ATTR_CELL_IDS
);
1671 for (int i
= 0; i
< rows
; ++i
) {
1672 int cell_id
= cell_ids
[i
* columns
+ column
];
1673 BrowserAccessibilityWin
* cell
=
1674 manager()->GetFromRendererID(cell_id
)->ToBrowserAccessibilityWin();
1675 if (cell
&& cell
->blink_role() == ui::AX_ROLE_COLUMN_HEADER
)
1676 (*n_column_header_cells
)++;
1679 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1680 (*n_column_header_cells
) * sizeof(cell_accessibles
[0])));
1682 for (int i
= 0; i
< rows
; ++i
) {
1683 int cell_id
= cell_ids
[i
* columns
+ column
];
1684 BrowserAccessibility
* cell
= manager()->GetFromRendererID(cell_id
);
1685 if (cell
&& cell
->role() == ui::AX_ROLE_COLUMN_HEADER
) {
1686 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1687 cell
->ToBrowserAccessibilityWin()->NewReference());
1695 STDMETHODIMP
BrowserAccessibilityWin::get_columnIndex(long* column_index
) {
1696 if (!instance_active())
1700 return E_INVALIDARG
;
1703 if (GetIntAttribute(
1704 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
)) {
1705 *column_index
= column
;
1712 STDMETHODIMP
BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned
) {
1713 if (!instance_active())
1716 if (!n_rows_spanned
)
1717 return E_INVALIDARG
;
1720 if (GetIntAttribute(
1721 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1723 *n_rows_spanned
= rowspan
;
1730 STDMETHODIMP
BrowserAccessibilityWin::get_rowHeaderCells(
1731 IUnknown
*** cell_accessibles
,
1732 long* n_row_header_cells
) {
1733 if (!instance_active())
1736 if (!cell_accessibles
|| !n_row_header_cells
)
1737 return E_INVALIDARG
;
1739 *n_row_header_cells
= 0;
1742 if (!GetIntAttribute(
1743 ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1747 BrowserAccessibility
* table
= parent();
1748 while (table
&& table
->role() != ui::AX_ROLE_TABLE
)
1749 table
= table
->parent();
1757 if (!table
->GetIntAttribute(
1758 ui::AX_ATTR_TABLE_COLUMN_COUNT
, &columns
) ||
1759 !table
->GetIntAttribute(
1760 ui::AX_ATTR_TABLE_ROW_COUNT
, &rows
)) {
1763 if (columns
<= 0 || rows
<= 0 || row
< 0 || row
>= rows
)
1766 const std::vector
<int32
>& cell_ids
= table
->GetIntListAttribute(
1767 ui::AX_ATTR_CELL_IDS
);
1769 for (int i
= 0; i
< columns
; ++i
) {
1770 int cell_id
= cell_ids
[row
* columns
+ i
];
1771 BrowserAccessibility
* cell
= manager()->GetFromRendererID(cell_id
);
1772 if (cell
&& cell
->role() == ui::AX_ROLE_ROW_HEADER
)
1773 (*n_row_header_cells
)++;
1776 *cell_accessibles
= static_cast<IUnknown
**>(CoTaskMemAlloc(
1777 (*n_row_header_cells
) * sizeof(cell_accessibles
[0])));
1779 for (int i
= 0; i
< columns
; ++i
) {
1780 int cell_id
= cell_ids
[row
* columns
+ i
];
1781 BrowserAccessibility
* cell
= manager()->GetFromRendererID(cell_id
);
1782 if (cell
&& cell
->role() == ui::AX_ROLE_ROW_HEADER
) {
1783 (*cell_accessibles
)[index
] = static_cast<IAccessible
*>(
1784 cell
->ToBrowserAccessibilityWin()->NewReference());
1792 STDMETHODIMP
BrowserAccessibilityWin::get_rowIndex(long* row_index
) {
1793 if (!instance_active())
1797 return E_INVALIDARG
;
1800 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
)) {
1807 STDMETHODIMP
BrowserAccessibilityWin::get_isSelected(boolean
* is_selected
) {
1808 if (!instance_active())
1812 return E_INVALIDARG
;
1814 *is_selected
= false;
1818 STDMETHODIMP
BrowserAccessibilityWin::get_rowColumnExtents(
1822 long* column_extents
,
1823 boolean
* is_selected
) {
1824 if (!instance_active())
1832 return E_INVALIDARG
;
1839 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
) &&
1841 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
) &&
1843 ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, &rowspan
) &&
1845 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
, &colspan
)) {
1847 *column_index
= column
;
1848 *row_extents
= rowspan
;
1849 *column_extents
= colspan
;
1850 *is_selected
= false;
1857 STDMETHODIMP
BrowserAccessibilityWin::get_table(IUnknown
** table
) {
1858 if (!instance_active())
1862 return E_INVALIDARG
;
1867 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, &row
);
1868 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
, &column
);
1870 BrowserAccessibility
* find_table
= parent();
1871 while (find_table
&& find_table
->role() != ui::AX_ROLE_TABLE
)
1872 find_table
= find_table
->parent();
1878 *table
= static_cast<IAccessibleTable
*>(
1879 find_table
->ToBrowserAccessibilityWin()->NewReference());
1885 // IAccessibleText methods.
1888 STDMETHODIMP
BrowserAccessibilityWin::get_nCharacters(LONG
* n_characters
) {
1889 if (!instance_active())
1893 return E_INVALIDARG
;
1895 *n_characters
= TextForIAccessibleText().length();
1899 STDMETHODIMP
BrowserAccessibilityWin::get_caretOffset(LONG
* offset
) {
1900 if (!instance_active())
1904 return E_INVALIDARG
;
1907 if (blink_role() == ui::AX_ROLE_TEXT_FIELD
||
1908 blink_role() == ui::AX_ROLE_TEXT_AREA
) {
1910 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
1912 *offset
= sel_start
;
1918 STDMETHODIMP
BrowserAccessibilityWin::get_characterExtents(
1920 enum IA2CoordinateType coordinate_type
,
1925 if (!instance_active())
1928 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
1929 return E_INVALIDARG
;
1931 const base::string16
& text_str
= TextForIAccessibleText();
1932 HandleSpecialTextOffset(text_str
, &offset
);
1934 if (offset
< 0 || offset
> static_cast<LONG
>(text_str
.size()))
1935 return E_INVALIDARG
;
1937 gfx::Rect character_bounds
;
1938 if (coordinate_type
== IA2_COORDTYPE_SCREEN_RELATIVE
) {
1939 character_bounds
= GetGlobalBoundsForRange(offset
, 1);
1940 } else if (coordinate_type
== IA2_COORDTYPE_PARENT_RELATIVE
) {
1941 character_bounds
= GetLocalBoundsForRange(offset
, 1);
1942 character_bounds
-= location().OffsetFromOrigin();
1944 return E_INVALIDARG
;
1947 *out_x
= character_bounds
.x();
1948 *out_y
= character_bounds
.y();
1949 *out_width
= character_bounds
.width();
1950 *out_height
= character_bounds
.height();
1955 STDMETHODIMP
BrowserAccessibilityWin::get_nSelections(LONG
* n_selections
) {
1956 if (!instance_active())
1960 return E_INVALIDARG
;
1963 if (blink_role() == ui::AX_ROLE_TEXT_FIELD
||
1964 blink_role() == ui::AX_ROLE_TEXT_AREA
) {
1967 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START
,
1969 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
) &&
1970 sel_start
!= sel_end
)
1977 STDMETHODIMP
BrowserAccessibilityWin::get_selection(LONG selection_index
,
1980 if (!instance_active())
1983 if (!start_offset
|| !end_offset
|| selection_index
!= 0)
1984 return E_INVALIDARG
;
1988 if (blink_role() == ui::AX_ROLE_TEXT_FIELD
||
1989 blink_role() == ui::AX_ROLE_TEXT_AREA
) {
1992 if (GetIntAttribute(
1993 ui::AX_ATTR_TEXT_SEL_START
, &sel_start
) &&
1994 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, &sel_end
)) {
1995 *start_offset
= sel_start
;
1996 *end_offset
= sel_end
;
2003 STDMETHODIMP
BrowserAccessibilityWin::get_text(LONG start_offset
,
2006 if (!instance_active())
2010 return E_INVALIDARG
;
2012 const base::string16
& text_str
= TextForIAccessibleText();
2014 // Handle special text offsets.
2015 HandleSpecialTextOffset(text_str
, &start_offset
);
2016 HandleSpecialTextOffset(text_str
, &end_offset
);
2018 // The spec allows the arguments to be reversed.
2019 if (start_offset
> end_offset
) {
2020 LONG tmp
= start_offset
;
2021 start_offset
= end_offset
;
2025 // The spec does not allow the start or end offsets to be out or range;
2026 // we must return an error if so.
2027 LONG len
= text_str
.length();
2028 if (start_offset
< 0)
2029 return E_INVALIDARG
;
2030 if (end_offset
> len
)
2031 return E_INVALIDARG
;
2033 base::string16 substr
= text_str
.substr(start_offset
,
2034 end_offset
- start_offset
);
2038 *text
= SysAllocString(substr
.c_str());
2043 STDMETHODIMP
BrowserAccessibilityWin::get_textAtOffset(
2045 enum IA2TextBoundaryType boundary_type
,
2049 if (!instance_active())
2052 if (!start_offset
|| !end_offset
|| !text
)
2053 return E_INVALIDARG
;
2055 // The IAccessible2 spec says we don't have to implement the "sentence"
2056 // boundary type, we can just let the screenreader handle it.
2057 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2064 const base::string16
& text_str
= TextForIAccessibleText();
2066 *start_offset
= FindBoundary(
2067 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2068 *end_offset
= FindBoundary(
2069 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2070 return get_text(*start_offset
, *end_offset
, text
);
2073 STDMETHODIMP
BrowserAccessibilityWin::get_textBeforeOffset(
2075 enum IA2TextBoundaryType boundary_type
,
2079 if (!instance_active())
2082 if (!start_offset
|| !end_offset
|| !text
)
2083 return E_INVALIDARG
;
2085 // The IAccessible2 spec says we don't have to implement the "sentence"
2086 // boundary type, we can just let the screenreader handle it.
2087 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2094 const base::string16
& text_str
= TextForIAccessibleText();
2096 *start_offset
= FindBoundary(
2097 text_str
, boundary_type
, offset
, ui::BACKWARDS_DIRECTION
);
2098 *end_offset
= offset
;
2099 return get_text(*start_offset
, *end_offset
, text
);
2102 STDMETHODIMP
BrowserAccessibilityWin::get_textAfterOffset(
2104 enum IA2TextBoundaryType boundary_type
,
2108 if (!instance_active())
2111 if (!start_offset
|| !end_offset
|| !text
)
2112 return E_INVALIDARG
;
2114 // The IAccessible2 spec says we don't have to implement the "sentence"
2115 // boundary type, we can just let the screenreader handle it.
2116 if (boundary_type
== IA2_TEXT_BOUNDARY_SENTENCE
) {
2123 const base::string16
& text_str
= TextForIAccessibleText();
2125 *start_offset
= offset
;
2126 *end_offset
= FindBoundary(
2127 text_str
, boundary_type
, offset
, ui::FORWARDS_DIRECTION
);
2128 return get_text(*start_offset
, *end_offset
, text
);
2131 STDMETHODIMP
BrowserAccessibilityWin::get_newText(IA2TextSegment
* new_text
) {
2132 if (!instance_active())
2136 return E_INVALIDARG
;
2138 base::string16 text
= TextForIAccessibleText();
2140 new_text
->text
= SysAllocString(text
.c_str());
2141 new_text
->start
= 0;
2142 new_text
->end
= static_cast<long>(text
.size());
2146 STDMETHODIMP
BrowserAccessibilityWin::get_oldText(IA2TextSegment
* old_text
) {
2147 if (!instance_active())
2151 return E_INVALIDARG
;
2153 old_text
->text
= SysAllocString(old_text_
.c_str());
2154 old_text
->start
= 0;
2155 old_text
->end
= static_cast<long>(old_text_
.size());
2159 STDMETHODIMP
BrowserAccessibilityWin::get_offsetAtPoint(
2162 enum IA2CoordinateType coord_type
,
2164 if (!instance_active())
2168 return E_INVALIDARG
;
2170 // TODO(dmazzoni): implement this. We're returning S_OK for now so that
2171 // screen readers still return partially accurate results rather than
2172 // completely failing.
2177 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringTo(
2180 enum IA2ScrollType scroll_type
) {
2181 // TODO(dmazzoni): adjust this for the start and end index, too.
2182 return scrollTo(scroll_type
);
2185 STDMETHODIMP
BrowserAccessibilityWin::scrollSubstringToPoint(
2188 enum IA2CoordinateType coordinate_type
,
2190 // TODO(dmazzoni): adjust this for the start and end index, too.
2191 return scrollToPoint(coordinate_type
, x
, y
);
2194 STDMETHODIMP
BrowserAccessibilityWin::addSelection(LONG start_offset
,
2196 if (!instance_active())
2199 const base::string16
& text_str
= TextForIAccessibleText();
2200 HandleSpecialTextOffset(text_str
, &start_offset
);
2201 HandleSpecialTextOffset(text_str
, &end_offset
);
2203 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2207 STDMETHODIMP
BrowserAccessibilityWin::removeSelection(LONG selection_index
) {
2208 if (!instance_active())
2211 if (selection_index
!= 0)
2212 return E_INVALIDARG
;
2214 manager()->SetTextSelection(*this, 0, 0);
2218 STDMETHODIMP
BrowserAccessibilityWin::setCaretOffset(LONG offset
) {
2219 if (!instance_active())
2222 const base::string16
& text_str
= TextForIAccessibleText();
2223 HandleSpecialTextOffset(text_str
, &offset
);
2224 manager()->SetTextSelection(*this, offset
, offset
);
2228 STDMETHODIMP
BrowserAccessibilityWin::setSelection(LONG selection_index
,
2231 if (!instance_active())
2234 if (selection_index
!= 0)
2235 return E_INVALIDARG
;
2237 const base::string16
& text_str
= TextForIAccessibleText();
2238 HandleSpecialTextOffset(text_str
, &start_offset
);
2239 HandleSpecialTextOffset(text_str
, &end_offset
);
2241 manager()->SetTextSelection(*this, start_offset
, end_offset
);
2246 // IAccessibleHypertext methods.
2249 STDMETHODIMP
BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count
) {
2250 if (!instance_active())
2253 if (!hyperlink_count
)
2254 return E_INVALIDARG
;
2256 *hyperlink_count
= hyperlink_offset_to_index_
.size();
2260 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlink(
2262 IAccessibleHyperlink
** hyperlink
) {
2263 if (!instance_active())
2268 index
>= static_cast<long>(hyperlinks_
.size())) {
2269 return E_INVALIDARG
;
2272 BrowserAccessibilityWin
* child
=
2273 children()[hyperlinks_
[index
]]->ToBrowserAccessibilityWin();
2274 *hyperlink
= static_cast<IAccessibleHyperlink
*>(child
->NewReference());
2278 STDMETHODIMP
BrowserAccessibilityWin::get_hyperlinkIndex(
2280 long* hyperlink_index
) {
2281 if (!instance_active())
2284 if (!hyperlink_index
)
2285 return E_INVALIDARG
;
2287 *hyperlink_index
= -1;
2289 if (char_index
< 0 || char_index
>= static_cast<long>(hypertext_
.size()))
2290 return E_INVALIDARG
;
2292 std::map
<int32
, int32
>::iterator it
=
2293 hyperlink_offset_to_index_
.find(char_index
);
2294 if (it
== hyperlink_offset_to_index_
.end())
2297 *hyperlink_index
= it
->second
;
2302 // IAccessibleValue methods.
2305 STDMETHODIMP
BrowserAccessibilityWin::get_currentValue(VARIANT
* value
) {
2306 if (!instance_active())
2310 return E_INVALIDARG
;
2313 if (GetFloatAttribute(
2314 ui::AX_ATTR_VALUE_FOR_RANGE
, &float_val
)) {
2316 value
->dblVal
= float_val
;
2320 value
->vt
= VT_EMPTY
;
2324 STDMETHODIMP
BrowserAccessibilityWin::get_minimumValue(VARIANT
* value
) {
2325 if (!instance_active())
2329 return E_INVALIDARG
;
2332 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE
,
2335 value
->dblVal
= float_val
;
2339 value
->vt
= VT_EMPTY
;
2343 STDMETHODIMP
BrowserAccessibilityWin::get_maximumValue(VARIANT
* value
) {
2344 if (!instance_active())
2348 return E_INVALIDARG
;
2351 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE
,
2354 value
->dblVal
= float_val
;
2358 value
->vt
= VT_EMPTY
;
2362 STDMETHODIMP
BrowserAccessibilityWin::setCurrentValue(VARIANT new_value
) {
2363 // TODO(dmazzoni): Implement this.
2368 // ISimpleDOMDocument methods.
2371 STDMETHODIMP
BrowserAccessibilityWin::get_URL(BSTR
* url
) {
2372 if (!instance_active())
2376 return E_INVALIDARG
;
2378 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL
, url
);
2381 STDMETHODIMP
BrowserAccessibilityWin::get_title(BSTR
* title
) {
2382 if (!instance_active())
2386 return E_INVALIDARG
;
2388 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE
, title
);
2391 STDMETHODIMP
BrowserAccessibilityWin::get_mimeType(BSTR
* mime_type
) {
2392 if (!instance_active())
2396 return E_INVALIDARG
;
2398 return GetStringAttributeAsBstr(
2399 ui::AX_ATTR_DOC_MIMETYPE
, mime_type
);
2402 STDMETHODIMP
BrowserAccessibilityWin::get_docType(BSTR
* doc_type
) {
2403 if (!instance_active())
2407 return E_INVALIDARG
;
2409 return GetStringAttributeAsBstr(
2410 ui::AX_ATTR_DOC_DOCTYPE
, doc_type
);
2414 // ISimpleDOMNode methods.
2417 STDMETHODIMP
BrowserAccessibilityWin::get_nodeInfo(
2419 short* name_space_id
,
2421 unsigned int* num_children
,
2422 unsigned int* unique_id
,
2423 unsigned short* node_type
) {
2424 if (!instance_active())
2427 if (!node_name
|| !name_space_id
|| !node_value
|| !num_children
||
2428 !unique_id
|| !node_type
) {
2429 return E_INVALIDARG
;
2433 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG
, &tag
))
2434 *node_name
= SysAllocString(tag
.c_str());
2439 *node_value
= SysAllocString(base::UTF8ToUTF16(value()).c_str());
2440 *num_children
= PlatformChildCount();
2441 *unique_id
= unique_id_win_
;
2443 if (ia_role_
== ROLE_SYSTEM_DOCUMENT
) {
2444 *node_type
= NODETYPE_DOCUMENT
;
2445 } else if (ia_role_
== ROLE_SYSTEM_TEXT
&&
2446 ((ia2_state_
& IA2_STATE_EDITABLE
) == 0)) {
2447 *node_type
= NODETYPE_TEXT
;
2449 *node_type
= NODETYPE_ELEMENT
;
2455 STDMETHODIMP
BrowserAccessibilityWin::get_attributes(
2456 unsigned short max_attribs
,
2458 short* name_space_id
,
2459 BSTR
* attrib_values
,
2460 unsigned short* num_attribs
) {
2461 if (!instance_active())
2464 if (!attrib_names
|| !name_space_id
|| !attrib_values
|| !num_attribs
)
2465 return E_INVALIDARG
;
2467 *num_attribs
= max_attribs
;
2468 if (*num_attribs
> html_attributes().size())
2469 *num_attribs
= html_attributes().size();
2471 for (unsigned short i
= 0; i
< *num_attribs
; ++i
) {
2472 attrib_names
[i
] = SysAllocString(
2473 base::UTF8ToUTF16(html_attributes()[i
].first
).c_str());
2474 name_space_id
[i
] = 0;
2475 attrib_values
[i
] = SysAllocString(
2476 base::UTF8ToUTF16(html_attributes()[i
].second
).c_str());
2481 STDMETHODIMP
BrowserAccessibilityWin::get_attributesForNames(
2482 unsigned short num_attribs
,
2484 short* name_space_id
,
2485 BSTR
* attrib_values
) {
2486 if (!instance_active())
2489 if (!attrib_names
|| !name_space_id
|| !attrib_values
)
2490 return E_INVALIDARG
;
2492 for (unsigned short i
= 0; i
< num_attribs
; ++i
) {
2493 name_space_id
[i
] = 0;
2495 std::string name
= base::UTF16ToUTF8((LPCWSTR
)attrib_names
[i
]);
2496 for (unsigned int j
= 0; j
< html_attributes().size(); ++j
) {
2497 if (html_attributes()[j
].first
== name
) {
2498 attrib_values
[i
] = SysAllocString(
2499 base::UTF8ToUTF16(html_attributes()[j
].second
).c_str());
2505 attrib_values
[i
] = NULL
;
2511 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyle(
2512 unsigned short max_style_properties
,
2513 boolean use_alternate_view
,
2514 BSTR
* style_properties
,
2516 unsigned short *num_style_properties
) {
2517 if (!instance_active())
2520 if (!style_properties
|| !style_values
)
2521 return E_INVALIDARG
;
2523 // We only cache a single style property for now: DISPLAY
2525 base::string16 display
;
2526 if (max_style_properties
== 0 ||
2527 !GetString16Attribute(ui::AX_ATTR_DISPLAY
, &display
)) {
2528 *num_style_properties
= 0;
2532 *num_style_properties
= 1;
2533 style_properties
[0] = SysAllocString(L
"display");
2534 style_values
[0] = SysAllocString(display
.c_str());
2539 STDMETHODIMP
BrowserAccessibilityWin::get_computedStyleForProperties(
2540 unsigned short num_style_properties
,
2541 boolean use_alternate_view
,
2542 BSTR
* style_properties
,
2543 BSTR
* style_values
) {
2544 if (!instance_active())
2547 if (!style_properties
|| !style_values
)
2548 return E_INVALIDARG
;
2550 // We only cache a single style property for now: DISPLAY
2552 for (unsigned short i
= 0; i
< num_style_properties
; ++i
) {
2553 base::string16 name
= (LPCWSTR
)style_properties
[i
];
2554 StringToLowerASCII(&name
);
2555 if (name
== L
"display") {
2556 base::string16 display
= GetString16Attribute(
2557 ui::AX_ATTR_DISPLAY
);
2558 style_values
[i
] = SysAllocString(display
.c_str());
2560 style_values
[i
] = NULL
;
2567 STDMETHODIMP
BrowserAccessibilityWin::scrollTo(boolean placeTopLeft
) {
2568 return scrollTo(placeTopLeft
?
2569 IA2_SCROLL_TYPE_TOP_LEFT
: IA2_SCROLL_TYPE_ANYWHERE
);
2572 STDMETHODIMP
BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode
** node
) {
2573 if (!instance_active())
2577 return E_INVALIDARG
;
2579 *node
= parent()->ToBrowserAccessibilityWin()->NewReference();
2583 STDMETHODIMP
BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode
** node
) {
2584 if (!instance_active())
2588 return E_INVALIDARG
;
2590 if (PlatformChildCount() == 0) {
2595 *node
= PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference();
2599 STDMETHODIMP
BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode
** node
) {
2600 if (!instance_active())
2604 return E_INVALIDARG
;
2606 if (PlatformChildCount() == 0) {
2611 *node
= PlatformGetChild(PlatformChildCount() - 1)
2612 ->ToBrowserAccessibilityWin()->NewReference();
2616 STDMETHODIMP
BrowserAccessibilityWin::get_previousSibling(
2617 ISimpleDOMNode
** node
) {
2618 if (!instance_active())
2622 return E_INVALIDARG
;
2624 if (!parent() || index_in_parent() <= 0) {
2629 *node
= parent()->children()[index_in_parent() - 1]->
2630 ToBrowserAccessibilityWin()->NewReference();
2634 STDMETHODIMP
BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode
** node
) {
2635 if (!instance_active())
2639 return E_INVALIDARG
;
2642 index_in_parent() < 0 ||
2643 index_in_parent() >= static_cast<int>(parent()->children().size()) - 1) {
2648 *node
= parent()->children()[index_in_parent() + 1]->
2649 ToBrowserAccessibilityWin()->NewReference();
2653 STDMETHODIMP
BrowserAccessibilityWin::get_childAt(
2654 unsigned int child_index
,
2655 ISimpleDOMNode
** node
) {
2656 if (!instance_active())
2660 return E_INVALIDARG
;
2662 if (child_index
>= PlatformChildCount())
2663 return E_INVALIDARG
;
2665 BrowserAccessibility
* child
= PlatformGetChild(child_index
);
2671 *node
= child
->ToBrowserAccessibilityWin()->NewReference();
2676 // ISimpleDOMText methods.
2679 STDMETHODIMP
BrowserAccessibilityWin::get_domText(BSTR
* dom_text
) {
2680 if (!instance_active())
2684 return E_INVALIDARG
;
2686 return GetStringAttributeAsBstr(
2687 ui::AX_ATTR_NAME
, dom_text
);
2690 STDMETHODIMP
BrowserAccessibilityWin::get_clippedSubstringBounds(
2691 unsigned int start_index
,
2692 unsigned int end_index
,
2697 // TODO(dmazzoni): fully support this API by intersecting the
2698 // rect with the container's rect.
2699 return get_unclippedSubstringBounds(
2700 start_index
, end_index
, out_x
, out_y
, out_width
, out_height
);
2703 STDMETHODIMP
BrowserAccessibilityWin::get_unclippedSubstringBounds(
2704 unsigned int start_index
,
2705 unsigned int end_index
,
2710 if (!instance_active())
2713 if (!out_x
|| !out_y
|| !out_width
|| !out_height
)
2714 return E_INVALIDARG
;
2716 const base::string16
& text_str
= TextForIAccessibleText();
2717 if (start_index
> text_str
.size() ||
2718 end_index
> text_str
.size() ||
2719 start_index
> end_index
) {
2720 return E_INVALIDARG
;
2723 gfx::Rect bounds
= GetGlobalBoundsForRange(
2724 start_index
, end_index
- start_index
);
2725 *out_x
= bounds
.x();
2726 *out_y
= bounds
.y();
2727 *out_width
= bounds
.width();
2728 *out_height
= bounds
.height();
2732 STDMETHODIMP
BrowserAccessibilityWin::scrollToSubstring(
2733 unsigned int start_index
,
2734 unsigned int end_index
) {
2735 if (!instance_active())
2738 const base::string16
& text_str
= TextForIAccessibleText();
2739 if (start_index
> text_str
.size() ||
2740 end_index
> text_str
.size() ||
2741 start_index
> end_index
) {
2742 return E_INVALIDARG
;
2745 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange(
2746 start_index
, end_index
- start_index
));
2747 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this);
2753 // IServiceProvider methods.
2756 STDMETHODIMP
BrowserAccessibilityWin::QueryService(REFGUID guidService
,
2759 if (!instance_active())
2762 // The system uses IAccessible APIs for many purposes, but only
2763 // assistive technology like screen readers uses IAccessible2.
2764 // Enable full accessibility support when IAccessible2 APIs are queried.
2765 if (riid
== IID_IAccessible2
)
2766 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility();
2768 if (guidService
== GUID_IAccessibleContentDocument
) {
2769 // Special Mozilla extension: return the accessible for the root document.
2770 // Screen readers use this to distinguish between a document loaded event
2771 // on the root document vs on an iframe.
2772 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface(
2773 IID_IAccessible2
, object
);
2776 if (guidService
== IID_IAccessible
||
2777 guidService
== IID_IAccessible2
||
2778 guidService
== IID_IAccessibleAction
||
2779 guidService
== IID_IAccessibleApplication
||
2780 guidService
== IID_IAccessibleHyperlink
||
2781 guidService
== IID_IAccessibleHypertext
||
2782 guidService
== IID_IAccessibleImage
||
2783 guidService
== IID_IAccessibleTable
||
2784 guidService
== IID_IAccessibleTable2
||
2785 guidService
== IID_IAccessibleTableCell
||
2786 guidService
== IID_IAccessibleText
||
2787 guidService
== IID_IAccessibleValue
||
2788 guidService
== IID_ISimpleDOMDocument
||
2789 guidService
== IID_ISimpleDOMNode
||
2790 guidService
== IID_ISimpleDOMText
||
2791 guidService
== GUID_ISimpleDOM
) {
2792 return QueryInterface(riid
, object
);
2795 // We only support the IAccessibleEx interface on Windows 8 and above. This
2796 // is needed for the on-screen Keyboard to show up in metro mode, when the
2797 // user taps an editable portion on the page.
2798 // All methods in the IAccessibleEx interface are unimplemented.
2799 if (riid
== IID_IAccessibleEx
&&
2800 base::win::GetVersion() >= base::win::VERSION_WIN8
) {
2801 return QueryInterface(riid
, object
);
2808 STDMETHODIMP
BrowserAccessibilityWin::GetPatternProvider(PATTERNID id
,
2809 IUnknown
** provider
) {
2810 DVLOG(1) << "In Function: "
2812 << " for pattern id: "
2814 if (id
== UIA_ValuePatternId
|| id
== UIA_TextPatternId
) {
2815 if (IsEditableText()) {
2816 // The BrowserAccessibilityManager keeps track of instances when
2817 // we don't want to show the on-screen keyboard.
2818 if (!manager()->IsOSKAllowed(GetGlobalBoundsRect()))
2821 DVLOG(1) << "Returning UIA text provider";
2822 base::win::UIATextProvider::CreateTextProvider(true, provider
);
2829 STDMETHODIMP
BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id
,
2831 DVLOG(1) << "In Function: "
2833 << " for property id: "
2835 V_VT(ret
) = VT_EMPTY
;
2836 if (id
== UIA_ControlTypePropertyId
) {
2837 if (IsEditableText()) {
2839 ret
->lVal
= UIA_EditControlTypeId
;
2840 DVLOG(1) << "Returning Edit control type";
2842 DVLOG(1) << "Returning empty control type";
2849 // CComObjectRootEx methods.
2852 HRESULT WINAPI
BrowserAccessibilityWin::InternalQueryInterface(
2854 const _ATL_INTMAP_ENTRY
* entries
,
2857 if (iid
== IID_IAccessibleImage
) {
2858 if (ia_role_
!= ROLE_SYSTEM_GRAPHIC
) {
2860 return E_NOINTERFACE
;
2862 } else if (iid
== IID_IAccessibleTable
|| iid
== IID_IAccessibleTable2
) {
2863 if (ia_role_
!= ROLE_SYSTEM_TABLE
) {
2865 return E_NOINTERFACE
;
2867 } else if (iid
== IID_IAccessibleTableCell
) {
2868 if (ia_role_
!= ROLE_SYSTEM_CELL
) {
2870 return E_NOINTERFACE
;
2872 } else if (iid
== IID_IAccessibleValue
) {
2873 if (ia_role_
!= ROLE_SYSTEM_PROGRESSBAR
&&
2874 ia_role_
!= ROLE_SYSTEM_SCROLLBAR
&&
2875 ia_role_
!= ROLE_SYSTEM_SLIDER
) {
2877 return E_NOINTERFACE
;
2879 } else if (iid
== IID_ISimpleDOMDocument
) {
2880 if (ia_role_
!= ROLE_SYSTEM_DOCUMENT
) {
2882 return E_NOINTERFACE
;
2886 return CComObjectRootBase::InternalQueryInterface(
2887 this_ptr
, entries
, iid
, object
);
2894 // Initialize this object and mark it as active.
2895 void BrowserAccessibilityWin::PreInitialize() {
2896 BrowserAccessibility::PreInitialize();
2900 // Expose the "display" and "tag" attributes.
2901 StringAttributeToIA2(ui::AX_ATTR_DISPLAY
, "display");
2902 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG
, "tag");
2903 StringAttributeToIA2(ui::AX_ATTR_ROLE
, "xml-roles");
2905 // Expose "level" attribute for headings, trees, etc.
2906 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL
, "level");
2908 // Expose the set size and position in set for listbox options.
2909 if (blink_role() == ui::AX_ROLE_LIST_BOX_OPTION
&&
2911 parent()->role() == ui::AX_ROLE_LIST_BOX
) {
2912 ia2_attributes_
.push_back(
2913 L
"setsize:" + base::IntToString16(parent()->PlatformChildCount()));
2914 ia2_attributes_
.push_back(
2915 L
"setsize:" + base::IntToString16(index_in_parent() + 1));
2918 if (ia_role_
== ROLE_SYSTEM_CHECKBUTTON
||
2919 ia_role_
== ROLE_SYSTEM_RADIOBUTTON
||
2920 ia2_role_
== IA2_ROLE_TOGGLE_BUTTON
) {
2921 ia2_attributes_
.push_back(L
"checkable:true");
2924 // Expose live region attributes.
2925 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS
, "live");
2926 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT
, "relevant");
2927 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC
, "atomic");
2928 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY
, "busy");
2930 // Expose container live region attributes.
2931 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS
,
2933 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT
,
2934 "container-relevant");
2935 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC
,
2936 "container-atomic");
2937 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY
,
2940 // Expose slider value.
2941 if (ia_role_
== ROLE_SYSTEM_PROGRESSBAR
||
2942 ia_role_
== ROLE_SYSTEM_SCROLLBAR
||
2943 ia_role_
== ROLE_SYSTEM_SLIDER
) {
2944 ia2_attributes_
.push_back(L
"valuetext:" + GetValueText());
2947 // Expose table cell index.
2948 if (ia_role_
== ROLE_SYSTEM_CELL
) {
2949 BrowserAccessibility
* table
= parent();
2950 while (table
&& table
->role() != ui::AX_ROLE_TABLE
)
2951 table
= table
->parent();
2953 const std::vector
<int32
>& unique_cell_ids
= table
->GetIntListAttribute(
2954 ui::AX_ATTR_UNIQUE_CELL_IDS
);
2955 for (size_t i
= 0; i
< unique_cell_ids
.size(); ++i
) {
2956 if (unique_cell_ids
[i
] == renderer_id()) {
2957 ia2_attributes_
.push_back(
2958 base::string16(L
"table-cell-index:") + base::IntToString16(i
));
2964 // The calculation of the accessible name of an element has been
2965 // standardized in the HTML to Platform Accessibility APIs Implementation
2966 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the
2967 // appropriate accessible name on Windows, we need to apply some logic
2968 // to the fields we get from WebKit.
2970 // TODO(dmazzoni): move most of this logic into WebKit.
2974 // name: the default name, e.g. inner text
2975 // title ui element: a reference to a <label> element on the same
2976 // page that labels this node.
2977 // description: accessible labels that override the default name:
2978 // aria-label or aria-labelledby or aria-describedby
2979 // help: the value of the "title" attribute
2981 // On Windows, the logic we apply lets some fields take precedence and
2982 // always returns the primary name in "name" and the secondary name,
2983 // if any, in "description".
2985 int title_elem_id
= GetIntAttribute(
2986 ui::AX_ATTR_TITLE_UI_ELEMENT
);
2987 std::string help
= GetStringAttribute(ui::AX_ATTR_HELP
);
2988 std::string description
= GetStringAttribute(
2989 ui::AX_ATTR_DESCRIPTION
);
2991 // WebKit annoyingly puts the title in the description if there's no other
2992 // description, which just confuses the rest of the logic. Put it back.
2993 // Now "help" is always the value of the "title" attribute, if present.
2994 std::string title_attr
;
2995 if (GetHtmlAttribute("title", &title_attr
) &&
2996 description
== title_attr
&&
2999 description
.clear();
3002 // Now implement the main logic: the descripion should become the name if
3003 // it's nonempty, and the help should become the description if
3004 // there's no description - or the name if there's no name or description.
3005 if (!description
.empty()) {
3006 set_name(description
);
3007 description
.clear();
3009 if (!help
.empty() && description
.empty()) {
3013 if (!description
.empty() && name().empty() && !title_elem_id
) {
3014 set_name(description
);
3015 description
.clear();
3018 // If it's a text field, also consider the placeholder.
3019 std::string placeholder
;
3020 if (blink_role() == ui::AX_ROLE_TEXT_FIELD
&&
3021 HasState(ui::AX_STATE_FOCUSABLE
) &&
3022 GetHtmlAttribute("placeholder", &placeholder
)) {
3023 if (name().empty() && !title_elem_id
) {
3024 set_name(placeholder
);
3025 } else if (description
.empty()) {
3026 description
= placeholder
;
3030 SetStringAttribute(ui::AX_ATTR_DESCRIPTION
, description
);
3031 SetStringAttribute(ui::AX_ATTR_HELP
, help
);
3033 // On Windows, the value of a document should be its url.
3034 if (blink_role() == ui::AX_ROLE_ROOT_WEB_AREA
||
3035 blink_role() == ui::AX_ROLE_WEB_AREA
) {
3036 set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL
));
3039 // For certain roles (listbox option, static text, and list marker)
3040 // WebKit stores the main accessible text in the "value" - swap it so
3041 // that it's the "name".
3042 if (name().empty() &&
3043 (blink_role() == ui::AX_ROLE_LIST_BOX_OPTION
||
3044 blink_role() == ui::AX_ROLE_STATIC_TEXT
||
3045 blink_role() == ui::AX_ROLE_LIST_MARKER
)) {
3046 std::string tmp
= value();
3051 // If this doesn't have a value and is linked then set its value to the url
3052 // attribute. This allows screen readers to read an empty link's destination.
3053 if (value().empty() && (ia_state_
& STATE_SYSTEM_LINKED
))
3054 set_value(GetStringAttribute(ui::AX_ATTR_URL
));
3056 // Clear any old relationships between this node and other nodes.
3057 for (size_t i
= 0; i
< relations_
.size(); ++i
)
3058 relations_
[i
]->Release();
3061 // Handle title UI element.
3062 if (title_elem_id
) {
3063 // Add a labelled by relationship.
3064 CComObject
<BrowserAccessibilityRelation
>* relation
;
3065 HRESULT hr
= CComObject
<BrowserAccessibilityRelation
>::CreateInstance(
3067 DCHECK(SUCCEEDED(hr
));
3069 relation
->Initialize(this, IA2_RELATION_LABELLED_BY
);
3070 relation
->AddTarget(title_elem_id
);
3071 relations_
.push_back(relation
);
3075 void BrowserAccessibilityWin::PostInitialize() {
3076 BrowserAccessibility::PostInitialize();
3078 // Construct the hypertext for this node.
3079 hyperlink_offset_to_index_
.clear();
3080 hyperlinks_
.clear();
3082 for (unsigned int i
= 0; i
< PlatformChildCount(); ++i
) {
3083 BrowserAccessibility
* child
= PlatformGetChild(i
);
3084 if (child
->role() == ui::AX_ROLE_STATIC_TEXT
) {
3085 hypertext_
+= base::UTF8ToUTF16(child
->name());
3087 hyperlink_offset_to_index_
[hypertext_
.size()] = hyperlinks_
.size();
3088 hypertext_
+= kEmbeddedCharacter
;
3089 hyperlinks_
.push_back(i
);
3092 DCHECK_EQ(hyperlink_offset_to_index_
.size(), hyperlinks_
.size());
3094 // Fire an event when an alert first appears.
3095 if (blink_role() == ui::AX_ROLE_ALERT
&& first_time_
)
3096 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT
, this);
3098 // Fire events if text has changed.
3099 base::string16 text
= TextForIAccessibleText();
3100 if (previous_text_
!= text
) {
3101 if (!previous_text_
.empty() && !text
.empty()) {
3102 manager()->NotifyAccessibilityEvent(
3103 ui::AX_EVENT_SHOW
, this);
3106 // TODO(dmazzoni): Look into HIDE events, too.
3108 old_text_
= previous_text_
;
3109 previous_text_
= text
;
3112 BrowserAccessibilityManagerWin
* manager
=
3113 this->manager()->ToBrowserAccessibilityManagerWin();
3115 // Fire events if the state has changed.
3116 if (!first_time_
&& ia_state_
!= old_ia_state_
) {
3117 // Normally focus events are handled elsewhere, however
3118 // focus for managed descendants is platform-specific.
3119 // Fire a focus event if the focused descendant in a multi-select
3120 // list box changes.
3121 if (blink_role() == ui::AX_ROLE_LIST_BOX_OPTION
&&
3122 (ia_state_
& STATE_SYSTEM_FOCUSABLE
) &&
3123 (ia_state_
& STATE_SYSTEM_SELECTABLE
) &&
3124 (ia_state_
& STATE_SYSTEM_FOCUSED
) &&
3125 !(old_ia_state_
& STATE_SYSTEM_FOCUSED
)) {
3126 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS
, unique_id_win());
3129 if ((ia_state_
& STATE_SYSTEM_SELECTED
) &&
3130 !(old_ia_state_
& STATE_SYSTEM_SELECTED
)) {
3131 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD
,
3133 } else if (!(ia_state_
& STATE_SYSTEM_SELECTED
) &&
3134 (old_ia_state_
& STATE_SYSTEM_SELECTED
)) {
3135 manager
->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE
,
3139 old_ia_state_
= ia_state_
;
3142 // Fire an event if this container object has scrolled.
3145 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X
, &sx
) &&
3146 GetIntAttribute(ui::AX_ATTR_SCROLL_Y
, &sy
)) {
3148 (sx
!= previous_scroll_x_
|| sy
!= previous_scroll_y_
)) {
3149 manager
->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND
,
3152 previous_scroll_x_
= sx
;
3153 previous_scroll_y_
= sy
;
3156 first_time_
= false;
3159 void BrowserAccessibilityWin::NativeAddReference() {
3163 void BrowserAccessibilityWin::NativeReleaseReference() {
3167 bool BrowserAccessibilityWin::IsNative() const {
3171 void BrowserAccessibilityWin::SetLocation(const gfx::Rect
& new_location
) {
3172 BrowserAccessibility::SetLocation(new_location
);
3173 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent(
3174 EVENT_OBJECT_LOCATIONCHANGE
, unique_id_win());
3177 BrowserAccessibilityWin
* BrowserAccessibilityWin::NewReference() {
3182 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetTargetFromChildID(
3183 const VARIANT
& var_id
) {
3184 if (var_id
.vt
!= VT_I4
)
3187 LONG child_id
= var_id
.lVal
;
3188 if (child_id
== CHILDID_SELF
)
3191 if (child_id
>= 1 && child_id
<= static_cast<LONG
>(PlatformChildCount()))
3192 return PlatformGetChild(child_id
- 1)->ToBrowserAccessibilityWin();
3194 return manager()->ToBrowserAccessibilityManagerWin()->
3195 GetFromUniqueIdWin(child_id
);
3198 HRESULT
BrowserAccessibilityWin::GetStringAttributeAsBstr(
3199 ui::AXStringAttribute attribute
,
3203 if (!GetString16Attribute(attribute
, &str
))
3209 *value_bstr
= SysAllocString(str
.c_str());
3210 DCHECK(*value_bstr
);
3215 void BrowserAccessibilityWin::StringAttributeToIA2(
3216 ui::AXStringAttribute attribute
,
3217 const char* ia2_attr
) {
3218 base::string16 value
;
3219 if (GetString16Attribute(attribute
, &value
))
3220 ia2_attributes_
.push_back(base::ASCIIToUTF16(ia2_attr
) + L
":" + value
);
3223 void BrowserAccessibilityWin::BoolAttributeToIA2(
3224 ui::AXBoolAttribute attribute
,
3225 const char* ia2_attr
) {
3227 if (GetBoolAttribute(attribute
, &value
)) {
3228 ia2_attributes_
.push_back((base::ASCIIToUTF16(ia2_attr
) + L
":") +
3229 (value
? L
"true" : L
"false"));
3233 void BrowserAccessibilityWin::IntAttributeToIA2(
3234 ui::AXIntAttribute attribute
,
3235 const char* ia2_attr
) {
3237 if (GetIntAttribute(attribute
, &value
)) {
3238 ia2_attributes_
.push_back(base::ASCIIToUTF16(ia2_attr
) + L
":" +
3239 base::IntToString16(value
));
3243 base::string16
BrowserAccessibilityWin::GetValueText() {
3245 base::string16 value
= base::UTF8ToUTF16(this->value());
3247 if (value
.empty() &&
3248 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE
, &fval
)) {
3249 value
= base::UTF8ToUTF16(base::DoubleToString(fval
));
3254 base::string16
BrowserAccessibilityWin::TextForIAccessibleText() {
3255 if (IsEditableText())
3256 return base::UTF8ToUTF16(value());
3257 return (blink_role() == ui::AX_ROLE_STATIC_TEXT
) ?
3258 base::UTF8ToUTF16(name()) : hypertext_
;
3261 void BrowserAccessibilityWin::HandleSpecialTextOffset(
3262 const base::string16
& text
,
3264 if (*offset
== IA2_TEXT_OFFSET_LENGTH
)
3265 *offset
= static_cast<LONG
>(text
.size());
3266 else if (*offset
== IA2_TEXT_OFFSET_CARET
)
3267 get_caretOffset(offset
);
3270 ui::TextBoundaryType
BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary(
3271 IA2TextBoundaryType ia2_boundary
) {
3272 switch(ia2_boundary
) {
3273 case IA2_TEXT_BOUNDARY_CHAR
: return ui::CHAR_BOUNDARY
;
3274 case IA2_TEXT_BOUNDARY_WORD
: return ui::WORD_BOUNDARY
;
3275 case IA2_TEXT_BOUNDARY_LINE
: return ui::LINE_BOUNDARY
;
3276 case IA2_TEXT_BOUNDARY_SENTENCE
: return ui::SENTENCE_BOUNDARY
;
3277 case IA2_TEXT_BOUNDARY_PARAGRAPH
: return ui::PARAGRAPH_BOUNDARY
;
3278 case IA2_TEXT_BOUNDARY_ALL
: return ui::ALL_BOUNDARY
;
3281 return ui::CHAR_BOUNDARY
;
3285 LONG
BrowserAccessibilityWin::FindBoundary(
3286 const base::string16
& text
,
3287 IA2TextBoundaryType ia2_boundary
,
3289 ui::TextBoundaryDirection direction
) {
3290 HandleSpecialTextOffset(text
, &start_offset
);
3291 ui::TextBoundaryType boundary
= IA2TextBoundaryToTextBoundary(ia2_boundary
);
3292 const std::vector
<int32
>& line_breaks
= GetIntListAttribute(
3293 ui::AX_ATTR_LINE_BREAKS
);
3294 return ui::FindAccessibleTextBoundary(
3295 text
, line_breaks
, boundary
, start_offset
, direction
);
3298 BrowserAccessibilityWin
* BrowserAccessibilityWin::GetFromRendererID(
3299 int32 renderer_id
) {
3300 return manager()->GetFromRendererID(renderer_id
)->ToBrowserAccessibilityWin();
3303 void BrowserAccessibilityWin::InitRoleAndState() {
3305 ia2_state_
= IA2_STATE_OPAQUE
;
3306 ia2_attributes_
.clear();
3308 if (HasState(ui::AX_STATE_BUSY
))
3309 ia_state_
|= STATE_SYSTEM_BUSY
;
3310 if (HasState(ui::AX_STATE_CHECKED
))
3311 ia_state_
|= STATE_SYSTEM_CHECKED
;
3312 if (HasState(ui::AX_STATE_COLLAPSED
))
3313 ia_state_
|= STATE_SYSTEM_COLLAPSED
;
3314 if (HasState(ui::AX_STATE_EXPANDED
))
3315 ia_state_
|= STATE_SYSTEM_EXPANDED
;
3316 if (HasState(ui::AX_STATE_FOCUSABLE
))
3317 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3318 if (HasState(ui::AX_STATE_HASPOPUP
))
3319 ia_state_
|= STATE_SYSTEM_HASPOPUP
;
3320 if (HasState(ui::AX_STATE_HOVERED
))
3321 ia_state_
|= STATE_SYSTEM_HOTTRACKED
;
3322 if (HasState(ui::AX_STATE_INDETERMINATE
))
3323 ia_state_
|= STATE_SYSTEM_INDETERMINATE
;
3324 if (HasState(ui::AX_STATE_INVISIBLE
))
3325 ia_state_
|= STATE_SYSTEM_INVISIBLE
;
3326 if (HasState(ui::AX_STATE_LINKED
))
3327 ia_state_
|= STATE_SYSTEM_LINKED
;
3328 if (HasState(ui::AX_STATE_MULTISELECTABLE
)) {
3329 ia_state_
|= STATE_SYSTEM_EXTSELECTABLE
;
3330 ia_state_
|= STATE_SYSTEM_MULTISELECTABLE
;
3332 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
3333 if (HasState(ui::AX_STATE_OFFSCREEN
))
3334 ia_state_
|= STATE_SYSTEM_OFFSCREEN
;
3335 if (HasState(ui::AX_STATE_PRESSED
))
3336 ia_state_
|= STATE_SYSTEM_PRESSED
;
3337 if (HasState(ui::AX_STATE_PROTECTED
))
3338 ia_state_
|= STATE_SYSTEM_PROTECTED
;
3339 if (HasState(ui::AX_STATE_REQUIRED
))
3340 ia2_state_
|= IA2_STATE_REQUIRED
;
3341 if (HasState(ui::AX_STATE_SELECTABLE
))
3342 ia_state_
|= STATE_SYSTEM_SELECTABLE
;
3343 if (HasState(ui::AX_STATE_SELECTED
))
3344 ia_state_
|= STATE_SYSTEM_SELECTED
;
3345 if (HasState(ui::AX_STATE_VISITED
))
3346 ia_state_
|= STATE_SYSTEM_TRAVERSED
;
3347 if (!HasState(ui::AX_STATE_ENABLED
))
3348 ia_state_
|= STATE_SYSTEM_UNAVAILABLE
;
3349 if (HasState(ui::AX_STATE_VERTICAL
)) {
3350 ia2_state_
|= IA2_STATE_VERTICAL
;
3352 ia2_state_
|= IA2_STATE_HORIZONTAL
;
3354 if (HasState(ui::AX_STATE_VISITED
))
3355 ia_state_
|= STATE_SYSTEM_TRAVERSED
;
3357 // WebKit marks everything as readonly unless it's editable text, so if it's
3358 // not readonly, mark it as editable now. The final computation of the
3359 // READONLY state for MSAA is below, after the switch.
3360 if (!HasState(ui::AX_STATE_READONLY
))
3361 ia2_state_
|= IA2_STATE_EDITABLE
;
3363 base::string16 invalid
;
3364 if (GetHtmlAttribute("aria-invalid", &invalid
))
3365 ia2_state_
|= IA2_STATE_INVALID_ENTRY
;
3367 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED
))
3368 ia_state_
|= STATE_SYSTEM_MIXED
;
3370 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE
))
3371 ia2_state_
|= IA2_STATE_EDITABLE
;
3373 base::string16 html_tag
= GetString16Attribute(
3374 ui::AX_ATTR_HTML_TAG
);
3377 switch (blink_role()) {
3378 case ui::AX_ROLE_ALERT
:
3379 ia_role_
= ROLE_SYSTEM_ALERT
;
3381 case ui::AX_ROLE_ALERT_DIALOG
:
3382 ia_role_
= ROLE_SYSTEM_DIALOG
;
3384 case ui::AX_ROLE_APPLICATION
:
3385 ia_role_
= ROLE_SYSTEM_APPLICATION
;
3387 case ui::AX_ROLE_ARTICLE
:
3388 ia_role_
= ROLE_SYSTEM_GROUPING
;
3389 ia2_role_
= IA2_ROLE_SECTION
;
3390 ia_state_
|= STATE_SYSTEM_READONLY
;
3392 case ui::AX_ROLE_BUSY_INDICATOR
:
3393 ia_role_
= ROLE_SYSTEM_ANIMATION
;
3394 ia_state_
|= STATE_SYSTEM_READONLY
;
3396 case ui::AX_ROLE_BUTTON
:
3397 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3398 bool is_aria_pressed_defined
;
3400 if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined
, &is_mixed
))
3401 ia_state_
|= STATE_SYSTEM_PRESSED
;
3402 if (is_aria_pressed_defined
)
3403 ia2_role_
= IA2_ROLE_TOGGLE_BUTTON
;
3405 ia_state_
|= STATE_SYSTEM_MIXED
;
3407 case ui::AX_ROLE_CANVAS
:
3408 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK
)) {
3409 role_name_
= L
"canvas";
3410 ia2_role_
= IA2_ROLE_CANVAS
;
3412 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3415 case ui::AX_ROLE_CELL
:
3416 ia_role_
= ROLE_SYSTEM_CELL
;
3418 case ui::AX_ROLE_CHECK_BOX
:
3419 ia_role_
= ROLE_SYSTEM_CHECKBUTTON
;
3421 case ui::AX_ROLE_COLOR_WELL
:
3422 ia_role_
= ROLE_SYSTEM_CLIENT
;
3423 ia2_role_
= IA2_ROLE_COLOR_CHOOSER
;
3425 case ui::AX_ROLE_COLUMN
:
3426 ia_role_
= ROLE_SYSTEM_COLUMN
;
3427 ia_state_
|= STATE_SYSTEM_READONLY
;
3429 case ui::AX_ROLE_COLUMN_HEADER
:
3430 ia_role_
= ROLE_SYSTEM_COLUMNHEADER
;
3431 ia_state_
|= STATE_SYSTEM_READONLY
;
3433 case ui::AX_ROLE_COMBO_BOX
:
3434 ia_role_
= ROLE_SYSTEM_COMBOBOX
;
3436 case ui::AX_ROLE_DIV
:
3437 role_name_
= L
"div";
3438 ia2_role_
= IA2_ROLE_SECTION
;
3440 case ui::AX_ROLE_DEFINITION
:
3441 role_name_
= html_tag
;
3442 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3443 ia_state_
|= STATE_SYSTEM_READONLY
;
3445 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL
:
3446 role_name_
= html_tag
;
3447 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3448 ia_state_
|= STATE_SYSTEM_READONLY
;
3450 case ui::AX_ROLE_DESCRIPTION_LIST_TERM
:
3451 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3452 ia_state_
|= STATE_SYSTEM_READONLY
;
3454 case ui::AX_ROLE_DIALOG
:
3455 ia_role_
= ROLE_SYSTEM_DIALOG
;
3456 ia_state_
|= STATE_SYSTEM_READONLY
;
3458 case ui::AX_ROLE_DISCLOSURE_TRIANGLE
:
3459 ia_role_
= ROLE_SYSTEM_OUTLINEBUTTON
;
3460 ia_state_
|= STATE_SYSTEM_READONLY
;
3462 case ui::AX_ROLE_DOCUMENT
:
3463 case ui::AX_ROLE_ROOT_WEB_AREA
:
3464 case ui::AX_ROLE_WEB_AREA
:
3465 ia_role_
= ROLE_SYSTEM_DOCUMENT
;
3466 ia_state_
|= STATE_SYSTEM_READONLY
;
3467 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3469 case ui::AX_ROLE_EDITABLE_TEXT
:
3470 ia_role_
= ROLE_SYSTEM_TEXT
;
3471 ia2_state_
|= IA2_STATE_SINGLE_LINE
;
3472 ia2_state_
|= IA2_STATE_EDITABLE
;
3474 case ui::AX_ROLE_FORM
:
3475 role_name_
= L
"form";
3476 ia2_role_
= IA2_ROLE_FORM
;
3478 case ui::AX_ROLE_FOOTER
:
3479 ia_role_
= IA2_ROLE_FOOTER
;
3480 ia_state_
|= STATE_SYSTEM_READONLY
;
3482 case ui::AX_ROLE_GRID
:
3483 ia_role_
= ROLE_SYSTEM_TABLE
;
3484 ia_state_
|= STATE_SYSTEM_READONLY
;
3486 case ui::AX_ROLE_GROUP
: {
3487 base::string16 aria_role
= GetString16Attribute(
3489 if (aria_role
== L
"group" || html_tag
== L
"fieldset") {
3490 ia_role_
= ROLE_SYSTEM_GROUPING
;
3491 } else if (html_tag
== L
"li") {
3492 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3494 if (html_tag
.empty())
3495 role_name_
= L
"div";
3497 role_name_
= html_tag
;
3498 ia2_role_
= IA2_ROLE_SECTION
;
3500 ia_state_
|= STATE_SYSTEM_READONLY
;
3503 case ui::AX_ROLE_GROW_AREA
:
3504 ia_role_
= ROLE_SYSTEM_GRIP
;
3505 ia_state_
|= STATE_SYSTEM_READONLY
;
3507 case ui::AX_ROLE_HEADING
:
3508 role_name_
= html_tag
;
3509 ia2_role_
= IA2_ROLE_HEADING
;
3510 ia_state_
|= STATE_SYSTEM_READONLY
;
3512 case ui::AX_ROLE_HORIZONTAL_RULE
:
3513 ia_role_
= ROLE_SYSTEM_SEPARATOR
;
3515 case ui::AX_ROLE_IMAGE
:
3516 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3517 ia_state_
|= STATE_SYSTEM_READONLY
;
3519 case ui::AX_ROLE_IMAGE_MAP
:
3520 role_name_
= html_tag
;
3521 ia2_role_
= IA2_ROLE_IMAGE_MAP
;
3522 ia_state_
|= STATE_SYSTEM_READONLY
;
3524 case ui::AX_ROLE_IMAGE_MAP_LINK
:
3525 ia_role_
= ROLE_SYSTEM_LINK
;
3526 ia_state_
|= STATE_SYSTEM_LINKED
;
3527 ia_state_
|= STATE_SYSTEM_READONLY
;
3529 case ui::AX_ROLE_LABEL
:
3530 ia_role_
= ROLE_SYSTEM_TEXT
;
3531 ia2_role_
= IA2_ROLE_LABEL
;
3533 case ui::AX_ROLE_BANNER
:
3534 case ui::AX_ROLE_COMPLEMENTARY
:
3535 case ui::AX_ROLE_CONTENT_INFO
:
3536 case ui::AX_ROLE_MAIN
:
3537 case ui::AX_ROLE_NAVIGATION
:
3538 case ui::AX_ROLE_SEARCH
:
3539 ia_role_
= ROLE_SYSTEM_GROUPING
;
3540 ia2_role_
= IA2_ROLE_SECTION
;
3541 ia_state_
|= STATE_SYSTEM_READONLY
;
3543 case ui::AX_ROLE_LINK
:
3544 ia_role_
= ROLE_SYSTEM_LINK
;
3545 ia_state_
|= STATE_SYSTEM_LINKED
;
3547 case ui::AX_ROLE_LIST
:
3548 ia_role_
= ROLE_SYSTEM_LIST
;
3549 ia_state_
|= STATE_SYSTEM_READONLY
;
3551 case ui::AX_ROLE_LIST_BOX
:
3552 ia_role_
= ROLE_SYSTEM_LIST
;
3554 case ui::AX_ROLE_LIST_BOX_OPTION
:
3555 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3556 if (ia_state_
& STATE_SYSTEM_SELECTABLE
) {
3557 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3558 if (HasState(ui::AX_STATE_FOCUSED
))
3559 ia_state_
|= STATE_SYSTEM_FOCUSED
;
3562 case ui::AX_ROLE_LIST_ITEM
:
3563 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3564 ia_state_
|= STATE_SYSTEM_READONLY
;
3566 case ui::AX_ROLE_LIST_MARKER
:
3567 ia_role_
= ROLE_SYSTEM_TEXT
;
3568 ia_state_
|= STATE_SYSTEM_READONLY
;
3570 case ui::AX_ROLE_MATH_ELEMENT
:
3571 ia_role_
= ROLE_SYSTEM_EQUATION
;
3572 ia_state_
|= STATE_SYSTEM_READONLY
;
3574 case ui::AX_ROLE_MENU
:
3575 case ui::AX_ROLE_MENU_BUTTON
:
3576 ia_role_
= ROLE_SYSTEM_MENUPOPUP
;
3578 case ui::AX_ROLE_MENU_BAR
:
3579 ia_role_
= ROLE_SYSTEM_MENUBAR
;
3581 case ui::AX_ROLE_MENU_ITEM
:
3582 ia_role_
= ROLE_SYSTEM_MENUITEM
;
3584 case ui::AX_ROLE_MENU_LIST_POPUP
:
3585 ia_role_
= ROLE_SYSTEM_CLIENT
;
3587 case ui::AX_ROLE_MENU_LIST_OPTION
:
3588 ia_role_
= ROLE_SYSTEM_LISTITEM
;
3589 if (ia_state_
& STATE_SYSTEM_SELECTABLE
) {
3590 ia_state_
|= STATE_SYSTEM_FOCUSABLE
;
3591 if (HasState(ui::AX_STATE_FOCUSED
))
3592 ia_state_
|= STATE_SYSTEM_FOCUSED
;
3595 case ui::AX_ROLE_NOTE
:
3596 ia_role_
= ROLE_SYSTEM_GROUPING
;
3597 ia2_role_
= IA2_ROLE_NOTE
;
3598 ia_state_
|= STATE_SYSTEM_READONLY
;
3600 case ui::AX_ROLE_OUTLINE
:
3601 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3602 ia_state_
|= STATE_SYSTEM_READONLY
;
3604 case ui::AX_ROLE_PARAGRAPH
:
3606 ia2_role_
= IA2_ROLE_PARAGRAPH
;
3608 case ui::AX_ROLE_POP_UP_BUTTON
:
3609 if (html_tag
== L
"select") {
3610 ia_role_
= ROLE_SYSTEM_COMBOBOX
;
3612 ia_role_
= ROLE_SYSTEM_BUTTONMENU
;
3615 case ui::AX_ROLE_PROGRESS_INDICATOR
:
3616 ia_role_
= ROLE_SYSTEM_PROGRESSBAR
;
3617 ia_state_
|= STATE_SYSTEM_READONLY
;
3619 case ui::AX_ROLE_RADIO_BUTTON
:
3620 ia_role_
= ROLE_SYSTEM_RADIOBUTTON
;
3622 case ui::AX_ROLE_RADIO_GROUP
:
3623 ia_role_
= ROLE_SYSTEM_GROUPING
;
3624 ia2_role_
= IA2_ROLE_SECTION
;
3626 case ui::AX_ROLE_REGION
:
3627 ia_role_
= ROLE_SYSTEM_GROUPING
;
3628 ia2_role_
= IA2_ROLE_SECTION
;
3629 ia_state_
|= STATE_SYSTEM_READONLY
;
3631 case ui::AX_ROLE_ROW
:
3632 ia_role_
= ROLE_SYSTEM_ROW
;
3633 ia_state_
|= STATE_SYSTEM_READONLY
;
3635 case ui::AX_ROLE_ROW_HEADER
:
3636 ia_role_
= ROLE_SYSTEM_ROWHEADER
;
3637 ia_state_
|= STATE_SYSTEM_READONLY
;
3639 case ui::AX_ROLE_RULER
:
3640 ia_role_
= ROLE_SYSTEM_CLIENT
;
3641 ia2_role_
= IA2_ROLE_RULER
;
3642 ia_state_
|= STATE_SYSTEM_READONLY
;
3644 case ui::AX_ROLE_SCROLL_AREA
:
3645 ia_role_
= ROLE_SYSTEM_CLIENT
;
3646 ia2_role_
= IA2_ROLE_SCROLL_PANE
;
3647 ia_state_
|= STATE_SYSTEM_READONLY
;
3649 case ui::AX_ROLE_SCROLL_BAR
:
3650 ia_role_
= ROLE_SYSTEM_SCROLLBAR
;
3652 case ui::AX_ROLE_SLIDER
:
3653 ia_role_
= ROLE_SYSTEM_SLIDER
;
3655 case ui::AX_ROLE_SPIN_BUTTON
:
3656 ia_role_
= ROLE_SYSTEM_SPINBUTTON
;
3658 case ui::AX_ROLE_SPIN_BUTTON_PART
:
3659 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3661 case ui::AX_ROLE_SPLIT_GROUP
:
3662 ia_role_
= ROLE_SYSTEM_CLIENT
;
3663 ia2_role_
= IA2_ROLE_SPLIT_PANE
;
3664 ia_state_
|= STATE_SYSTEM_READONLY
;
3666 case ui::AX_ROLE_ANNOTATION
:
3667 case ui::AX_ROLE_STATIC_TEXT
:
3668 ia_role_
= ROLE_SYSTEM_TEXT
;
3669 ia_state_
|= STATE_SYSTEM_READONLY
;
3671 case ui::AX_ROLE_STATUS
:
3672 ia_role_
= ROLE_SYSTEM_STATUSBAR
;
3673 ia_state_
|= STATE_SYSTEM_READONLY
;
3675 case ui::AX_ROLE_SPLITTER
:
3676 ia_role_
= ROLE_SYSTEM_SEPARATOR
;
3678 case ui::AX_ROLE_SVG_ROOT
:
3679 ia_role_
= ROLE_SYSTEM_GRAPHIC
;
3681 case ui::AX_ROLE_TAB
:
3682 ia_role_
= ROLE_SYSTEM_PAGETAB
;
3684 case ui::AX_ROLE_TABLE
: {
3685 base::string16 aria_role
= GetString16Attribute(
3687 if (aria_role
== L
"treegrid") {
3688 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3690 ia_role_
= ROLE_SYSTEM_TABLE
;
3691 ia_state_
|= STATE_SYSTEM_READONLY
;
3695 case ui::AX_ROLE_TABLE_HEADER_CONTAINER
:
3696 ia_role_
= ROLE_SYSTEM_GROUPING
;
3697 ia2_role_
= IA2_ROLE_SECTION
;
3698 ia_state_
|= STATE_SYSTEM_READONLY
;
3700 case ui::AX_ROLE_TAB_LIST
:
3701 ia_role_
= ROLE_SYSTEM_PAGETABLIST
;
3703 case ui::AX_ROLE_TAB_PANEL
:
3704 ia_role_
= ROLE_SYSTEM_PROPERTYPAGE
;
3706 case ui::AX_ROLE_TOGGLE_BUTTON
:
3707 ia_role_
= ROLE_SYSTEM_PUSHBUTTON
;
3708 ia2_role_
= IA2_ROLE_TOGGLE_BUTTON
;
3710 case ui::AX_ROLE_TEXT_AREA
:
3711 ia_role_
= ROLE_SYSTEM_TEXT
;
3712 ia2_state_
|= IA2_STATE_MULTI_LINE
;
3713 ia2_state_
|= IA2_STATE_EDITABLE
;
3714 ia2_state_
|= IA2_STATE_SELECTABLE_TEXT
;
3716 case ui::AX_ROLE_TEXT_FIELD
:
3717 ia_role_
= ROLE_SYSTEM_TEXT
;
3718 ia2_state_
|= IA2_STATE_SINGLE_LINE
;
3719 ia2_state_
|= IA2_STATE_EDITABLE
;
3720 ia2_state_
|= IA2_STATE_SELECTABLE_TEXT
;
3722 case ui::AX_ROLE_TIMER
:
3723 ia_role_
= ROLE_SYSTEM_CLOCK
;
3724 ia_state_
|= STATE_SYSTEM_READONLY
;
3726 case ui::AX_ROLE_TOOLBAR
:
3727 ia_role_
= ROLE_SYSTEM_TOOLBAR
;
3728 ia_state_
|= STATE_SYSTEM_READONLY
;
3730 case ui::AX_ROLE_TOOLTIP
:
3731 ia_role_
= ROLE_SYSTEM_TOOLTIP
;
3732 ia_state_
|= STATE_SYSTEM_READONLY
;
3734 case ui::AX_ROLE_TREE
:
3735 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3736 ia_state_
|= STATE_SYSTEM_READONLY
;
3738 case ui::AX_ROLE_TREE_GRID
:
3739 ia_role_
= ROLE_SYSTEM_OUTLINE
;
3740 ia_state_
|= STATE_SYSTEM_READONLY
;
3742 case ui::AX_ROLE_TREE_ITEM
:
3743 ia_role_
= ROLE_SYSTEM_OUTLINEITEM
;
3744 ia_state_
|= STATE_SYSTEM_READONLY
;
3746 case ui::AX_ROLE_WINDOW
:
3747 ia_role_
= ROLE_SYSTEM_WINDOW
;
3750 // TODO(dmazzoni): figure out the proper MSAA role for all of these.
3751 case ui::AX_ROLE_BROWSER
:
3752 case ui::AX_ROLE_DIRECTORY
:
3753 case ui::AX_ROLE_DRAWER
:
3754 case ui::AX_ROLE_HELP_TAG
:
3755 case ui::AX_ROLE_IGNORED
:
3756 case ui::AX_ROLE_INCREMENTOR
:
3757 case ui::AX_ROLE_LOG
:
3758 case ui::AX_ROLE_MARQUEE
:
3759 case ui::AX_ROLE_MATTE
:
3760 case ui::AX_ROLE_PRESENTATIONAL
:
3761 case ui::AX_ROLE_RULER_MARKER
:
3762 case ui::AX_ROLE_SHEET
:
3763 case ui::AX_ROLE_SLIDER_THUMB
:
3764 case ui::AX_ROLE_SYSTEM_WIDE
:
3765 case ui::AX_ROLE_VALUE_INDICATOR
:
3767 ia_role_
= ROLE_SYSTEM_CLIENT
;
3771 // Compute the final value of READONLY for MSAA.
3773 // We always set the READONLY state for elements that have the
3774 // aria-readonly attribute and for a few roles (in the switch above).
3775 // We clear the READONLY state on focusable controls and on a document.
3776 // Everything else, the majority of objects, do not have this state set.
3777 if (HasState(ui::AX_STATE_FOCUSABLE
) &&
3778 ia_role_
!= ROLE_SYSTEM_DOCUMENT
) {
3779 ia_state_
&= ~(STATE_SYSTEM_READONLY
);
3781 if (!HasState(ui::AX_STATE_READONLY
))
3782 ia_state_
&= ~(STATE_SYSTEM_READONLY
);
3783 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY
))
3784 ia_state_
|= STATE_SYSTEM_READONLY
;
3786 // The role should always be set.
3787 DCHECK(!role_name_
.empty() || ia_role_
);
3789 // If we didn't explicitly set the IAccessible2 role, make it the same
3790 // as the MSAA role.
3792 ia2_role_
= ia_role_
;
3795 } // namespace content