[PATCH v2 2/3] uiautomationcore: Implement NavigateDirection_{First/Last}Child for MSAA Providers.

Connor McAdams wine at gitlab.winehq.org
Tue Jun 14 08:18:24 CDT 2022


From: Connor McAdams <cmcadams at codeweavers.com>

Signed-off-by: Connor McAdams <cmcadams at codeweavers.com>
---
 dlls/uiautomationcore/tests/uiautomation.c | 155 ++++++++++-----------
 dlls/uiautomationcore/uia_provider.c       | 109 ++++++++++++++-
 2 files changed, 182 insertions(+), 82 deletions(-)

diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 10d91d04d81..da19d3668fa 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -1134,37 +1134,36 @@ 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_FirstChild, &elfrag2);
-    todo_wine ok(Accessible_child.ref == 2, "Unexpected refcnt %ld\n", Accessible_child.ref);
-    todo_wine 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 == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elfrag2, "elfrag2 == NULL\n");
+    CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
+    CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
+    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);
+    SET_EXPECT(Accessible_get_accChildCount);
+    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(!!elfrag2, "elfrag2 == NULL\n");
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 3);
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
-    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)
+    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(elfrag2, &Accessible_child.IAccessible_iface, CHILDID_SELF);
-
-        SET_EXPECT(Accessible_get_accChildCount);
-        SET_EXPECT(Accessible_get_accChild);
-        SET_EXPECT(Accessible_get_accState);
-        hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_NextSibling, &elfrag3);
-        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);
     }
+    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);
 
     /* Retrieve childid 3 as first child now that Accessible_child is invisible. */
     set_accessible_props(&Accessible_child, 0, STATE_SYSTEM_INVISIBLE, 0, NULL, 0, 0, 0, 0);
@@ -1173,19 +1172,16 @@ static void test_uia_prov_from_acc_navigation(void)
     SET_EXPECT_MULTI(Accessible_get_accState, 2);
     SET_EXPECT(Accessible_child_get_accState);
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &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_MULTI(Accessible_get_accChildCount, 4);
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 3);
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accState, 2);
-    todo_wine CHECK_CALLED(Accessible_child_get_accState);
-    if (elfrag2)
-    {
-        check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
-        IRawElementProviderFragment_Release(elfrag2);
-        ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.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_MULTI(Accessible_get_accChildCount, 4);
+    CHECK_CALLED_MULTI(Accessible_get_accChild, 3);
+    CHECK_CALLED_MULTI(Accessible_get_accState, 2);
+    CHECK_CALLED(Accessible_child_get_accState);
+    check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
+    IRawElementProviderFragment_Release(elfrag2);
+    ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
 
     /* Retrieve childid 4 (Accessible_child2) as last child. */
     set_accessible_props(&Accessible_child2, 0, STATE_SYSTEM_FOCUSABLE, 0, NULL, 0, 0, 0, 0);
@@ -1195,38 +1191,38 @@ static void test_uia_prov_from_acc_navigation(void)
     SET_EXPECT(Accessible_child2_accNavigate);
     SET_EXPECT(Accessible_child2_get_accParent);
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &elfrag2);
-    todo_wine ok(Accessible_child2.ref == 2, "Unexpected refcnt %ld\n", Accessible_child2.ref);
-    todo_wine ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+    ok(Accessible_child2.ref == 2, "Unexpected refcnt %ld\n", Accessible_child2.ref);
+    ok(Accessible.ref == 3, "Unexpected refcnt %ld\n", Accessible.ref);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!elfrag2, "elfrag2 == NULL\n");
+    CHECK_CALLED_MULTI(Accessible_get_accChildCount, 2);
+    CHECK_CALLED(Accessible_get_accChild);
+    CHECK_CALLED(Accessible_child2_get_accState);
+    CHECK_CALLED(Accessible_child2_accNavigate);
+    CHECK_CALLED(Accessible_child2_get_accParent);
+
+    check_fragment_acc(elfrag2, &Accessible_child2.IAccessible_iface, CHILDID_SELF);
+    SET_EXPECT(Accessible_get_accChildCount);
+    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(!!elfrag2, "elfrag2 == NULL\n");
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accChildCount, 2);
+    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_child2_get_accState);
-    todo_wine CHECK_CALLED(Accessible_child2_accNavigate);
-    todo_wine CHECK_CALLED(Accessible_child2_get_accParent);
-    if (elfrag2)
+    todo_wine CHECK_CALLED(Accessible_get_accState);
+    if (elfrag3)
     {
-        check_fragment_acc(elfrag2, &Accessible_child2.IAccessible_iface, CHILDID_SELF);
-
-        SET_EXPECT(Accessible_get_accChildCount);
-        SET_EXPECT(Accessible_get_accChild);
-        SET_EXPECT(Accessible_get_accState);
-        hr = IRawElementProviderFragment_Navigate(elfrag2, NavigateDirection_PreviousSibling, &elfrag3);
-        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);
     }
 
