| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/accessibility/platform/ax_platform_node.h" |
| |
| #include <atlbase.h> |
| #include <atlcom.h> |
| #include <oleacc.h> |
| |
| #include <memory> |
| |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_comptr.h" |
| #include "base/win/scoped_variant.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/iaccessible2/ia2_api_all.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/accessibility/platform/test_ax_node_wrapper.h" |
| #include "ui/base/win/atl_module.h" |
| |
| using base::win::ScopedBstr; |
| using base::win::ScopedComPtr; |
| using base::win::ScopedVariant; |
| |
| namespace ui { |
| |
| namespace { |
| |
| // Most IAccessible functions require a VARIANT set to CHILDID_SELF as |
| // the first argument. |
| ScopedVariant SELF(CHILDID_SELF); |
| |
| } // namespace |
| |
| class AXPlatformNodeWinTest : public testing::Test { |
| public: |
| AXPlatformNodeWinTest() {} |
| ~AXPlatformNodeWinTest() override {} |
| |
| void SetUp() override { |
| win::CreateATLModuleIfNeeded(); |
| } |
| |
| // Initialize given an AXTreeUpdate. |
| void Init(const AXTreeUpdate& initial_state) { |
| tree_.reset(new AXTree(initial_state)); |
| } |
| |
| // Convenience functions to initialize directly from a few AXNodeData objects. |
| void Init(const AXNodeData& node1) { |
| AXTreeUpdate update; |
| update.root_id = node1.id; |
| update.nodes.push_back(node1); |
| Init(update); |
| } |
| |
| void Init(const AXNodeData& node1, |
| const AXNodeData& node2) { |
| AXTreeUpdate update; |
| update.root_id = node1.id; |
| update.nodes.push_back(node1); |
| update.nodes.push_back(node2); |
| Init(update); |
| } |
| |
| void Init(const AXNodeData& node1, |
| const AXNodeData& node2, |
| const AXNodeData& node3) { |
| AXTreeUpdate update; |
| update.root_id = node1.id; |
| update.nodes.push_back(node1); |
| update.nodes.push_back(node2); |
| update.nodes.push_back(node3); |
| Init(update); |
| } |
| void Init(const AXNodeData& node1, |
| const AXNodeData& node2, |
| const AXNodeData& node3, |
| const AXNodeData& node4) { |
| AXTreeUpdate update; |
| update.root_id = node1.id; |
| update.nodes.push_back(node1); |
| update.nodes.push_back(node2); |
| update.nodes.push_back(node3); |
| update.nodes.push_back(node4); |
| Init(update); |
| } |
| |
| protected: |
| AXNode* GetRootNode() { |
| return tree_->root(); |
| } |
| |
| ScopedComPtr<IAccessible> IAccessibleFromNode(AXNode* node) { |
| TestAXNodeWrapper* wrapper = |
| TestAXNodeWrapper::GetOrCreate(tree_.get(), node); |
| if (!wrapper) |
| return ScopedComPtr<IAccessible>(); |
| AXPlatformNode* ax_platform_node = wrapper->ax_platform_node(); |
| IAccessible* iaccessible = ax_platform_node->GetNativeViewAccessible(); |
| return ScopedComPtr<IAccessible>(iaccessible); |
| } |
| |
| ScopedComPtr<IAccessible> GetRootIAccessible() { |
| return IAccessibleFromNode(GetRootNode()); |
| } |
| |
| ScopedComPtr<IAccessible2> ToIAccessible2( |
| ScopedComPtr<IAccessible> accessible) { |
| CHECK(accessible); |
| ScopedComPtr<IServiceProvider> service_provider; |
| accessible.CopyTo(service_provider.GetAddressOf()); |
| ScopedComPtr<IAccessible2> result; |
| CHECK(SUCCEEDED(service_provider->QueryService(IID_IAccessible2, |
| result.GetAddressOf()))); |
| return result; |
| } |
| |
| std::unique_ptr<AXTree> tree_; |
| }; |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleDetachedObject) { |
| AXNodeData root; |
| root.id = 1; |
| root.AddStringAttribute(AX_ATTR_NAME, "Name"); |
| Init(root); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, root_obj->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name", name); |
| |
| tree_.reset(new AXTree()); |
| ScopedBstr name2; |
| EXPECT_EQ(E_FAIL, root_obj->get_accName(SELF, name2.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleHitTest) { |
| AXNodeData root; |
| root.id = 0; |
| root.child_ids.push_back(1); |
| root.child_ids.push_back(2); |
| root.location = gfx::RectF(0, 0, 30, 30); |
| |
| AXNodeData node1; |
| node1.id = 1; |
| node1.location = gfx::RectF(0, 0, 10, 10); |
| node1.AddStringAttribute(AX_ATTR_NAME, "Name1"); |
| |
| AXNodeData node2; |
| node2.id = 2; |
| node2.location = gfx::RectF(20, 20, 10, 10); |
| node2.AddStringAttribute(AX_ATTR_NAME, "Name2"); |
| |
| Init(root, node1, node2); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| |
| ScopedVariant obj; |
| |
| // This is way outside of the root node |
| EXPECT_EQ(S_FALSE, root_obj->accHitTest(50, 50, obj.Receive())); |
| EXPECT_EQ(VT_EMPTY, obj.type()); |
| |
| // this is directly on node 1. |
| EXPECT_EQ(S_OK, root_obj->accHitTest(5, 5, obj.Receive())); |
| ASSERT_NE(nullptr, obj.ptr()); |
| |
| // We got something back, make sure that it has the correct name. |
| base::win::ScopedComPtr<IAccessible> accessible; |
| HRESULT hr = V_DISPATCH(obj.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)); |
| EXPECT_EQ(S_OK, hr); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name1", name); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleName) { |
| AXNodeData root; |
| root.id = 1; |
| root.AddStringAttribute(AX_ATTR_NAME, "Name"); |
| Init(root); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, root_obj->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name", name); |
| |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accName(SELF, nullptr)); |
| ScopedVariant bad_id(999); |
| ScopedBstr name2; |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accName(bad_id, name2.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleDescription) { |
| AXNodeData root; |
| root.id = 1; |
| root.AddStringAttribute(AX_ATTR_DESCRIPTION, "Description"); |
| Init(root); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| ScopedBstr description; |
| EXPECT_EQ(S_OK, root_obj->get_accDescription(SELF, description.Receive())); |
| EXPECT_STREQ(L"Description", description); |
| |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accDescription(SELF, nullptr)); |
| ScopedVariant bad_id(999); |
| ScopedBstr d2; |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accDescription(bad_id, d2.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleValue) { |
| AXNodeData root; |
| root.id = 1; |
| root.AddStringAttribute(AX_ATTR_VALUE, "Value"); |
| Init(root); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| ScopedBstr value; |
| EXPECT_EQ(S_OK, root_obj->get_accValue(SELF, value.Receive())); |
| EXPECT_STREQ(L"Value", value); |
| |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accValue(SELF, nullptr)); |
| ScopedVariant bad_id(999); |
| ScopedBstr v2; |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accValue(bad_id, v2.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleShortcut) { |
| AXNodeData root; |
| root.id = 1; |
| root.AddStringAttribute(AX_ATTR_KEY_SHORTCUTS, "Shortcut"); |
| Init(root); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| ScopedBstr shortcut; |
| EXPECT_EQ(S_OK, root_obj->get_accKeyboardShortcut(SELF, shortcut.Receive())); |
| EXPECT_STREQ(L"Shortcut", shortcut); |
| |
| EXPECT_EQ(E_INVALIDARG, root_obj->get_accKeyboardShortcut(SELF, nullptr)); |
| ScopedVariant bad_id(999); |
| ScopedBstr k2; |
| EXPECT_EQ(E_INVALIDARG, |
| root_obj->get_accKeyboardShortcut(bad_id, k2.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionNotListBox) { |
| // We only support AX_ROLE_LIST_BOX as this point, so, this should return |
| // not implemented. We're choosing AX_ROLE_ALERT, but it could be anything |
| // but AX_ROLE_LIST_BOX_OPTION. |
| |
| AXNodeData not_supported; |
| not_supported.id = 0; |
| not_supported.role = AX_ROLE_ALERT; |
| |
| Init(not_supported); |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| |
| ScopedVariant selection; |
| EXPECT_EQ(E_NOTIMPL, root_obj->get_accSelection(selection.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionNothingSelected) { |
| // We're going to set up a AX_ROLE_LIST_BOX_OPTION with 2 options with |
| // nothing selected |
| AXNodeData list; |
| list.id = 0; |
| list.role = AX_ROLE_LIST_BOX; |
| |
| list.child_ids.push_back(2); |
| list.child_ids.push_back(3); |
| |
| AXNodeData list_item_2; |
| list_item_2.id = 2; |
| list_item_2.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_2.AddStringAttribute(AX_ATTR_NAME, "Name2"); |
| |
| AXNodeData list_item_3; |
| list_item_3.id = 3; |
| list_item_3.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_3.AddStringAttribute(AX_ATTR_NAME, "Name3"); |
| |
| // Nothing is selected. This should return S_OK and the selection should |
| // be VT_EMPTY. |
| |
| Init(list, list_item_2, list_item_3); |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| |
| ScopedVariant selection; |
| EXPECT_EQ(S_OK, root_obj->get_accSelection(selection.Receive())); |
| EXPECT_EQ(VT_EMPTY, selection.type()); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionOneSelected) { |
| // We're going to set up a AX_ROLE_LIST_BOX_OPTION with 2 options with |
| // one selected. |
| AXNodeData list; |
| list.id = 0; |
| list.role = AX_ROLE_LIST_BOX; |
| |
| list.child_ids.push_back(2); |
| list.child_ids.push_back(3); |
| |
| AXNodeData list_item_2; |
| list_item_2.id = 2; |
| list_item_2.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_2.state = 1 << ui::AX_STATE_SELECTED; |
| list_item_2.AddStringAttribute(AX_ATTR_NAME, "Name2"); |
| |
| AXNodeData list_item_3; |
| list_item_3.id = 3; |
| list_item_3.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_3.AddStringAttribute(AX_ATTR_NAME, "Name3"); |
| |
| Init(list, list_item_2, list_item_3); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| |
| ScopedVariant selection; |
| EXPECT_EQ(S_OK, root_obj->get_accSelection(selection.Receive())); |
| ASSERT_NE(nullptr, selection.ptr()); |
| |
| // We got something back, make sure that it has the correct name. |
| base::win::ScopedComPtr<IAccessible> accessible; |
| HRESULT hr = |
| V_DISPATCH(selection.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)); |
| EXPECT_EQ(S_OK, hr); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name2", name); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionMultipleSelected) { |
| // We're going to set up a AX_ROLE_LIST_BOX_OPTION with 3 options with |
| // two selected. |
| AXNodeData list; |
| list.id = 0; |
| list.role = AX_ROLE_LIST_BOX; |
| |
| list.child_ids.push_back(2); |
| list.child_ids.push_back(3); |
| list.child_ids.push_back(4); |
| |
| AXNodeData list_item_2; |
| list_item_2.id = 2; |
| list_item_2.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_2.state = 1 << ui::AX_STATE_SELECTED; |
| list_item_2.AddStringAttribute(AX_ATTR_NAME, "Name2"); |
| |
| AXNodeData list_item_3; |
| list_item_3.id = 3; |
| list_item_3.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_3.state = 1 << ui::AX_STATE_SELECTED; |
| list_item_3.AddStringAttribute(AX_ATTR_NAME, "Name3"); |
| |
| AXNodeData list_item_4; |
| list_item_4.id = 4; |
| list_item_4.role = AX_ROLE_LIST_BOX_OPTION; |
| list_item_4.AddStringAttribute(AX_ATTR_NAME, "Name4"); |
| Init(list, list_item_2, list_item_3, list_item_4); |
| |
| ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); |
| |
| ScopedVariant selection; |
| EXPECT_EQ(S_OK, root_obj->get_accSelection(selection.Receive())); |
| ASSERT_NE(nullptr, selection.ptr()); |
| |
| // We got something back, make sure that it has the corrent name. |
| base::win::ScopedComPtr<IEnumVARIANT> accessibles; |
| HRESULT hr = |
| V_DISPATCH(selection.ptr())->QueryInterface(IID_PPV_ARGS(&accessibles)); |
| EXPECT_EQ(S_OK, hr); |
| ULONG ignore; |
| |
| // Check out the first selected item. |
| { |
| ScopedVariant item; |
| hr = accessibles->Next(1, item.Receive(), &ignore); |
| EXPECT_EQ(S_OK, hr); |
| |
| base::win::ScopedComPtr<IAccessible> accessible; |
| HRESULT hr = |
| V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)); |
| EXPECT_EQ(S_OK, hr); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name2", name); |
| } |
| |
| // and the second selected element. |
| { |
| ScopedVariant item; |
| hr = accessibles->Next(1, item.Receive(), &ignore); |
| EXPECT_EQ(S_OK, hr); |
| |
| base::win::ScopedComPtr<IAccessible> accessible; |
| HRESULT hr = |
| V_DISPATCH(item.ptr())->QueryInterface(IID_PPV_ARGS(&accessible)); |
| EXPECT_EQ(S_OK, hr); |
| ScopedBstr name; |
| EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive())); |
| EXPECT_STREQ(L"Name3", name); |
| } |
| |
| // There shouldn't be any more selected. |
| { |
| ScopedVariant item; |
| hr = accessibles->Next(1, item.Receive(), &ignore); |
| EXPECT_EQ(S_FALSE, hr); |
| } |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleRole) { |
| AXNodeData root; |
| root.id = 1; |
| root.child_ids.push_back(2); |
| |
| AXNodeData child; |
| child.id = 2; |
| |
| Init(root, child); |
| AXNode* child_node = GetRootNode()->children()[0]; |
| ScopedComPtr<IAccessible> child_iaccessible( |
| IAccessibleFromNode(child_node)); |
| |
| ScopedVariant role; |
| |
| child.role = AX_ROLE_ALERT; |
| child_node->SetData(child); |
| EXPECT_EQ(S_OK, child_iaccessible->get_accRole(SELF, role.Receive())); |
| EXPECT_EQ(ROLE_SYSTEM_ALERT, V_I4(role.ptr())); |
| |
| child.role = AX_ROLE_BUTTON; |
| child_node->SetData(child); |
| EXPECT_EQ(S_OK, child_iaccessible->get_accRole(SELF, role.Receive())); |
| EXPECT_EQ(ROLE_SYSTEM_PUSHBUTTON, V_I4(role.ptr())); |
| |
| child.role = AX_ROLE_POP_UP_BUTTON; |
| child_node->SetData(child); |
| EXPECT_EQ(S_OK, child_iaccessible->get_accRole(SELF, role.Receive())); |
| EXPECT_EQ(ROLE_SYSTEM_BUTTONMENU, V_I4(role.ptr())); |
| |
| EXPECT_EQ(E_INVALIDARG, child_iaccessible->get_accRole(SELF, nullptr)); |
| ScopedVariant bad_id(999); |
| EXPECT_EQ(E_INVALIDARG, |
| child_iaccessible->get_accRole(bad_id, role.Receive())); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleLocation) { |
| AXNodeData root; |
| root.id = 1; |
| root.location = gfx::RectF(10, 40, 800, 600); |
| Init(root); |
| |
| TestAXNodeWrapper::SetGlobalCoordinateOffset(gfx::Vector2d(100, 200)); |
| |
| LONG x_left, y_top, width, height; |
| EXPECT_EQ(S_OK, GetRootIAccessible()->accLocation(&x_left, &y_top, &width, |
| &height, SELF)); |
| EXPECT_EQ(110, x_left); |
| EXPECT_EQ(240, y_top); |
| EXPECT_EQ(800, width); |
| EXPECT_EQ(600, height); |
| |
| EXPECT_EQ(E_INVALIDARG, GetRootIAccessible()->accLocation( |
| nullptr, &y_top, &width, &height, SELF)); |
| EXPECT_EQ(E_INVALIDARG, GetRootIAccessible()->accLocation( |
| &x_left, nullptr, &width, &height, SELF)); |
| EXPECT_EQ(E_INVALIDARG, GetRootIAccessible()->accLocation( |
| &x_left, &y_top, nullptr, &height, SELF)); |
| EXPECT_EQ(E_INVALIDARG, GetRootIAccessible()->accLocation( |
| &x_left, &y_top, &width, nullptr, SELF)); |
| ScopedVariant bad_id(999); |
| EXPECT_EQ(E_INVALIDARG, GetRootIAccessible()->accLocation( |
| &x_left, &y_top, &width, &height, bad_id)); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { |
| AXNodeData root; |
| root.id = 1; |
| root.child_ids.push_back(2); |
| root.child_ids.push_back(3); |
| |
| AXNodeData button; |
| button.role = AX_ROLE_BUTTON; |
| button.id = 2; |
| |
| AXNodeData checkbox; |
| checkbox.role = AX_ROLE_CHECK_BOX; |
| checkbox.id = 3; |
| |
| Init(root, button, checkbox); |
| AXNode* button_node = GetRootNode()->children()[0]; |
| AXNode* checkbox_node = GetRootNode()->children()[1]; |
| ScopedComPtr<IAccessible> root_iaccessible(GetRootIAccessible()); |
| ScopedComPtr<IAccessible> button_iaccessible( |
| IAccessibleFromNode(button_node)); |
| ScopedComPtr<IAccessible> checkbox_iaccessible( |
| IAccessibleFromNode(checkbox_node)); |
| |
| LONG child_count; |
| EXPECT_EQ(S_OK, root_iaccessible->get_accChildCount(&child_count)); |
| EXPECT_EQ(2L, child_count); |
| EXPECT_EQ(S_OK, button_iaccessible->get_accChildCount(&child_count)); |
| EXPECT_EQ(0L, child_count); |
| EXPECT_EQ(S_OK, checkbox_iaccessible->get_accChildCount(&child_count)); |
| EXPECT_EQ(0L, child_count); |
| |
| { |
| ScopedComPtr<IDispatch> result; |
| EXPECT_EQ(S_OK, |
| root_iaccessible->get_accChild(SELF, result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), root_iaccessible); |
| } |
| |
| { |
| ScopedComPtr<IDispatch> result; |
| ScopedVariant child1(1); |
| EXPECT_EQ(S_OK, |
| root_iaccessible->get_accChild(child1, result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), button_iaccessible); |
| } |
| |
| { |
| ScopedComPtr<IDispatch> result; |
| ScopedVariant child2(2); |
| EXPECT_EQ(S_OK, |
| root_iaccessible->get_accChild(child2, result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), checkbox_iaccessible); |
| } |
| |
| { |
| // Asking for child id 3 should fail. |
| ScopedComPtr<IDispatch> result; |
| ScopedVariant child3(3); |
| EXPECT_EQ(E_INVALIDARG, |
| root_iaccessible->get_accChild(child3, result.GetAddressOf())); |
| } |
| |
| // We should be able to ask for the button by its unique id too. |
| LONG button_unique_id; |
| ScopedComPtr<IAccessible2> button_iaccessible2 = |
| ToIAccessible2(button_iaccessible); |
| button_iaccessible2->get_uniqueID(&button_unique_id); |
| ASSERT_LT(button_unique_id, 0); |
| { |
| ScopedComPtr<IDispatch> result; |
| ScopedVariant button_id_variant(button_unique_id); |
| EXPECT_EQ(S_OK, root_iaccessible->get_accChild(button_id_variant, |
| result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), button_iaccessible); |
| } |
| |
| // We shouldn't be able to ask for the root node by its unique ID |
| // from one of its children, though. |
| LONG root_unique_id; |
| ScopedComPtr<IAccessible2> root_iaccessible2 = |
| ToIAccessible2(root_iaccessible); |
| root_iaccessible2->get_uniqueID(&root_unique_id); |
| ASSERT_LT(root_unique_id, 0); |
| { |
| ScopedComPtr<IDispatch> result; |
| ScopedVariant root_id_variant(root_unique_id); |
| EXPECT_EQ(E_INVALIDARG, button_iaccessible->get_accChild( |
| root_id_variant, result.GetAddressOf())); |
| } |
| |
| // Now check parents. |
| { |
| ScopedComPtr<IDispatch> result; |
| EXPECT_EQ(S_OK, button_iaccessible->get_accParent(result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), root_iaccessible); |
| } |
| |
| { |
| ScopedComPtr<IDispatch> result; |
| EXPECT_EQ(S_OK, checkbox_iaccessible->get_accParent(result.GetAddressOf())); |
| EXPECT_EQ(result.Get(), root_iaccessible); |
| } |
| |
| { |
| ScopedComPtr<IDispatch> result; |
| EXPECT_EQ(S_FALSE, root_iaccessible->get_accParent(result.GetAddressOf())); |
| } |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessible2IndexInParent) { |
| AXNodeData root; |
| root.id = 1; |
| root.child_ids.push_back(2); |
| root.child_ids.push_back(3); |
| |
| AXNodeData left; |
| left.id = 2; |
| |
| AXNodeData right; |
| right.id = 3; |
| |
| Init(root, left, right); |
| ScopedComPtr<IAccessible> root_iaccessible(GetRootIAccessible()); |
| ScopedComPtr<IAccessible2> root_iaccessible2 = |
| ToIAccessible2(root_iaccessible); |
| ScopedComPtr<IAccessible> left_iaccessible( |
| IAccessibleFromNode(GetRootNode()->children()[0])); |
| ScopedComPtr<IAccessible2> left_iaccessible2 = |
| ToIAccessible2(left_iaccessible); |
| ScopedComPtr<IAccessible> right_iaccessible( |
| IAccessibleFromNode(GetRootNode()->children()[1])); |
| ScopedComPtr<IAccessible2> right_iaccessible2 = |
| ToIAccessible2(right_iaccessible); |
| |
| LONG index; |
| EXPECT_EQ(E_FAIL, root_iaccessible2->get_indexInParent(&index)); |
| |
| EXPECT_EQ(S_OK, left_iaccessible2->get_indexInParent(&index)); |
| EXPECT_EQ(0, index); |
| |
| EXPECT_EQ(S_OK, right_iaccessible2->get_indexInParent(&index)); |
| EXPECT_EQ(1, index); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestAccNavigate) { |
| AXNodeData root; |
| root.id = 1; |
| root.role = AX_ROLE_ROOT_WEB_AREA; |
| |
| AXNodeData child1; |
| child1.id = 2; |
| child1.role = AX_ROLE_STATIC_TEXT; |
| root.child_ids.push_back(2); |
| |
| AXNodeData child2; |
| child2.id = 3; |
| child2.role = AX_ROLE_STATIC_TEXT; |
| root.child_ids.push_back(3); |
| |
| Init(root, child1, child2); |
| ScopedComPtr<IAccessible> ia_root(GetRootIAccessible()); |
| ScopedComPtr<IDispatch> disp_root; |
| ASSERT_HRESULT_SUCCEEDED(ia_root.CopyTo(disp_root.GetAddressOf())); |
| ScopedVariant var_root(disp_root.Get()); |
| ScopedComPtr<IAccessible> ia_child1( |
| IAccessibleFromNode(GetRootNode()->children()[0])); |
| ScopedComPtr<IDispatch> disp_child1; |
| ASSERT_HRESULT_SUCCEEDED(ia_child1.CopyTo(disp_child1.GetAddressOf())); |
| ScopedVariant var_child1(disp_child1.Get()); |
| ScopedComPtr<IAccessible> ia_child2( |
| IAccessibleFromNode(GetRootNode()->children()[1])); |
| ScopedComPtr<IDispatch> disp_child2; |
| ASSERT_HRESULT_SUCCEEDED(ia_child2.CopyTo(disp_child2.GetAddressOf())); |
| ScopedVariant var_child2(disp_child2.Get()); |
| ScopedVariant end; |
| |
| // Invalid arguments. |
| EXPECT_EQ( |
| E_INVALIDARG, |
| ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant::kEmptyVariant, nullptr)); |
| EXPECT_EQ(E_INVALIDARG, |
| ia_child1->accNavigate(NAVDIR_NEXT, ScopedVariant::kEmptyVariant, |
| end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| |
| // Navigating to first/last child should only be from self. |
| EXPECT_EQ(E_INVALIDARG, |
| ia_root->accNavigate(NAVDIR_FIRSTCHILD, var_root, end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| EXPECT_EQ(E_INVALIDARG, |
| ia_root->accNavigate(NAVDIR_LASTCHILD, var_root, end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| |
| // Spatial directions are not supported. |
| EXPECT_EQ(E_NOTIMPL, ia_child1->accNavigate(NAVDIR_UP, SELF, end.AsInput())); |
| EXPECT_EQ(E_NOTIMPL, ia_root->accNavigate(NAVDIR_DOWN, SELF, end.AsInput())); |
| EXPECT_EQ(E_NOTIMPL, |
| ia_child1->accNavigate(NAVDIR_RIGHT, SELF, end.AsInput())); |
| EXPECT_EQ(E_NOTIMPL, |
| ia_child2->accNavigate(NAVDIR_LEFT, SELF, end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| |
| // Logical directions should be supported. |
| EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_FIRSTCHILD, SELF, end.AsInput())); |
| EXPECT_EQ(0, var_child1.Compare(end)); |
| EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_LASTCHILD, SELF, end.AsInput())); |
| EXPECT_EQ(0, var_child2.Compare(end)); |
| |
| EXPECT_EQ(S_OK, ia_child1->accNavigate(NAVDIR_NEXT, SELF, end.AsInput())); |
| EXPECT_EQ(0, var_child2.Compare(end)); |
| EXPECT_EQ(S_OK, ia_child2->accNavigate(NAVDIR_PREVIOUS, SELF, end.AsInput())); |
| EXPECT_EQ(0, var_child1.Compare(end)); |
| |
| // Child indices can also be passed by variant. |
| // Indices are one-based. |
| EXPECT_EQ(S_OK, |
| ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant(1), end.AsInput())); |
| EXPECT_EQ(0, var_child2.Compare(end)); |
| EXPECT_EQ(S_OK, ia_root->accNavigate(NAVDIR_PREVIOUS, ScopedVariant(2), |
| end.AsInput())); |
| EXPECT_EQ(0, var_child1.Compare(end)); |
| |
| // Test out-of-bounds. |
| EXPECT_EQ(S_FALSE, |
| ia_child1->accNavigate(NAVDIR_PREVIOUS, SELF, end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| EXPECT_EQ(S_FALSE, ia_child2->accNavigate(NAVDIR_NEXT, SELF, end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| |
| EXPECT_EQ(S_FALSE, ia_root->accNavigate(NAVDIR_PREVIOUS, ScopedVariant(1), |
| end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| EXPECT_EQ(S_FALSE, |
| ia_root->accNavigate(NAVDIR_NEXT, ScopedVariant(2), end.AsInput())); |
| EXPECT_EQ(VT_EMPTY, end.type()); |
| } |
| |
| TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { |
| AXNodeData text_field_node; |
| text_field_node.id = 1; |
| text_field_node.role = ui::AX_ROLE_TEXT_FIELD; |
| text_field_node.state = 1 << ui::AX_STATE_EDITABLE; |
| text_field_node.SetValue("Hi"); |
| |
| Init(text_field_node); |
| ScopedComPtr<IAccessible2> ia2_text_field = |
| ToIAccessible2(GetRootIAccessible()); |
| ScopedComPtr<IAccessibleText> text_field; |
| ia2_text_field.CopyTo(text_field.GetAddressOf()); |
| ASSERT_NE(nullptr, text_field); |
| |
| EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 0, 1)); |
| EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 1, 0)); |
| EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 2, 2)); |
| EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, IA2_TEXT_OFFSET_CARET, |
| IA2_TEXT_OFFSET_LENGTH)); |
| |
| EXPECT_HRESULT_FAILED(text_field->setSelection(1, 0, 0)); |
| EXPECT_HRESULT_FAILED(text_field->setSelection(0, 0, 5)); |
| } |
| |
| } // namespace ui |