Commit e8eb4d78 authored by Ethan Jimenez's avatar Ethan Jimenez Committed by Commit Bot

Finish implementing ISelectionProvider for UIA

1. Completing `ISelectionProvider` methods `GetSelection`, which was
   previously partially implemented, and `get_IsSelectionRequired`.

2. Adding unit tests for the `ISelectionProvider` methods.

3. Removing unnecessary `AddRef` call before `SafeArrayPutElement`
   in `AXPlatformNodeWin::CreateUIAElementsArrayFromIdVector`.

Bug: 847971
Change-Id: Ic2d2b458812baffeb2777a259fe10369670b0f92
Reviewed-on: https://chromium-review.googlesource.com/c/1479852
Commit-Queue: Ethan Jimenez <ethavar@microsoft.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#634409}
parent 8f219b08
...@@ -373,7 +373,6 @@ SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsArrayFromIdVector( ...@@ -373,7 +373,6 @@ SAFEARRAY* AXPlatformNodeWin::CreateUIAElementsArrayFromIdVector(
AXPlatformNodeWin* node_win = AXPlatformNodeWin* node_win =
static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id)); static_cast<AXPlatformNodeWin*>(GetDelegate()->GetFromNodeID(node_id));
DCHECK(node_win); DCHECK(node_win);
node_win->AddRef();
SafeArrayPutElement(uia_array, &i, SafeArrayPutElement(uia_array, &i,
static_cast<IRawElementProviderSimple*>(node_win)); static_cast<IRawElementProviderSimple*>(node_win));
++i; ++i;
...@@ -1868,15 +1867,31 @@ IFACEMETHODIMP AXPlatformNodeWin::get_SelectionContainer( ...@@ -1868,15 +1867,31 @@ IFACEMETHODIMP AXPlatformNodeWin::get_SelectionContainer(
IFACEMETHODIMP AXPlatformNodeWin::GetSelection(SAFEARRAY** result) { IFACEMETHODIMP AXPlatformNodeWin::GetSelection(SAFEARRAY** result) {
UIA_VALIDATE_CALL_1_ARG(result); UIA_VALIDATE_CALL_1_ARG(result);
int child_count = GetDelegate()->GetChildCount();
*result = SafeArrayCreateVector(VT_UNKNOWN, 0, child_count); std::vector<AXPlatformNodeWin*> selected_children;
LONG child_count = GetDelegate()->GetChildCount();
for (LONG i = 0; i < child_count; ++i) { for (LONG i = 0; i < child_count; ++i) {
auto* child = static_cast<AXPlatformNodeWin*>( auto* child = static_cast<AXPlatformNodeWin*>(
FromNativeViewAccessible(GetDelegate()->ChildAtIndex(i))); FromNativeViewAccessible(GetDelegate()->ChildAtIndex(i)));
DCHECK(child); DCHECK(child);
child->AddRef(); if (child->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
SafeArrayPutElement(*result, &i, selected_children.push_back(child);
static_cast<IRawElementProviderSimple*>(child)); }
LONG selected_children_count = selected_children.size();
*result = SafeArrayCreateVector(VT_UNKNOWN, 0, selected_children_count);
if (!*result)
return E_OUTOFMEMORY;
for (LONG i = 0; i < selected_children_count; ++i) {
HRESULT hr = SafeArrayPutElement(
*result, &i,
static_cast<IRawElementProviderSimple*>(selected_children[i]));
if (FAILED(hr)) {
SafeArrayDestroy(*result);
*result = nullptr;
return hr;
}
} }
return S_OK; return S_OK;
} }
...@@ -1889,7 +1904,8 @@ IFACEMETHODIMP AXPlatformNodeWin::get_CanSelectMultiple(BOOL* result) { ...@@ -1889,7 +1904,8 @@ IFACEMETHODIMP AXPlatformNodeWin::get_CanSelectMultiple(BOOL* result) {
IFACEMETHODIMP AXPlatformNodeWin::get_IsSelectionRequired(BOOL* result) { IFACEMETHODIMP AXPlatformNodeWin::get_IsSelectionRequired(BOOL* result) {
UIA_VALIDATE_CALL_1_ARG(result); UIA_VALIDATE_CALL_1_ARG(result);
return E_NOTIMPL; *result = GetData().HasState(ax::mojom::State::kRequired);
return S_OK;
} }
// //
......
...@@ -137,7 +137,6 @@ ComPtr<T> AXPlatformNodeWinTest::QueryInterfaceFromNode(AXNode* node) { ...@@ -137,7 +137,6 @@ ComPtr<T> AXPlatformNodeWinTest::QueryInterfaceFromNode(AXNode* node) {
EXPECT_HRESULT_SUCCEEDED( EXPECT_HRESULT_SUCCEEDED(
ax_platform_node->GetNativeViewAccessible()->QueryInterface(__uuidof(T), ax_platform_node->GetNativeViewAccessible()->QueryInterface(__uuidof(T),
&result)); &result));
return result; return result;
} }
...@@ -152,13 +151,7 @@ AXPlatformNodeWinTest::GetRootIRawElementProviderFragment() { ...@@ -152,13 +151,7 @@ AXPlatformNodeWinTest::GetRootIRawElementProviderFragment() {
} }
ComPtr<IAccessible> AXPlatformNodeWinTest::IAccessibleFromNode(AXNode* node) { ComPtr<IAccessible> AXPlatformNodeWinTest::IAccessibleFromNode(AXNode* node) {
TestAXNodeWrapper* wrapper = return QueryInterfaceFromNode<IAccessible>(node);
TestAXNodeWrapper::GetOrCreate(tree_.get(), node);
if (!wrapper)
return ComPtr<IAccessible>();
AXPlatformNode* ax_platform_node = wrapper->ax_platform_node();
IAccessible* iaccessible = ax_platform_node->GetNativeViewAccessible();
return ComPtr<IAccessible>(iaccessible);
} }
ComPtr<IAccessible> AXPlatformNodeWinTest::GetRootIAccessible() { ComPtr<IAccessible> AXPlatformNodeWinTest::GetRootIAccessible() {
...@@ -254,6 +247,45 @@ AXPlatformNodeWinTest::GetFragmentRoot() { ...@@ -254,6 +247,45 @@ AXPlatformNodeWinTest::GetFragmentRoot() {
return fragment_root_provider; return fragment_root_provider;
} }
void AXPlatformNodeWinTest::InitListBox(
bool option_1_is_selected,
bool option_2_is_selected,
bool option_3_is_selected,
ax::mojom::State additional_state = ax::mojom::State::kNone) {
AXNodeData listbox;
listbox.id = 0;
listbox.SetName("ListBox");
listbox.role = ax::mojom::Role::kListBox;
if (additional_state != ax::mojom::State::kNone)
listbox.AddState(additional_state);
AXNodeData option_1;
option_1.id = 1;
option_1.SetName("Option1");
option_1.role = ax::mojom::Role::kListBoxOption;
if (option_1_is_selected)
option_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
listbox.child_ids.push_back(option_1.id);
AXNodeData option_2;
option_2.id = 2;
option_2.SetName("Option2");
option_2.role = ax::mojom::Role::kListBoxOption;
if (option_2_is_selected)
option_2.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
listbox.child_ids.push_back(option_2.id);
AXNodeData option_3;
option_3.id = 3;
option_3.SetName("Option3");
option_3.role = ax::mojom::Role::kListBoxOption;
if (option_3_is_selected)
option_3.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
listbox.child_ids.push_back(option_3.id);
Init(listbox, option_1, option_2, option_3);
}
TEST_F(AXPlatformNodeWinTest, TestIAccessibleDetachedObject) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleDetachedObject) {
AXNodeData root; AXNodeData root;
root.id = 1; root.id = 1;
...@@ -3092,12 +3124,12 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetRuntimeId) { ...@@ -3092,12 +3124,12 @@ TEST_F(AXPlatformNodeWinTest, TestUIAGetRuntimeId) {
SAFEARRAY* runtime_id; SAFEARRAY* runtime_id;
EXPECT_HRESULT_SUCCEEDED(root_provider->GetRuntimeId(&runtime_id)); EXPECT_HRESULT_SUCCEEDED(root_provider->GetRuntimeId(&runtime_id));
long array_lower_bound; LONG array_lower_bound;
EXPECT_HRESULT_SUCCEEDED( EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetLBound(runtime_id, 1, &array_lower_bound)); ::SafeArrayGetLBound(runtime_id, 1, &array_lower_bound));
EXPECT_EQ(0, array_lower_bound); EXPECT_EQ(0, array_lower_bound);
long array_upper_bound; LONG array_upper_bound;
EXPECT_HRESULT_SUCCEEDED( EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetUBound(runtime_id, 1, &array_upper_bound)); ::SafeArrayGetUBound(runtime_id, 1, &array_upper_bound));
EXPECT_EQ(1, array_upper_bound); EXPECT_EQ(1, array_upper_bound);
...@@ -3176,6 +3208,168 @@ TEST_F(AXPlatformNodeWinTest, TestUIANavigate) { ...@@ -3176,6 +3208,168 @@ TEST_F(AXPlatformNodeWinTest, TestUIANavigate) {
nullptr); nullptr);
} }
TEST_F(AXPlatformNodeWinTest, TestISelectionProviderCanSelectMultipleDefault) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ false,
/*option_3_is_selected*/ false);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
BOOL multiple = TRUE;
EXPECT_HRESULT_SUCCEEDED(
selection_provider->get_CanSelectMultiple(&multiple));
EXPECT_FALSE(multiple);
}
TEST_F(AXPlatformNodeWinTest, TestISelectionProviderCanSelectMultipleTrue) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ false,
/*option_3_is_selected*/ false,
/*additional_state*/ ax::mojom::State::kMultiselectable);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
BOOL multiple = FALSE;
EXPECT_HRESULT_SUCCEEDED(
selection_provider->get_CanSelectMultiple(&multiple));
EXPECT_TRUE(multiple);
}
TEST_F(AXPlatformNodeWinTest,
TestISelectionProviderIsSelectionRequiredDefault) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ false,
/*option_3_is_selected*/ false);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
BOOL selection_required = TRUE;
EXPECT_HRESULT_SUCCEEDED(
selection_provider->get_IsSelectionRequired(&selection_required));
EXPECT_FALSE(selection_required);
}
TEST_F(AXPlatformNodeWinTest, TestISelectionProviderIsSelectionRequiredTrue) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ false,
/*option_3_is_selected*/ false,
/*additional_state*/ ax::mojom::State::kRequired);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
BOOL selection_required = FALSE;
EXPECT_HRESULT_SUCCEEDED(
selection_provider->get_IsSelectionRequired(&selection_required));
EXPECT_TRUE(selection_required);
}
TEST_F(AXPlatformNodeWinTest, TestISelectionProviderGetSelectionNoneSelected) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ false,
/*option_3_is_selected*/ false);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
SAFEARRAY* selected_items;
EXPECT_HRESULT_SUCCEEDED(selection_provider->GetSelection(&selected_items));
EXPECT_NE(nullptr, selected_items);
LONG array_lower_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetLBound(selected_items, 1, &array_lower_bound));
EXPECT_EQ(0, array_lower_bound);
LONG array_upper_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetUBound(selected_items, 1, &array_upper_bound));
EXPECT_EQ(-1, array_upper_bound);
EXPECT_HRESULT_SUCCEEDED(::SafeArrayDestroy(selected_items));
}
TEST_F(AXPlatformNodeWinTest,
TestISelectionProviderGetSelectionSingleItemSelected) {
InitListBox(/*option_1_is_selected*/ false,
/*option_2_is_selected*/ true,
/*option_3_is_selected*/ false);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
ComPtr<IRawElementProviderSimple> option2_provider(
QueryInterfaceFromNode<IRawElementProviderSimple>(
GetRootNode()->children()[1]));
SAFEARRAY* selected_items;
EXPECT_HRESULT_SUCCEEDED(selection_provider->GetSelection(&selected_items));
EXPECT_NE(nullptr, selected_items);
LONG array_lower_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetLBound(selected_items, 1, &array_lower_bound));
EXPECT_EQ(0, array_lower_bound);
LONG array_upper_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetUBound(selected_items, 1, &array_upper_bound));
EXPECT_EQ(0, array_upper_bound);
IRawElementProviderSimple** array_data;
EXPECT_HRESULT_SUCCEEDED(::SafeArrayAccessData(
selected_items, reinterpret_cast<void**>(&array_data)));
EXPECT_EQ(option2_provider.Get(), array_data[0]);
EXPECT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(selected_items));
EXPECT_HRESULT_SUCCEEDED(::SafeArrayDestroy(selected_items));
}
TEST_F(AXPlatformNodeWinTest,
TestISelectionProviderGetSelectionMultipleItemsSelected) {
InitListBox(/*option_1_is_selected*/ true,
/*option_2_is_selected*/ true,
/*option_3_is_selected*/ true);
ComPtr<ISelectionProvider> selection_provider(
QueryInterfaceFromNode<ISelectionProvider>(GetRootNode()));
ComPtr<IRawElementProviderSimple> option1_provider(
QueryInterfaceFromNode<IRawElementProviderSimple>(
GetRootNode()->children()[0]));
ComPtr<IRawElementProviderSimple> option2_provider(
QueryInterfaceFromNode<IRawElementProviderSimple>(
GetRootNode()->children()[1]));
ComPtr<IRawElementProviderSimple> option3_provider(
QueryInterfaceFromNode<IRawElementProviderSimple>(
GetRootNode()->children()[2]));
SAFEARRAY* selected_items;
EXPECT_HRESULT_SUCCEEDED(selection_provider->GetSelection(&selected_items));
EXPECT_NE(nullptr, selected_items);
LONG array_lower_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetLBound(selected_items, 1, &array_lower_bound));
EXPECT_EQ(0, array_lower_bound);
LONG array_upper_bound;
EXPECT_HRESULT_SUCCEEDED(
::SafeArrayGetUBound(selected_items, 1, &array_upper_bound));
EXPECT_EQ(2, array_upper_bound);
IRawElementProviderSimple** array_data;
EXPECT_HRESULT_SUCCEEDED(::SafeArrayAccessData(
selected_items, reinterpret_cast<void**>(&array_data)));
EXPECT_EQ(option1_provider.Get(), array_data[0]);
EXPECT_EQ(option2_provider.Get(), array_data[1]);
EXPECT_EQ(option3_provider.Get(), array_data[2]);
EXPECT_HRESULT_SUCCEEDED(::SafeArrayUnaccessData(selected_items));
EXPECT_HRESULT_SUCCEEDED(::SafeArrayDestroy(selected_items));
}
TEST_F(AXPlatformNodeWinTest, TestUIAErrorHandling) { TEST_F(AXPlatformNodeWinTest, TestUIAErrorHandling) {
AXNodeData root; AXNodeData root;
Init(root); Init(root);
......
...@@ -59,6 +59,11 @@ class AXPlatformNodeWinTest : public ui::AXPlatformNodeTest { ...@@ -59,6 +59,11 @@ class AXPlatformNodeWinTest : public ui::AXPlatformNodeTest {
void InitFragmentRoot(); void InitFragmentRoot();
Microsoft::WRL::ComPtr<IRawElementProviderFragmentRoot> GetFragmentRoot(); Microsoft::WRL::ComPtr<IRawElementProviderFragmentRoot> GetFragmentRoot();
void InitListBox(bool option_1_is_selected,
bool option_2_is_selected,
bool option_3_is_selected,
ax::mojom::State additional_state);
std::unique_ptr<AXFragmentRootWin> ax_fragment_root_; std::unique_ptr<AXFragmentRootWin> ax_fragment_root_;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment