1 // Copyright 2015 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.
9 #include "base/memory/scoped_ptr.h"
10 #include "base/win/scoped_bstr.h"
11 #include "base/win/scoped_comptr.h"
12 #include "base/win/scoped_variant.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/iaccessible2/ia2_api_all.h"
15 #include "ui/accessibility/ax_node_data.h"
16 #include "ui/accessibility/platform/ax_platform_node.h"
17 #include "ui/accessibility/platform/test_ax_node_wrapper.h"
18 #include "ui/base/win/atl_module.h"
20 using base::win::ScopedBstr
;
21 using base::win::ScopedComPtr
;
22 using base::win::ScopedVariant
;
28 // Most IAccessible functions require a VARIANT set to CHILDID_SELF as
29 // the first argument.
30 ScopedVariant
SELF(CHILDID_SELF
);
34 class AXPlatformNodeWinTest
: public testing::Test
{
36 AXPlatformNodeWinTest() {}
37 virtual ~AXPlatformNodeWinTest() {}
39 void SetUp() override
{
40 win::CreateATLModuleIfNeeded();
43 // Initialize given an AXTreeUpdate.
44 void Init(const AXTreeUpdate
& initial_state
) {
45 tree_
.reset(new AXTree(initial_state
));
48 // Convenience functions to initialize directly from a few AXNodeDatas.
49 void Init(const AXNodeData
& node1
) {
51 update
.nodes
.push_back(node1
);
55 void Init(const AXNodeData
& node1
,
56 const AXNodeData
& node2
) {
58 update
.nodes
.push_back(node1
);
59 update
.nodes
.push_back(node2
);
63 void Init(const AXNodeData
& node1
,
64 const AXNodeData
& node2
,
65 const AXNodeData
& node3
) {
67 update
.nodes
.push_back(node1
);
68 update
.nodes
.push_back(node2
);
69 update
.nodes
.push_back(node3
);
74 AXNode
* GetRootNode() {
78 ScopedComPtr
<IAccessible
> IAccessibleFromNode(AXNode
* node
) {
79 TestAXNodeWrapper
* wrapper
=
80 TestAXNodeWrapper::GetOrCreate(tree_
.get(), node
);
81 AXPlatformNode
* ax_platform_node
= wrapper
->ax_platform_node();
82 IAccessible
* iaccessible
= ax_platform_node
->GetNativeViewAccessible();
83 iaccessible
->AddRef();
84 return ScopedComPtr
<IAccessible
>(iaccessible
);
87 ScopedComPtr
<IAccessible
> GetRootIAccessible() {
88 return IAccessibleFromNode(GetRootNode());
91 ScopedComPtr
<IAccessible2
> ToIAccessible2(
92 ScopedComPtr
<IAccessible
> accessible
) {
93 ScopedComPtr
<IServiceProvider
> service_provider
;
94 service_provider
.QueryFrom(accessible
.get());
95 ScopedComPtr
<IAccessible2
> result
;
97 service_provider
->QueryService(IID_IAccessible2
, result
.Receive())));
101 scoped_ptr
<AXTree
> tree_
;
104 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleDetachedObject
) {
107 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
108 root
.AddStringAttribute(AX_ATTR_NAME
, "Name");
111 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
113 ASSERT_EQ(S_OK
, root_obj
->get_accName(SELF
, name
.Receive()));
114 EXPECT_EQ(L
"Name", base::string16(name
));
116 tree_
.reset(new AXTree());
118 ASSERT_EQ(E_FAIL
, root_obj
->get_accName(SELF
, name2
.Receive()));
121 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleName
) {
124 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
125 root
.AddStringAttribute(AX_ATTR_NAME
, "Name");
128 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
130 ASSERT_EQ(S_OK
, root_obj
->get_accName(SELF
, name
.Receive()));
131 EXPECT_EQ(L
"Name", base::string16(name
));
133 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accName(SELF
, nullptr));
134 ScopedVariant
bad_id(999);
136 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accName(bad_id
, name2
.Receive()));
139 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleDescription
) {
142 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
143 root
.AddStringAttribute(AX_ATTR_DESCRIPTION
, "Description");
146 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
147 ScopedBstr description
;
148 ASSERT_EQ(S_OK
, root_obj
->get_accDescription(SELF
, description
.Receive()));
149 EXPECT_EQ(L
"Description", base::string16(description
));
151 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accDescription(SELF
, nullptr));
152 ScopedVariant
bad_id(999);
154 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accDescription(bad_id
, d2
.Receive()));
157 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleHelp
) {
160 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
161 root
.AddStringAttribute(AX_ATTR_HELP
, "Help");
164 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
166 ASSERT_EQ(S_OK
, root_obj
->get_accHelp(SELF
, help
.Receive()));
167 EXPECT_EQ(L
"Help", base::string16(help
));
169 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accHelp(SELF
, nullptr));
170 ScopedVariant
bad_id(999);
172 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accHelp(bad_id
, h2
.Receive()));
175 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleValue
) {
178 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
179 root
.AddStringAttribute(AX_ATTR_VALUE
, "Value");
182 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
184 ASSERT_EQ(S_OK
, root_obj
->get_accValue(SELF
, value
.Receive()));
185 EXPECT_EQ(L
"Value", base::string16(value
));
187 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accValue(SELF
, nullptr));
188 ScopedVariant
bad_id(999);
190 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accValue(bad_id
, v2
.Receive()));
193 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleShortcut
) {
196 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
197 root
.AddStringAttribute(AX_ATTR_SHORTCUT
, "Shortcut");
200 ScopedComPtr
<IAccessible
> root_obj(GetRootIAccessible());
202 ASSERT_EQ(S_OK
, root_obj
->get_accKeyboardShortcut(
203 SELF
, shortcut
.Receive()));
204 EXPECT_EQ(L
"Shortcut", base::string16(shortcut
));
206 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accKeyboardShortcut(SELF
, nullptr));
207 ScopedVariant
bad_id(999);
209 ASSERT_EQ(E_INVALIDARG
, root_obj
->get_accKeyboardShortcut(
210 bad_id
, k2
.Receive()));
213 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleRole
) {
216 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
217 root
.child_ids
.push_back(2);
223 AXNode
* child_node
= GetRootNode()->children()[0];
224 ScopedComPtr
<IAccessible
> child_iaccessible(
225 IAccessibleFromNode(child_node
));
229 child
.role
= AX_ROLE_ALERT
;
230 child_node
->SetData(child
);
231 ASSERT_EQ(S_OK
, child_iaccessible
->get_accRole(SELF
, role
.Receive()));
232 EXPECT_EQ(ROLE_SYSTEM_ALERT
, V_I4(role
.ptr()));
234 child
.role
= AX_ROLE_BUTTON
;
235 child_node
->SetData(child
);
236 ASSERT_EQ(S_OK
, child_iaccessible
->get_accRole(SELF
, role
.Receive()));
237 EXPECT_EQ(ROLE_SYSTEM_PUSHBUTTON
, V_I4(role
.ptr()));
239 child
.role
= AX_ROLE_POP_UP_BUTTON
;
240 child_node
->SetData(child
);
241 ASSERT_EQ(S_OK
, child_iaccessible
->get_accRole(SELF
, role
.Receive()));
242 EXPECT_EQ(ROLE_SYSTEM_BUTTONMENU
, V_I4(role
.ptr()));
244 ASSERT_EQ(E_INVALIDARG
, child_iaccessible
->get_accRole(SELF
, nullptr));
245 ScopedVariant
bad_id(999);
246 ASSERT_EQ(E_INVALIDARG
, child_iaccessible
->get_accRole(
247 bad_id
, role
.Receive()));
250 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleLocation
) {
253 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
254 root
.location
= gfx::Rect(10, 40, 800, 600);
257 TestAXNodeWrapper::SetGlobalCoordinateOffset(gfx::Vector2d(100, 200));
259 LONG x_left
, y_top
, width
, height
;
260 ASSERT_EQ(S_OK
, GetRootIAccessible()->accLocation(
261 &x_left
, &y_top
, &width
, &height
, SELF
));
262 EXPECT_EQ(110, x_left
);
263 EXPECT_EQ(240, y_top
);
264 EXPECT_EQ(800, width
);
265 EXPECT_EQ(600, height
);
267 ASSERT_EQ(E_INVALIDARG
, GetRootIAccessible()->accLocation(
268 nullptr, &y_top
, &width
, &height
, SELF
));
269 ASSERT_EQ(E_INVALIDARG
, GetRootIAccessible()->accLocation(
270 &x_left
, nullptr, &width
, &height
, SELF
));
271 ASSERT_EQ(E_INVALIDARG
, GetRootIAccessible()->accLocation(
272 &x_left
, &y_top
, nullptr, &height
, SELF
));
273 ASSERT_EQ(E_INVALIDARG
, GetRootIAccessible()->accLocation(
274 &x_left
, &y_top
, &width
, nullptr, SELF
));
275 ScopedVariant
bad_id(999);
276 ASSERT_EQ(E_INVALIDARG
, GetRootIAccessible()->accLocation(
277 &x_left
, &y_top
, &width
, &height
, bad_id
));
280 TEST_F(AXPlatformNodeWinTest
, TestIAccessibleChildAndParent
) {
283 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
284 root
.child_ids
.push_back(2);
285 root
.child_ids
.push_back(3);
288 button
.role
= AX_ROLE_BUTTON
;
292 checkbox
.role
= AX_ROLE_CHECK_BOX
;
295 Init(root
, button
, checkbox
);
296 AXNode
* button_node
= GetRootNode()->children()[0];
297 AXNode
* checkbox_node
= GetRootNode()->children()[1];
298 ScopedComPtr
<IAccessible
> root_iaccessible(GetRootIAccessible());
299 ScopedComPtr
<IAccessible
> button_iaccessible(
300 IAccessibleFromNode(button_node
));
301 ScopedComPtr
<IAccessible
> checkbox_iaccessible(
302 IAccessibleFromNode(checkbox_node
));
305 ASSERT_EQ(S_OK
, root_iaccessible
->get_accChildCount(&child_count
));
306 ASSERT_EQ(2L, child_count
);
307 ASSERT_EQ(S_OK
, button_iaccessible
->get_accChildCount(&child_count
));
308 ASSERT_EQ(0L, child_count
);
309 ASSERT_EQ(S_OK
, checkbox_iaccessible
->get_accChildCount(&child_count
));
310 ASSERT_EQ(0L, child_count
);
313 ScopedComPtr
<IDispatch
> result
;
314 ASSERT_EQ(S_OK
, root_iaccessible
->get_accChild(SELF
, result
.Receive()));
315 ASSERT_EQ(result
.get(), root_iaccessible
);
319 ScopedComPtr
<IDispatch
> result
;
320 ScopedVariant
child1(1);
321 ASSERT_EQ(S_OK
, root_iaccessible
->get_accChild(child1
, result
.Receive()));
322 ASSERT_EQ(result
.get(), button_iaccessible
);
326 ScopedComPtr
<IDispatch
> result
;
327 ScopedVariant
child2(2);
328 ASSERT_EQ(S_OK
, root_iaccessible
->get_accChild(child2
, result
.Receive()));
329 ASSERT_EQ(result
.get(), checkbox_iaccessible
);
333 // Asking for child id 3 should fail.
334 ScopedComPtr
<IDispatch
> result
;
335 ScopedVariant
child3(3);
336 ASSERT_EQ(E_FAIL
, root_iaccessible
->get_accChild(child3
, result
.Receive()));
339 // We should be able to ask for the button by its unique id too.
340 LONG button_unique_id
;
341 ScopedComPtr
<IAccessible2
> button_iaccessible2
=
342 ToIAccessible2(button_iaccessible
);
343 button_iaccessible2
->get_uniqueID(&button_unique_id
);
344 ASSERT_LT(button_unique_id
, 0);
346 ScopedComPtr
<IDispatch
> result
;
347 ScopedVariant
button_id_variant(button_unique_id
);
348 ASSERT_EQ(S_OK
, root_iaccessible
->get_accChild(button_id_variant
,
350 ASSERT_EQ(result
.get(), button_iaccessible
);
353 // Now check parents.
355 ScopedComPtr
<IDispatch
> result
;
356 ASSERT_EQ(S_OK
, button_iaccessible
->get_accParent(result
.Receive()));
357 ASSERT_EQ(result
.get(), root_iaccessible
);
361 ScopedComPtr
<IDispatch
> result
;
362 ASSERT_EQ(S_OK
, checkbox_iaccessible
->get_accParent(result
.Receive()));
363 ASSERT_EQ(result
.get(), root_iaccessible
);
367 ScopedComPtr
<IDispatch
> result
;
368 ASSERT_EQ(S_FALSE
, root_iaccessible
->get_accParent(result
.Receive()));
372 TEST_F(AXPlatformNodeWinTest
, TestIAccessible2IndexInParent
) {
375 root
.role
= AX_ROLE_ROOT_WEB_AREA
;
376 root
.child_ids
.push_back(2);
377 root
.child_ids
.push_back(3);
385 Init(root
, left
, right
);
386 ScopedComPtr
<IAccessible
> root_iaccessible(GetRootIAccessible());
387 ScopedComPtr
<IAccessible2
> root_iaccessible2
=
388 ToIAccessible2(root_iaccessible
);
389 ScopedComPtr
<IAccessible
> left_iaccessible(
390 IAccessibleFromNode(GetRootNode()->children()[0]));
391 ScopedComPtr
<IAccessible2
> left_iaccessible2
=
392 ToIAccessible2(left_iaccessible
);
393 ScopedComPtr
<IAccessible
> right_iaccessible(
394 IAccessibleFromNode(GetRootNode()->children()[1]));
395 ScopedComPtr
<IAccessible2
> right_iaccessible2
=
396 ToIAccessible2(right_iaccessible
);
399 ASSERT_EQ(E_FAIL
, root_iaccessible2
->get_indexInParent(&index
));
401 ASSERT_EQ(S_OK
, left_iaccessible2
->get_indexInParent(&index
));
404 ASSERT_EQ(S_OK
, right_iaccessible2
->get_indexInParent(&index
));