+    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);
+
     /* Retrieve childid 3 as last child, now that Accessible_child2 is STATE_SYSTEM_INVISIBLE. */
     set_accessible_props(&Accessible_child2, 0, STATE_SYSTEM_INVISIBLE, 0, NULL, 0, 0, 0, 0);
     SET_EXPECT_MULTI(Accessible_get_accChildCount, 3);
@@ -1234,19 +1230,16 @@ static void test_uia_prov_from_acc_navigation(void)
     SET_EXPECT(Accessible_get_accState);
     SET_EXPECT(Accessible_child2_get_accState);
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &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_MULTI(Accessible_get_accChildCount, 3);
-    todo_wine CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
-    todo_wine CHECK_CALLED(Accessible_get_accState);
-    todo_wine CHECK_CALLED(Accessible_child2_get_accState);
-    if (elfrag2)
-    {
-        check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
-        IRawElementProviderFragment_Release(elfrag2);
-        ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.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_MULTI(Accessible_get_accChildCount, 3);
+    CHECK_CALLED_MULTI(Accessible_get_accChild, 2);
+    CHECK_CALLED(Accessible_get_accState);
+    CHECK_CALLED(Accessible_child2_get_accState);
+    check_fragment_acc(elfrag2, &Accessible.IAccessible_iface, 3);
+    IRawElementProviderFragment_Release(elfrag2);
+    ok(Accessible.ref == 2, "Unexpected refcnt %ld\n", Accessible.ref);
 
     IRawElementProviderFragment_Release(elfrag);
     IRawElementProviderSimple_Release(elprov);
@@ -1309,15 +1302,15 @@ static void test_uia_prov_from_acc_navigation(void)
     /* ChildCount of 0, do nothing for First/Last child.*/
     SET_EXPECT(Accessible_child_get_accChildCount);
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &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(Accessible_child_get_accChildCount);
+    CHECK_CALLED(Accessible_child_get_accChildCount);
 
     SET_EXPECT(Accessible_child_get_accChildCount);
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &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(Accessible_child_get_accChildCount);
+    CHECK_CALLED(Accessible_child_get_accChildCount);
 
     /*
      * In the case of sibling navigation on an IAccessible that wasn't
@@ -1438,12 +1431,12 @@ static void test_uia_prov_from_acc_navigation(void)
      */
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_FirstChild, &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");
 
     hr = IRawElementProviderFragment_Navigate(elfrag, NavigateDirection_LastChild, &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");
 
     /*
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c
index 1dfe447331e..af093527a58 100644
--- a/dlls/uiautomationcore/uia_provider.c
+++ b/dlls/uiautomationcore/uia_provider.c
@@ -220,6 +220,73 @@ static HRESULT msaa_acc_get_parent(IAccessible *acc, IAccessible **parent)
     return hr;
 }
 
+#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)
+{
+    LONG child_count, cur_pos;
+    IDispatch *disp;
+    VARIANT cid;
+    HRESULT hr;
+
+    *child = NULL;
+    *child_id = 0;
+    cur_pos = start_pos;
+    while (1)
+    {
+        hr = IAccessible_get_accChildCount(acc, &child_count);
+        if (FAILED(hr) || (cur_pos > child_count))
+            break;
+
+        variant_init_i4(&cid, cur_pos);
+        hr = IAccessible_get_accChild(acc, cid, &disp);
+        if (FAILED(hr))
+            break;
+
+        if (hr == S_FALSE)
+        {
+            if (!msaa_check_acc_state(acc, cid, STATE_SYSTEM_INVISIBLE))
+            {
+                *child = acc;
+                *child_id = *child_pos = cur_pos;
+                IAccessible_AddRef(acc);
+                return S_OK;
+            }
+        }
+        else
+        {
+            IAccessible *acc_child = NULL;
+
+            hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc_child);
+            IDispatch_Release(disp);
+            if (FAILED(hr))
+                break;
+
+            variant_init_i4(&cid, CHILDID_SELF);
+            if (!msaa_check_acc_state(acc_child, cid, STATE_SYSTEM_INVISIBLE))
+            {
+                *child = acc_child;
+                *child_id = CHILDID_SELF;
+                *child_pos = cur_pos;
+                return S_OK;
+            }
+
+            IAccessible_Release(acc_child);
+        }
+
+        if (direction == DIR_FORWARD)
+            cur_pos++;
+        else
+            cur_pos--;
+
+        if ((cur_pos > child_count) || (cur_pos <= 0))
+            break;
+    }
+
+    return hr;
+}
+
 static LONG msaa_role_to_uia_control_type(LONG role)
 {
     switch (role)
@@ -315,6 +382,9 @@ struct msaa_provider {
 
     BOOL root_acc_check_ran;
     BOOL is_root_acc;
+
+    IAccessible *parent;
+    INT child_pos;
 };
 
 static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
@@ -326,7 +396,7 @@ static BOOL msaa_check_root_acc(struct msaa_provider *msaa_prov)
         return msaa_prov->is_root_acc;
 
     msaa_prov->root_acc_check_ran = TRUE;
-    if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+    if (V_I4(&msaa_prov->cid) != CHILDID_SELF || msaa_prov->parent)
         return FALSE;
 
     hr = AccessibleObjectFromWindow(msaa_prov->hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
@@ -383,6 +453,8 @@ ULONG WINAPI msaa_provider_Release(IRawElementProviderSimple *iface)
     if (!refcount)
     {
         IAccessible_Release(msaa_prov->acc);
+        if (msaa_prov->parent)
+            IAccessible_Release(msaa_prov->parent);
         heap_free(msaa_prov);
     }
 
@@ -531,6 +603,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
         enum NavigateDirection direction, IRawElementProviderFragment **ret_val)
 {
     struct msaa_provider *msaa_prov = impl_from_msaa_fragment(iface);
+    LONG child_count, child_id, child_pos;
     IRawElementProviderSimple *elprov;
     IAccessible *acc;
     HRESULT hr;
@@ -567,6 +640,40 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface,
 
     case NavigateDirection_FirstChild:
     case NavigateDirection_LastChild:
+        if (V_I4(&msaa_prov->cid) != CHILDID_SELF)
+            break;
+
+        hr = IAccessible_get_accChildCount(msaa_prov->acc, &child_count);
+        if (FAILED(hr) || !child_count)
+            break;
+
+        if (direction == NavigateDirection_FirstChild)
+            hr = msaa_acc_get_next_child(msaa_prov->acc, 1, DIR_FORWARD, &acc, &child_id,
+                    &child_pos);
+        else
+            hr = msaa_acc_get_next_child(msaa_prov->acc, child_count, DIR_REVERSE, &acc, &child_id,
+                    &child_pos);
+
+        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->acc;
+            IAccessible_AddRef(msaa_prov->acc);
+            if (acc != msaa_prov->acc)
+                prov->child_pos = child_pos;
+            else
+                prov->child_pos = child_id;
+        }
+        IAccessible_Release(acc);
+
+        break;
+
     case NavigateDirection_NextSibling:
     case NavigateDirection_PreviousSibling:
         FIXME("Unimplemented NavigateDirection %d\n", direction);
-- 
GitLab


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



More information about the wine-devel mailing list