OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/accessibility/platform/ax_platform_node.h" | 5 #include "ui/accessibility/platform/ax_platform_node.h" |
6 | 6 |
7 #include <atlbase.h> | 7 #include <atlbase.h> |
8 #include <atlcom.h> | 8 #include <atlcom.h> |
9 #include <oleacc.h> | 9 #include <oleacc.h> |
10 | 10 |
(...skipping 29 matching lines...) Expand all Loading... |
40 | 40 |
41 void SetUp() override { | 41 void SetUp() override { |
42 win::CreateATLModuleIfNeeded(); | 42 win::CreateATLModuleIfNeeded(); |
43 } | 43 } |
44 | 44 |
45 // Initialize given an AXTreeUpdate. | 45 // Initialize given an AXTreeUpdate. |
46 void Init(const AXTreeUpdate& initial_state) { | 46 void Init(const AXTreeUpdate& initial_state) { |
47 tree_.reset(new AXTree(initial_state)); | 47 tree_.reset(new AXTree(initial_state)); |
48 } | 48 } |
49 | 49 |
50 // Convenience functions to initialize directly from a few AXNodeDatas. | 50 // Convenience functions to initialize directly from a few AXNodeData objects. |
51 void Init(const AXNodeData& node1) { | 51 void Init(const AXNodeData& node1) { |
52 AXTreeUpdate update; | 52 AXTreeUpdate update; |
53 update.root_id = node1.id; | 53 update.root_id = node1.id; |
54 update.nodes.push_back(node1); | 54 update.nodes.push_back(node1); |
55 Init(update); | 55 Init(update); |
56 } | 56 } |
57 | 57 |
58 void Init(const AXNodeData& node1, | 58 void Init(const AXNodeData& node1, |
59 const AXNodeData& node2) { | 59 const AXNodeData& node2) { |
60 AXTreeUpdate update; | 60 AXTreeUpdate update; |
(...skipping 15 matching lines...) Expand all Loading... |
76 } | 76 } |
77 | 77 |
78 protected: | 78 protected: |
79 AXNode* GetRootNode() { | 79 AXNode* GetRootNode() { |
80 return tree_->root(); | 80 return tree_->root(); |
81 } | 81 } |
82 | 82 |
83 ScopedComPtr<IAccessible> IAccessibleFromNode(AXNode* node) { | 83 ScopedComPtr<IAccessible> IAccessibleFromNode(AXNode* node) { |
84 TestAXNodeWrapper* wrapper = | 84 TestAXNodeWrapper* wrapper = |
85 TestAXNodeWrapper::GetOrCreate(tree_.get(), node); | 85 TestAXNodeWrapper::GetOrCreate(tree_.get(), node); |
| 86 if (!wrapper) |
| 87 return ScopedComPtr<IAccessible>(); |
86 AXPlatformNode* ax_platform_node = wrapper->ax_platform_node(); | 88 AXPlatformNode* ax_platform_node = wrapper->ax_platform_node(); |
87 IAccessible* iaccessible = ax_platform_node->GetNativeViewAccessible(); | 89 IAccessible* iaccessible = ax_platform_node->GetNativeViewAccessible(); |
88 iaccessible->AddRef(); | |
89 return ScopedComPtr<IAccessible>(iaccessible); | 90 return ScopedComPtr<IAccessible>(iaccessible); |
90 } | 91 } |
91 | 92 |
92 ScopedComPtr<IAccessible> GetRootIAccessible() { | 93 ScopedComPtr<IAccessible> GetRootIAccessible() { |
93 return IAccessibleFromNode(GetRootNode()); | 94 return IAccessibleFromNode(GetRootNode()); |
94 } | 95 } |
95 | 96 |
96 ScopedComPtr<IAccessible2> ToIAccessible2( | 97 ScopedComPtr<IAccessible2> ToIAccessible2( |
97 ScopedComPtr<IAccessible> accessible) { | 98 ScopedComPtr<IAccessible> accessible) { |
98 CHECK(accessible.Get()); | 99 CHECK(accessible); |
99 ScopedComPtr<IServiceProvider> service_provider; | 100 ScopedComPtr<IServiceProvider> service_provider; |
100 service_provider.QueryFrom(accessible.Get()); | 101 service_provider.QueryFrom(accessible.Get()); |
101 ScopedComPtr<IAccessible2> result; | 102 ScopedComPtr<IAccessible2> result; |
102 CHECK(SUCCEEDED(service_provider->QueryService(IID_IAccessible2, | 103 CHECK(SUCCEEDED(service_provider->QueryService(IID_IAccessible2, |
103 result.GetAddressOf()))); | 104 result.GetAddressOf()))); |
104 return result; | 105 return result; |
105 } | 106 } |
106 | 107 |
107 std::unique_ptr<AXTree> tree_; | 108 std::unique_ptr<AXTree> tree_; |
108 }; | 109 }; |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 LONG index; | 395 LONG index; |
395 EXPECT_EQ(E_FAIL, root_iaccessible2->get_indexInParent(&index)); | 396 EXPECT_EQ(E_FAIL, root_iaccessible2->get_indexInParent(&index)); |
396 | 397 |
397 EXPECT_EQ(S_OK, left_iaccessible2->get_indexInParent(&index)); | 398 EXPECT_EQ(S_OK, left_iaccessible2->get_indexInParent(&index)); |
398 EXPECT_EQ(0, index); | 399 EXPECT_EQ(0, index); |
399 | 400 |
400 EXPECT_EQ(S_OK, right_iaccessible2->get_indexInParent(&index)); | 401 EXPECT_EQ(S_OK, right_iaccessible2->get_indexInParent(&index)); |
401 EXPECT_EQ(1, index); | 402 EXPECT_EQ(1, index); |
402 } | 403 } |
403 | 404 |
| 405 TEST_F(AXPlatformNodeWinTest, TestAccNavigate) { |
| 406 AXNodeData root; |
| 407 root.id = 1; |
| 408 root.role = AX_ROLE_ROOT_WEB_AREA; |
| 409 |
| 410 AXNodeData child1; |
| 411 child1.id = 2; |
| 412 child1.role = AX_ROLE_STATIC_TEXT; |
| 413 root.child_ids.push_back(2); |
| 414 |
| 415 AXNodeData child2; |
| 416 child2.id = 3; |
| 417 child2.role = AX_ROLE_STATIC_TEXT; |
| 418 root.child_ids.push_back(3); |
| 419 |
| 420 Init(root, child1, child2); |
| 421 ScopedComPtr<IAccessible> ia_root(GetRootIAccessible()); |
| 422 ScopedComPtr<IDispatch> disp_root; |
| 423 ASSERT_HRESULT_SUCCEEDED(ia_root.CopyTo(disp_root.GetAddressOf())); |
| 424 ScopedVariant var_root(disp_root.Get()); |
| 425 ScopedComPtr<IAccessible> ia_child1( |
| 426 IAccessibleFromNode(GetRootNode()->children()[0])); |
| 427 ScopedComPtr<IDispatch> disp_child1; |
| 428 ASSERT_HRESULT_SUCCEEDED(ia_child1.CopyTo(disp_child1.GetAddressOf())); |
| 429 ScopedVariant var_child1(disp_child1.Get()); |
| 430 ScopedComPtr<IAccessible> ia_child2( |
| 431 IAccessibleFromNode(GetRootNode()->children()[1])); |
| 432 ScopedComPtr<IDispatch> disp_child2; |
| 433 ASSERT_HRESULT_SUCCEEDED(ia_child2.CopyTo(disp_child2.GetAddressOf())); |
| 434 ScopedVariant var_child2(disp_child2.Get()); |
| 435 ScopedVariant end; |
| 436 |
| 437 // Invalid arguments. |
| 438 EXPECT_EQ( |
| 439 E_INVALIDARG, |
| 440 ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant::kEmptyVariant, nullptr)); |
| 441 EXPECT_EQ(E_INVALIDARG, |
| 442 ia_child1->accNavigate(NAVDIR_NEXT, ScopedVariant::kEmptyVariant, |
| 443 end.AsInput())); |
| 444 EXPECT_EQ(VT_EMPTY, end.type()); |
| 445 |
| 446 // Navigating to first/last child should only be from self. |
| 447 EXPECT_EQ(E_INVALIDARG, |
| 448 ia_root->accNavigate(NAVDIR_FIRSTCHILD, var_root, end.AsInput())); |
| 449 EXPECT_EQ(VT_EMPTY, end.type()); |
| 450 EXPECT_EQ(E_INVALIDARG, |
| 451 ia_root->accNavigate(NAVDIR_LASTCHILD, var_root, end.AsInput())); |
| 452 EXPECT_EQ(VT_EMPTY, end.type()); |
| 453 |
| 454 // Spatial directions are not supported. |
| 455 EXPECT_EQ(E_NOTIMPL, ia_child1->accNavigate(NAVDIR_UP, SELF, end.AsInput())); |
| 456 EXPECT_EQ(E_NOTIMPL, ia_root->accNavigate(NAVDIR_DOWN, SELF, end.AsInput())); |
| 457 EXPECT_EQ(E_NOTIMPL, |
| 458 ia_child1->accNavigate(NAVDIR_RIGHT, SELF, end.AsInput())); |
| 459 EXPECT_EQ(E_NOTIMPL, |
| 460 ia_child2->accNavigate(NAVDIR_LEFT, SELF, end.AsInput())); |
| 461 EXPECT_EQ(VT_EMPTY, end.type()); |
| 462 |
| 463 // Logical directions should be supported. |
| 464 EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_FIRSTCHILD, SELF, end.AsInput())); |
| 465 EXPECT_EQ(0, var_child1.Compare(end)); |
| 466 EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_LASTCHILD, SELF, end.AsInput())); |
| 467 EXPECT_EQ(0, var_child2.Compare(end)); |
| 468 |
| 469 EXPECT_EQ(S_OK, ia_child1->accNavigate(NAVDIR_NEXT, SELF, end.AsInput())); |
| 470 EXPECT_EQ(0, var_child2.Compare(end)); |
| 471 EXPECT_EQ(S_OK, ia_child2->accNavigate(NAVDIR_PREVIOUS, SELF, end.AsInput())); |
| 472 EXPECT_EQ(0, var_child1.Compare(end)); |
| 473 |
| 474 // Child indices can also be passed by variant. |
| 475 // Indices are one-based. |
| 476 EXPECT_EQ(S_OK, |
| 477 ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant(1), end.AsInput())); |
| 478 EXPECT_EQ(0, var_child2.Compare(end)); |
| 479 EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_PREVIOUS, ScopedVariant(2), |
| 480 end.AsInput())); |
| 481 EXPECT_EQ(0, var_child1.Compare(end)); |
| 482 |
| 483 // Test out-of-bounds. |
| 484 EXPECT_EQ(S_FALSE, |
| 485 ia_child1->accNavigate(NAVDIR_PREVIOUS, SELF, end.AsInput())); |
| 486 EXPECT_EQ(VT_EMPTY, end.type()); |
| 487 EXPECT_EQ(S_FALSE, ia_child2->accNavigate(NAVDIR_NEXT, SELF, end.AsInput())); |
| 488 EXPECT_EQ(VT_EMPTY, end.type()); |
| 489 |
| 490 EXPECT_EQ(S_FALSE, ia_root->accNavigate(NAVDIR_PREVIOUS, ScopedVariant(1), |
| 491 end.AsInput())); |
| 492 EXPECT_EQ(VT_EMPTY, end.type()); |
| 493 EXPECT_EQ(S_FALSE, |
| 494 ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant(2), end.AsInput())); |
| 495 EXPECT_EQ(VT_EMPTY, end.type()); |
| 496 } |
| 497 |
404 TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { | 498 TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { |
405 AXNodeData text_field_node; | 499 AXNodeData text_field_node; |
406 text_field_node.id = 1; | 500 text_field_node.id = 1; |
407 text_field_node.role = ui::AX_ROLE_TEXT_FIELD; | 501 text_field_node.role = ui::AX_ROLE_TEXT_FIELD; |
408 text_field_node.state = 1 << ui::AX_STATE_EDITABLE; | 502 text_field_node.state = 1 << ui::AX_STATE_EDITABLE; |
409 text_field_node.SetValue("Hi"); | 503 text_field_node.SetValue("Hi"); |
410 | 504 |
411 Init(text_field_node); | 505 Init(text_field_node); |
412 ScopedComPtr<IAccessible2> ia2_text_field = | 506 ScopedComPtr<IAccessible2> ia2_text_field = |
413 ToIAccessible2(GetRootIAccessible()); | 507 ToIAccessible2(GetRootIAccessible()); |
414 ScopedComPtr<IAccessibleText> text_field; | 508 ScopedComPtr<IAccessibleText> text_field; |
415 text_field.QueryFrom(ia2_text_field.Get()); | 509 text_field.QueryFrom(ia2_text_field.Get()); |
416 ASSERT_NE(nullptr, text_field); | 510 ASSERT_NE(nullptr, text_field); |
417 | 511 |
418 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 0, 1)); | 512 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 0, 1)); |
419 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 1, 0)); | 513 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 1, 0)); |
420 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 2, 2)); | 514 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 2, 2)); |
421 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, IA2_TEXT_OFFSET_CARET, | 515 EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, IA2_TEXT_OFFSET_CARET, |
422 IA2_TEXT_OFFSET_LENGTH)); | 516 IA2_TEXT_OFFSET_LENGTH)); |
423 | 517 |
424 EXPECT_HRESULT_FAILED(text_field->setSelection(1, 0, 0)); | 518 EXPECT_HRESULT_FAILED(text_field->setSelection(1, 0, 0)); |
425 EXPECT_HRESULT_FAILED(text_field->setSelection(0, 0, 5)); | 519 EXPECT_HRESULT_FAILED(text_field->setSelection(0, 0, 5)); |
426 } | 520 } |
427 | 521 |
428 } // namespace ui | 522 } // namespace ui |
OLD | NEW |