From 68f6a73ad2b438229b00ac31e48a815face2dfc9 Mon Sep 17 00:00:00 2001
From: Connor McAdams <cmcadams@codeweavers.com>
Date: Thu, 9 Mar 2023 12:13:01 -0500
Subject: [PATCH] uiautomationcore: Add stub IUIAutomationTreeWalker
 implementation.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
---
 dlls/uiautomationcore/tests/uiautomation.c |  38 ++++
 dlls/uiautomationcore/uia_com_client.c     | 203 ++++++++++++++++++++-
 2 files changed, 239 insertions(+), 2 deletions(-)

diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c
index 2a4f87c7b59..4f4ce10d622 100644
--- a/dlls/uiautomationcore/tests/uiautomation.c
+++ b/dlls/uiautomationcore/tests/uiautomation.c
@@ -11701,6 +11701,43 @@ static void test_Element_Find(IUIAutomation *uia_iface)
     UnregisterClassA("test_Element_Find class", NULL);
 }
 
+static void test_CUIAutomation_TreeWalker_ifaces(IUIAutomation *uia_iface)
+{
+    IUIAutomationCondition *cond, *cond2;
+    IUIAutomationTreeWalker *walker;
+    HRESULT hr;
+
+    cond = NULL;
+    hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!cond, "cond == NULL\n");
+
+    /* NULL input argument tests. */
+    walker = (void *)0xdeadbeef;
+    hr = IUIAutomation_CreateTreeWalker(uia_iface, NULL, &walker);
+    ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
+    ok(!walker, "walker != NULL\n");
+
+    hr = IUIAutomation_CreateTreeWalker(uia_iface, cond, NULL);
+    ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
+
+    /* Actually create TreeWalker. */
+    walker = NULL;
+    hr = IUIAutomation_CreateTreeWalker(uia_iface, cond, &walker);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!walker, "walker == NULL\n");
+
+    hr = IUIAutomationTreeWalker_get_Condition(walker, &cond2);
+    ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+    ok(!!cond2, "cond2 == NULL\n");
+
+    ok(iface_cmp((IUnknown *)cond, (IUnknown *)cond2), "cond != cond2\n");
+    IUIAutomationCondition_Release(cond);
+    IUIAutomationCondition_Release(cond2);
+
+    IUIAutomationTreeWalker_Release(walker);
+}
+
 struct uia_com_classes {
     const GUID *clsid;
     const GUID *iid;
@@ -11802,6 +11839,7 @@ static void test_CUIAutomation(void)
     test_CUIAutomation_condition_ifaces(uia_iface);
     test_CUIAutomation_value_conversion(uia_iface);
     test_CUIAutomation_cache_request_iface(uia_iface);
+    test_CUIAutomation_TreeWalker_ifaces(uia_iface);
     test_ElementFromHandle(uia_iface, has_cui8);
     test_Element_GetPropertyValue(uia_iface);
     test_Element_cache_methods(uia_iface);
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c
index 4b2e5cba741..ec634be5e13 100644
--- a/dlls/uiautomationcore/uia_com_client.c
+++ b/dlls/uiautomationcore/uia_com_client.c
@@ -2442,6 +2442,204 @@ exit:
     return hr;
 }
 
