uiautomationcore: Implement NavigateDirection_{Previous/Next}Sibling for MSAA providers.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2022-05-25 12:21:40 -04:00 committed by Alexandre Julliard
parent 66b6786e8d
commit 163e9c5b45
2 changed files with 194 additions and 77 deletions

View file

@ -1112,13 +1112,13 @@ static void test_uia_prov_from_acc_navigation(void)
elfrag2 = (void *)0xdeadbeef;
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
elfrag2 = (void *)0xdeadbeef;
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_PreviousSibling, &elfrag2);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
/*
@ -1149,18 +1149,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_NextSibling, &elfrag3);
todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
todo_wine CHECK_CALLED(Accessible_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_get_accChild);
todo_wine CHECK_CALLED(Accessible_get_accState);
if (elfrag3)
{
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
}
ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!elfrag3, "elfrag2 == NULL\n");
CHECK_CALLED(Accessible_get_accChildCount);
CHECK_CALLED(Accessible_get_accChild);
CHECK_CALLED(Accessible_get_accState);
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
@ -1206,19 +1204,16 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_PreviousSibling, &elfrag3);
todo_wine ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!elfrag3, "elfrag2 == NULL\n");
todo_wine CHECK_CALLED(Accessible_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_get_accChild);
todo_wine CHECK_CALLED(Accessible_get_accState);
if (elfrag3)
{
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
}
ok(Accessible.ref == 5, "Unexpected refcnt %ld\n", Accessible.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!elfrag3, "elfrag2 == NULL\n");
CHECK_CALLED(Accessible_get_accChildCount);
CHECK_CALLED(Accessible_get_accChild);
CHECK_CALLED(Accessible_get_accState);
check_fragment_acc(elfrag3, &Accessible.IAccessible_iface, 3);
IRawElementProviderFragment_Release(elfrag3);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child2.ref == 1, "Unexpected refcnt %ld\n", Accessible_child2.ref);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
@ -1338,21 +1333,21 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child2_accLocation);
SET_EXPECT(Accessible_child2_get_accName);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 5);
todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 4);
todo_wine CHECK_CALLED(Accessible_child_get_accParent);
todo_wine CHECK_CALLED(Accessible_child_get_accRole);
todo_wine CHECK_CALLED(Accessible_child_get_accState);
todo_wine CHECK_CALLED(Accessible_child_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_child_accLocation);
todo_wine CHECK_CALLED(Accessible_child_get_accName);
todo_wine CHECK_CALLED(Accessible_child2_get_accRole);
todo_wine CHECK_CALLED(Accessible_child2_get_accState);
todo_wine CHECK_CALLED(Accessible_child2_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_child2_accLocation);
todo_wine CHECK_CALLED(Accessible_child2_get_accName);
CHECK_CALLED_MULTI(Accessible_get_accChildCount, 5);
CHECK_CALLED_MULTI(Accessible_get_accChild, 4);
CHECK_CALLED(Accessible_child_get_accParent);
CHECK_CALLED(Accessible_child_get_accRole);
CHECK_CALLED(Accessible_child_get_accState);
CHECK_CALLED(Accessible_child_get_accChildCount);
CHECK_CALLED(Accessible_child_accLocation);
CHECK_CALLED(Accessible_child_get_accName);
CHECK_CALLED(Accessible_child2_get_accRole);
CHECK_CALLED(Accessible_child2_get_accState);
CHECK_CALLED(Accessible_child2_get_accChildCount);
CHECK_CALLED(Accessible_child2_accLocation);
CHECK_CALLED(Accessible_child2_get_accName);
/* Now they have a role mismatch, we can determine our position. */
set_accessible_props(&Accessible_child2, ROLE_SYSTEM_DOCUMENT, STATE_SYSTEM_FOCUSABLE, 1,
@ -1369,27 +1364,27 @@ static void test_uia_prov_from_acc_navigation(void)
* Even though we didn't get a new fragment, now that we know our
* position, a reference is added to the parent IAccessible.
*/
todo_wine ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!elfrag2, "elfrag2 != NULL\n");
todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 6);
todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 5);
todo_wine CHECK_CALLED(Accessible_get_accState);
todo_wine CHECK_CALLED(Accessible_child_get_accParent);
todo_wine CHECK_CALLED(Accessible_child_get_accRole);
todo_wine CHECK_CALLED(Accessible_child2_get_accRole);
CHECK_CALLED_MULTI(Accessible_get_accChildCount, 6);
CHECK_CALLED_MULTI(Accessible_get_accChild, 5);
CHECK_CALLED(Accessible_get_accState);
CHECK_CALLED(Accessible_child_get_accParent);
CHECK_CALLED(Accessible_child_get_accRole);
CHECK_CALLED(Accessible_child2_get_accRole);
/* Now that we know our position, no extra nav work. */
SET_EXPECT(Accessible_get_accChildCount);
SET_EXPECT(Accessible_get_accChild);
SET_EXPECT(Accessible_get_accState);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
todo_wine CHECK_CALLED(Accessible_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_get_accChild);
todo_wine CHECK_CALLED(Accessible_get_accState);
ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!elfrag2, "elfrag2 == NULL\n");
CHECK_CALLED(Accessible_get_accChildCount);
CHECK_CALLED(Accessible_get_accChild);
CHECK_CALLED(Accessible_get_accState);
if (elfrag2)
{
check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
@ -1449,23 +1444,20 @@ static void test_uia_prov_from_acc_navigation(void)
SET_EXPECT(Accessible_child_accNavigate);
SET_EXPECT(Accessible_child_get_accParent);
hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_NextSibling, &elfrag2);
todo_wine ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
todo_wine ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(!!elfrag2, "elfrag2 == NULL\n");
todo_wine CHECK_CALLED(Accessible_get_accChildCount);
todo_wine CHECK_CALLED(Accessible_get_accChild);
todo_wine CHECK_CALLED(Accessible_child_get_accState);
todo_wine CHECK_CALLED(Accessible_child_accNavigate);
todo_wine CHECK_CALLED(Accessible_child_get_accParent);
if (elfrag2)
{
check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
}
ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
ok(Accessible.ref == 4, "Unexpected refcnt %ld\n", Accessible.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(!!elfrag2, "elfrag2 == NULL\n");
CHECK_CALLED(Accessible_get_accChildCount);
CHECK_CALLED(Accessible_get_accChild);
CHECK_CALLED(Accessible_child_get_accState);
CHECK_CALLED(Accessible_child_accNavigate);
CHECK_CALLED(Accessible_child_get_accParent);
check_fragment_acc(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
IRawElementProviderFragment_Release(elfrag2);
ok(Accessible_child.ref == 1, "Unexpected refcnt %ld\n", Accessible_child.ref);
ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
IRawElementProviderFragment_Release(elfrag);
IRawElementProviderSimple_Release(elprov);
ok(Accessible.ref == 1, "Unexpected refcnt %ld\n", Accessible.ref);

View file

@ -223,7 +223,7 @@ static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
#define DIR_FORWARD 0
#define DIR_REVERSE 1
static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG direction,
IAccessible **child, LONG *child_id, LONG *child_pos)
IAccessible **child, LONG *child_id, LONG *child_pos, BOOL check_visible)
{
LONG child_count, cur_pos;
IDispatch *disp;
@ -246,7 +246,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
if (hr == S_FALSE)
{
if (!msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
if (!check_visible || !msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
{
*child = acc;
*child_id = *child_pos = cur_pos;
@ -264,7 +264,7 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
break;
variant_init_i4(&cid, CHILDID_SELF);
if (!msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
if (!check_visible || !msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
{
*child = acc_child;
*child_id = CHILDID_SELF;
@ -287,6 +287,86 @@ static HRESULT msaa_acc_get_next_child(IAccessible *acc, LONG start_pos, LONG di
return hr;
}
static HRESULT msaa_acc_get_child_pos(IAccessible *acc, IAccessible **out_parent,
LONG *out_child_pos)
{
LONG child_count, child_id, child_pos, match_pos;
IAccessible *child, *parent, *match, **children;
HRESULT hr;
int i;
*out_parent = NULL;
*out_child_pos = 0;
hr = msaa_acc_get_parent(acc, &parent);
if (FAILED(hr) || !parent)
return hr;
hr = IAccessible_get_accChildCount(parent, &child_count);
if (FAILED(hr) || !child_count)
{
IAccessible_Release(parent);
return hr;
}
children = heap_alloc_zero(sizeof(*children) * child_count);
if (!children)
return E_OUTOFMEMORY;
match = NULL;
for (i = 0; i < child_count; i++)
{
hr = msaa_acc_get_next_child(parent, i + 1, DIR_FORWARD, &child, &child_id, &child_pos, FALSE);
if (FAILED(hr) || !child)
goto exit;
if (child != parent)
children[i] = child;
else
IAccessible_Release(child);
}
for (i = 0; i < child_count; i++)
{
if (!children[i])
continue;
if (msaa_acc_compare(acc, children[i]))
{
if (!match)
{
match = children[i];
match_pos = i + 1;
}
/* Can't have more than one IAccessible match. */
else
{
match = NULL;
match_pos = 0;
break;
}
}
}
exit:
if (match)
{
*out_parent = parent;
*out_child_pos = match_pos;
}
else
IAccessible_Release(parent);
for (i = 0; i < child_count; i++)
{
if (children[i])
IAccessible_Release(children[i]);
}
heap_free(children);
return hr;
}
static LONG msaa_role_to_uia_control_type(LONG role)
{
switch (role)
@ -649,10 +729,10 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
if (direction == NavigateDirection_FirstChild)
hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
&child_pos);
&child_pos, TRUE);
else
hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
&child_pos);
&child_pos, TRUE);
if (FAILED(hr) || !acc)
break;
@ -676,8 +756,53 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
case NavigateDirection_NextSibling:
case NavigateDirection_PreviousSibling:
FIXME("Unimplemented NavigateDirection %d\n", direction);
return E_NOTIMPL;
if (msaa_check_root_acc(msaa_prov))
break;
if (!msaa_prov->parent)
{
if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
{
msaa_prov->parent = msaa_prov->acc;
IAccessible_AddRef(msaa_prov->acc);
msaa_prov->child_pos = V_I4(&msaa_prov->cid);
}
else
{
hr = msaa_acc_get_child_pos(msaa_prov->acc, &acc, &child_pos);
if (FAILED(hr) || !acc)
break;
msaa_prov->parent = acc;
msaa_prov->child_pos = child_pos;
}
}
if (direction == NavigateDirection_NextSibling)
hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos + 1, DIR_FORWARD,
&acc, &child_id, &child_pos, TRUE);
else
hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos - 1, DIR_REVERSE,
&acc, &child_id, &child_pos, TRUE);
if (FAILED(hr) || !acc)
break;
hr = UiaProviderFromIAccessible(acc, child_id, 0, &elprov);
if (SUCCEEDED(hr))
{
struct msaa_provider *prov = impl_from_msaa_provider(elprov);
*ret_val = &prov->IRawElementProviderFragment_iface;
prov->parent = msaa_prov->parent;
IAccessible_AddRef(msaa_prov->parent);
if (acc != msaa_prov->acc)
prov->child_pos = child_pos;
else
prov->child_pos = child_id;
}
IAccessible_Release(acc);
break;
default:
FIXME("Invalid NavigateDirection %d\n", direction);