[PATCH 3/3] uiautomationcore: Implement NavigateDirection_{Previous/Next}Sibling for MSAA providers.

Connor McAdams wine at gitlab.winehq.org
Mon Jun 13 20:24:03 CDT 2022


From: Connor McAdams <cmcadams at codeweavers.com>

Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
 dlls/uiautomationcore/tests/uiautomation.c | 132 +++++++++----------
 dlls/uiautomationcore/uia_provider.c       | 140 +++++++++++++++++++--
 2 files changed, 195 insertions(+), 77 deletions(-)

diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index da19d3668fa..15d7b4e784b 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -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);
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 891cb486161..f72628885bd 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -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 *end_pos)
+        IAccessible **child, LONG *child_id, LONG *end_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 = *end_pos = cur_pos;
@@ -263,7 +263,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;
@@ -286,6 +286,83 @@ 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_pos)
+{
+    IAccessible *child, *parent, *match, **children;
+    LONG child_count, child_id, end_pos, match_pos;
+    HRESULT hr;
+    int i;
+
+    *out_parent = NULL;
+    *out_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, &end_pos, FALSE);
+        if (FAILED(hr) || !child)
+            goto exit;
+
+        if (child != parent)
+            children[i] = 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_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)
@@ -647,9 +724,11 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
             break;
 
         if (direction == NavigateDirection_FirstChild)
-            hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id, &end_pos);
+            hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
+                    &end_pos, TRUE);
         else
-            hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id, &end_pos);
+            hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
+                    &end_pos, TRUE);
 
         if (FAILED(hr) || !acc)
             break;
@@ -675,8 +754,55 @@ 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_id);
+                if (FAILED(hr) || !acc)
+                    break;
+                msaa_prov->parent = acc;
+                msaa_prov->child_pos = child_id;
+            }
+        }
+
+        if (direction == NavigateDirection_NextSibling)
+            hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos + 1, DIR_FORWARD,
+                    &acc, &child_id, &end_pos, TRUE);
+        else
+            hr = msaa_acc_get_next_child(msaa_prov->parent, msaa_prov->child_pos - 1, DIR_REVERSE,
+                    &acc, &child_id, &end_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 = end_pos;
+            else
+                prov->child_pos = child_id;
+        }
+
+        if (acc != msaa_prov->parent)
+            IAccessible_Release(acc);
+
+        break;
 
     default:
         FIXME("Invalid NavigateDirection %d\n", direction);
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/241



More information about the wine-devel mailing list