+/*
+ * IUIAutomationTreeWalker interface.
+ */
+struct uia_tree_walker {
+    IUIAutomationTreeWalker IUIAutomationTreeWalker_iface;
+    LONG ref;
+
+    IUIAutomationCondition *nav_cond;
+    struct UiaCondition *cond_struct;
+};
+
+static inline struct uia_tree_walker *impl_from_IUIAutomationTreeWalker(IUIAutomationTreeWalker *iface)
+{
+    return CONTAINING_RECORD(iface, struct uia_tree_walker, IUIAutomationTreeWalker_iface);
+}
+
+static HRESULT WINAPI uia_tree_walker_QueryInterface(IUIAutomationTreeWalker *iface, REFIID riid, void **ppv)
+{
+    if (IsEqualIID(riid, &IID_IUIAutomationTreeWalker) || IsEqualIID(riid, &IID_IUnknown))
+        *ppv = iface;
+    else
+        return E_NOINTERFACE;
+
+    IUIAutomationTreeWalker_AddRef(iface);
+    return S_OK;
+}
+
+static ULONG WINAPI uia_tree_walker_AddRef(IUIAutomationTreeWalker *iface)
+{
+    struct uia_tree_walker *tree_walker = impl_from_IUIAutomationTreeWalker(iface);
+    ULONG ref = InterlockedIncrement(&tree_walker->ref);
+
+    TRACE("%p, refcount %ld\n", tree_walker, ref);
+    return ref;
+}
+
+static ULONG WINAPI uia_tree_walker_Release(IUIAutomationTreeWalker *iface)
+{
+    struct uia_tree_walker *tree_walker = impl_from_IUIAutomationTreeWalker(iface);
+    ULONG ref = InterlockedDecrement(&tree_walker->ref);
+
+    TRACE("%p, refcount %ld\n", tree_walker, ref);
+    if (!ref)
+    {
+        IUIAutomationCondition_Release(tree_walker->nav_cond);
+        heap_free(tree_walker);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetParentElement(IUIAutomationTreeWalker *iface, IUIAutomationElement *elem,
+        IUIAutomationElement **parent)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, parent);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetFirstChildElement(IUIAutomationTreeWalker *iface, IUIAutomationElement *elem,
+        IUIAutomationElement **first)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, first);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetLastChildElement(IUIAutomationTreeWalker *iface, IUIAutomationElement *elem,
+        IUIAutomationElement **last)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, last);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetNextSiblingElement(IUIAutomationTreeWalker *iface, IUIAutomationElement *elem,
+        IUIAutomationElement **next)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, next);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetPreviousSiblingElement(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationElement **prev)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, prev);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_NormalizeElement(IUIAutomationTreeWalker *iface, IUIAutomationElement *elem,
+        IUIAutomationElement **normalized)
+{
+    FIXME("%p, %p, %p: stub\n", iface, elem, normalized);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetParentElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **parent)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, parent);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetFirstChildElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **first)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, first);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetLastChildElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **last)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, last);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetNextSiblingElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **next)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, next);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_GetPreviousSiblingElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **prev)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, prev);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_NormalizeElementBuildCache(IUIAutomationTreeWalker *iface,
+        IUIAutomationElement *elem, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **normalized)
+{
+    FIXME("%p, %p, %p, %p: stub\n", iface, elem, cache_req, normalized);
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI uia_tree_walker_get_Condition(IUIAutomationTreeWalker *iface,
+        IUIAutomationCondition **condition)
+{
+    struct uia_tree_walker *tree_walker = impl_from_IUIAutomationTreeWalker(iface);
+
+    TRACE("%p, %p\n", iface, condition);
+
+    if (!condition)
+        return E_POINTER;
+
+    IUIAutomationCondition_AddRef(tree_walker->nav_cond);
+    *condition = tree_walker->nav_cond;
+
+    return S_OK;
+}
+
+static const IUIAutomationTreeWalkerVtbl uia_tree_walker_vtbl = {
+    uia_tree_walker_QueryInterface,
+    uia_tree_walker_AddRef,
+    uia_tree_walker_Release,
+    uia_tree_walker_GetParentElement,
+    uia_tree_walker_GetFirstChildElement,
+    uia_tree_walker_GetLastChildElement,
+    uia_tree_walker_GetNextSiblingElement,
+    uia_tree_walker_GetPreviousSiblingElement,
+    uia_tree_walker_NormalizeElement,
+    uia_tree_walker_GetParentElementBuildCache,
+    uia_tree_walker_GetFirstChildElementBuildCache,
+    uia_tree_walker_GetLastChildElementBuildCache,
+    uia_tree_walker_GetNextSiblingElementBuildCache,
+    uia_tree_walker_GetPreviousSiblingElementBuildCache,
+    uia_tree_walker_NormalizeElementBuildCache,
+    uia_tree_walker_get_Condition,
+};
+
+static HRESULT create_uia_tree_walker(IUIAutomationTreeWalker **out_tree_walker, IUIAutomationCondition *nav_cond)
+{
+    struct uia_tree_walker *tree_walker;
+    struct UiaCondition *cond_struct;
+    HRESULT hr;
+
+    if (!out_tree_walker)
+        return E_POINTER;
+
+    *out_tree_walker = NULL;
+    hr = get_uia_condition_struct_from_iface(nav_cond, &cond_struct);
+    if (FAILED(hr))
+        return hr;
+
+    tree_walker = heap_alloc_zero(sizeof(*tree_walker));
+    if (!tree_walker)
+        return E_OUTOFMEMORY;
+
+    tree_walker->IUIAutomationTreeWalker_iface.lpVtbl = &uia_tree_walker_vtbl;
+    tree_walker->ref = 1;
+    tree_walker->nav_cond = nav_cond;
+    IUIAutomationCondition_AddRef(nav_cond);
+    tree_walker->cond_struct = cond_struct;
+
+    *out_tree_walker = &tree_walker->IUIAutomationTreeWalker_iface;
+    return S_OK;
+}
+
 /*
  * IUIAutomation interface.
  */
@@ -2576,8 +2774,9 @@ static HRESULT WINAPI uia_iface_GetFocusedElementBuildCache(IUIAutomation6 *ifac
 static HRESULT WINAPI uia_iface_CreateTreeWalker(IUIAutomation6 *iface, IUIAutomationCondition *cond,
         IUIAutomationTreeWalker **out_walker)
 {
-    FIXME("%p, %p, %p: stub\n", iface, cond, out_walker);
-    return E_NOTIMPL;
+    TRACE("%p, %p, %p\n", iface, cond, out_walker);
+
+    return create_uia_tree_walker(out_walker, cond);
 }
 
 static HRESULT WINAPI uia_iface_get_ControlViewWalker(IUIAutomation6 *iface, IUIAutomationTreeWalker **out_walker)
-- 
GitLab